Download

Is it possible to Tick an actor from outside the game thread?

Hi,
I wonder if it is possible and safe to tick an actor, such as an APlayerController actor, from a thread other than the game thread, even if that required the actor to be prevented from getting ticked in the game thread itself?

Any discussion is more than welcomed, even if not providing ideal answer whatsoever.

cheers.

If you have a strong reference to the object (not just a bare pointer) and if the tick function does not call any other objects, then that could be safe.
However, that requirement is quite limiting. You’ll often want to check collision, or talk to the controlled pawn, or do a bunch of other interaction with other objects, from within your Tick.
It may be better to put whatever code you’re worried will take a long time in its own thread and task, and then “pick up” the information from this thread/task in the Tick function. This adds a frame of latency from the “push information to the task” (at the end of the previous tick) to the “pick up the finished information” at the beginning of the next Tick, but it lets you overlap computation safely (as long as that operation doesn’t need to talk to other unsafe objects, of course.)

1 Like

Thanks for your reply.

The full task of the new thread will be collecting input readings of each player at a very high frequency, then for the game thread to read those inputs in a thread safe manner.

The reason I posed the question as “ticking an actor” is that I originally wanted to deal with APlayerController as the source of input for the new thread(which might be a very high level source not suitable for the task I now kinda realized).

Do you know a better way to access inputs for such a task, and a way to know which input abstract device is assigned to which player (in case I have more than one player simultaneously)?

My proper question would have been What is the input abstraction object that is safely approached from non-game threads?

Maybe?
PrimaryActorTick.bRunOnAnyThread = true;
i don’t know if you really mean tick actor or a new thread if you wanna tick a actor outside from the game thread that’s the code

TheLuffy
this PrimaryActorTick.bRunOnAnyThread is such a great addition to the discussion, although I think issues are still much more intricate than I would’ve hoped.

My last post (immediately above your post) will add better explanation of what I am trying to do, take a look at it.

Nonetheless, thanks for your reply.

From my tests, input is already captured accurately multiple times in a frame, so you can use it as-is.

Here’s how I tested it:


To get the accurate time, use Get Accurate Real Time, which returns the exact time it’s called instead of once per frame. Limit the framerate to 1 fps. At the start of a frame, press the input a bunch of times, then stop before the frame changes to keep the input all within a single frame. Then print the inputs; this will print the input number and the accurate time it was pressed.


Considering these fire at the exact time the input is received, the way you’re trying to do it will actually return less accurate results because you aren’t acting on input the exact moment it’s pressed; there will be a greater delay between the input being pressed and the action being performed.

1 Like

That is really, really great, thanks indeed.

I’d like actually to read an axis input (joystick) periodically from inside the frame, what do you think better suits that?
And do you think multithreading is still a good option for performance (although less accurate due to potential delays)?

Nonetheless, you made my â– â– â– â–  day, thanks again.

1 Like

So I did some tests, and not good news: apparently, axis events only update once per-frame. I tested this first by using the actual input node, and it only triggered once per-frame just like tick. Then I used the get node and put it in the method I did in the last post, and the value was always the same within the frame. So I think axis inputs only update once; I think this is because they’re “continuous”. You can test it in C++ to see if you can get different values within the frame, but if you can’t, you’re stuck with one axis value per-frame.

There is some good news, though: this is just within a single frame; over multiple frames, the axis values change. So the higher the framerate, the more accurate the axis input. So this is where that “frames win games” thing comes in.

I don’t know, you’ll have to test. I think it will be more performant since it’s in a different thread, but it will be unreliable because of how multithreading works. Code in a thread can run at any time, so the input may or may not be captured all the time, or at the right time, etc.; it may even be missed for an entire frame; remember: threads are not promised to complete at any specific time; they are completely independent of the game thread.

IMO, the best way to handle input is how unreal has it setup.

All the inputs are buffered by the kernel. If you only present the results to the user every so often, you’re really no worse off just reading and updating the simulation each time you present the frame. HOWEVER, you may want to try to get timing information for the inputs, if you update the simulation at a higher rate than you render/present to the user. Unreal by default doesn’t get you that.

midgunner66

That’s a bunch of bad news unfortunately.
Nonetheless, my man you did such a great job, thanks a lot.

1 Like

The actual logic of what I am trying to do is, in each frame (of game thread itself), we will actually take the input reading that happened exactly 10 ms ago, it will be our “accepted” input reading for current frame. But we will also take into consideration all inputs recorded from 10 ms ago till now, as if we were able to exactly predict the future 10 ms of input readings. That means a lot to our core gameplay mechanics.

So what does that mean? it means that the only process that needs to run at a very fast rate is updating input reading from a thread that runs independent of the actual game thread, putting data in a input-time table.

My problem is that I don’t know if it is possible and safe to do that, the lowest access point to inputs I reached now from reviewing the source code is the object UPlayerInput (inside of APlayerController), which is uobject not an actor and I really wonder if I only can access this object from a dedicated thread, even if that meant it would be not accessible from the main game thread.

Anyhow, thanks for your reply I really appreciate it. I will try to further learn this topic, it must work someday.

Alien threads can read anything from main game thread.
What you can’t do is modify the state of game framework classes from outside main thread unless you’re modifying data at UProperty level.

UObjects can also tick, so you don’t have to use an Actor to tick, you can create tickable object subsystems that run in World initialization regardless of actors spawned in the world.

Indeed UPlayerInput is a GameFramework class.

Now I’d assume that alien thread can read only and not modify(unless uproperty) will also mean that alien thread can’t tick said GameFramework object (correct me if I’m wrong), because tick potentially has a state-changing side-effect.

again is it tickable from alien thread?

Back to UPlayerInput, it is a GameFramework class, and it handles input reading (as far as I know for now), so instead of UPlayerInput, could I access a yet lower level class outside of GameFramework, and yet still higher than the OS input handling level itself, could that be done within UE4 input abstraction?

Most UObjects are thread safe to modify.
Actors, Components and the likes, are not.

If you create your own UObject and make it tick then it’s only up to you if it is thread safe or not.

1 Like

What input device do you have where sub-10-ms frames are meaningful?

A typical keyboard will give you no better than 10 ms timing, and game controllers (at least XInput style) will give you maybe 120 Hz / 8 ms. If it’s “important to gameplay” what happens at finer-than-10-ms resolution, then you clearly can’t be designing a game for the mass market.

Are you building research hardware, or targeting only some very specialized input devices, like some location based entertainment? That actually sounds kind of cool if it’s the case :slight_smile:

No, the game is actually targeting common market devices and systems.
The game is not actually trying to deal with an input device with 1000hz polling ferquency, no it rather wants to catch the regular 120-250hz input change as soon as it happened instead of waiting till the beginning of the next frame which would take 3-15 ms after the actual input last read value, this is a huge difference in our system (both Physics and Visuals).

In other words, we knowingly want to catch repeated similar readings until change is caught. The game thread can run as low as 30fps or as high as 1000 fps we don’t care. However, the input readings must be at least 300fps regardless of input device polling frequency.

Key to note that the game will actually use an input 10ms old (from the past), but it will know all the readings that are younger, which is pivotal to our overall design from a chronological standpoint. Problem is I have no background in OS whatsoever, so it is kinda tough.

But there is no correlation between hardware poll rate and your frame rate.

The 120 Hz change may happen right before you start your simulation/render step, or it may happen 7.9 milliseconds before you start your simulation/render step, and this delay will drift in and out of phase. If that delay is “important to gameplay” then gameplay will drift in and out of phase.

I have no idea what your gameplay, simulation, and general system is like, so maybe there’s some case where this actually does make sense, but when I examine these requirements from first principles, it does set off alarm bells. For example, if this is a fighting game, where twitch button timing matters, the amount of drift over frames would significantly impact the responsiveness of that fighter.

Now, from music, we know that jitter is the enemy, rather than absolute delay. MIDI is a good case study – each note in a chord is at least 0.7 ms (using running status,) and the difference between a single note or a fat both-hands chord can be noticeable. To get MIDI into a computer, a game controller or keyboard just isn’t satisfactory; the jitter is too much. This is why we have USB MIDI keyboards to use for playing phrases into desktop music systems.

Yes that’s exactly right, and this fluctuation is one of the issue that we’re trying to get rid of.

By recording a table of inputs with their timings at high frequency, then at any given frame we can get the input reading that is always 10 ms in the past. We want the delay, contrary to what it otherwise seems, we want it but we want it to be a very fixed delay, instead of depending on game frame starting reading, and previous frame reading. Further more we want to know all input changes (be it only one or two readings) that have happened during this past 10 ms.

The whole idea is fixing the delay rather than getting immediate fresh input reading.

I really understand your concern here, and it is a quite rational one honestly, but it would suffice to tell you that upon testing the game with rather frame based input delay, the difference between what I want and what I have now, is what makes the game move from being a really cool game mechanics to a competitively solid game mechanics. The difference isn’t noticeable by casual players, but it is crucial for our game as a business on the long run.

How does the user predict this? Frame rate is frame rate. Why is “always delay by 10 milliseconds” any better than “always delay by 0 milliseconds?”
Are you not locking to vsync? Are you not hitting your frame rate targets? If so, player feel likely has bigger problems than “what is the raw USB input time!”

All in all, yes, you can spin up a separate thread. You can have that thread talk to XInput and/or raw mouse / keyboard HID events (this may require some shunting within the UE source code – I haven’t looked to see whether this is safe to “just do”) You can then have some function that you call from your player controller that “harvests” the queue of events captured by that thread. This is all achievable, worst case by doing some C++ hacking.

When I said input prediction, the player isn’t the focus here, I was talking about what happens inside the game logic internal solvers that handle input values, input predictions, and other key calculations.
As far as the player is concerned, he/she will be guaranteed a 10ms fixed delay from the joystick, to the game loop “time” itself, regardless of visual representation of the game simulation. That’s another issue (where we had no problem at all).

This is a great point. But if you think about it, firstly there’s no such thing as zero delay at all. Depending on the frame start time compared to last device polling time assuming both are 120hz. This zero delay is actually an inherent delay that can be as little as few nanoseconds, or as large as 8.32ms. Add to it not knowing where exactly along this range of potential delay the device reading has actually happened, and knowing whether two device input readings have happened during one frame to frame gap. And if the game is 50 to 60 frames, that is way worse.

[you of course don’t need to feel obligated to respond to what was mention above whatsoever, but if you were to, please note that I mentioned a. different inherent delay periods, b. inability to measure said period, c. inability to know pre-last polling if happened after last game-frame pick(two or more pollings in one frame), d. Low frames 50-60 (16-20ms), e. Fluctuating DeltaTime of our game logic frames themselves.]

We are (and by “we” I mean I, as I am the one developing the game in reality alone), we are designing the game logic to benefit a lot from having two-polling-input-readings with a fixed 10ms delay. The value of this system is much superior to the not-so-zero-delay that the default game loop is doing in a game running 120+ fps, let alone low frequencies. The only case that would make our system redundant (and not contradictory still) is if game runs with frame DeltaTime of say 2 or 3 ms, 400fps.

BTW, the game is already running smoothly 120fps, no apparent problem whatsoever, however, it is designed to be a tight online sport game that we need to make it as chronologically controlled environment as humanly possible for a myriad of reasons along the way. And I can talk about that all day long. An input with a fixed delay longer than polling interval is an important part of it. And that is big requirement that’s not met yet.