6 ms·
This seems like an anti-pattern. I usually tell people that sleeps only belong in one place in their code and they should make linters complain if it pops up a
by drewcoo 4y ago
This seems like an anti-pattern.
I usually tell people that sleeps only belong in one place in their code and they should make linters complain if it pops up anywhere else. The place is the method they call to poll. And even then it's better to wait for something than poll to see if we're ready.
This, though, seems to be trying to sleep to slow down frame rate. What the? Maybe it's intended as some way to make all users' experiences suck equally even if they have fast boxes. Maybe. And depending on the actual scenario maybe it makes sense to handle this with a "better" sleep.
The problem as stated is not a solved problem because it's not generally anything anyone would want to do.
In fact, people might have left this intentionally hard to do to discourage its use.
- singron 4y agoDepending on the graphics api, something needs to sleep somewhere to get decent latency. E.g. in vulkan, if the present mode is fifo, then rendering frames as fast as possible will fill the swapchain with stale frames that won't be presented for a while. It can be a lot better to sleep and render the frame right before it can be presented. But if you sleep a little too long, you will miss the frame, so you need a better sleep function like this.
- bsder 4y agoThe problem with all the modern graphics APIs isn't sleep()--the graphics card generally can be filled to 100%, and you kick off another render when the swapchain releases a frame. The problem is knowing how long the actual render is going to take. Scenes can vary vastly in complexity and predicting how long they will take to render a priori is extremely difficult.
- 10000truths 4y agoNot an expert in graphics rendering, but could you estimate the render time by calculating (a priori) the number of draw calls needed to render the frame?
- kevingadd 4y agoUnfortunately no - the cost will depend on the number of visible pixels, and worse, there are secondary costs depending on the arrangement of those visible pixels on screen, what part of the texture happens to be there, whether any sparse textures need to get paged in from system memory, whether GPU occlusion culling is going to cull certain things it wouldn't normally cull, etc. And other things are competing for GPU time like your OS compositor
- devit 4y agoThat's what vsync is for. And despite what some people and games seem to think, vsync isn't an optional feature that you can turn on or off, it's something that must always be enabled since it's essential to display correct graphics.
- dxuh 4y agoSadly VSync is not all upside and it increases input latency significantly, so it might be a deal breaker depending on what you play or just the person playing. Despite their age GSync and FreeSync are not that ubiquitous yet. I agree that for most single player games it is simply correct to enable it, but people often don't do it, because they paid lots of money for their gaming computers and want to see the FPS numbers go way up to justify their expense.
- jsmith45 4y agoAssuming a fixed monitor display, and a separate faster than framerate game update loop, then the ideal way to render is to sleep until you just barely have enough time and then start rending, finishing just time for the card to read out the data. But obviously that is hard, since render time is not the same for every frame. [1] But if we could pull this off, it would obviously have latency that is as low as possible given the hardware in question, with the frame actually output always containing the latest possible data without screen tearing. (If you allow for screen tearing by swapping buffers mid scan-out, then you can get even less input latency for the lower parts of the screen with the render as fast as possible techniques.) The continuously render new frames as fast as possible approach (with triple buffering) is somewhat of a crude approximation of this. It works, and is potentially better than vsync, but is not ideal. It can give impressive framerate numbers though. Variable refresh rate (gsync, freesync) can avoid this, and are best, at least as long as you can't render frames faster than the maximum refresh rate of the monitor. If you can render frames faster, then all these considerations apply again. Footnotes: [1] I seem to recall was some technology that tried to simulate this (without the sleep) by making render calls for "frames" that are too early be no-ops, and only once we are close to when the frame needs to start being actually drawn do the draw calls do anything. This obviously yielded absurd framerate numbers.
- nemothekid 4y agoI've used this exact pattern before when designing a homegrown kiosk display renderer with an immediate mode GUI; and I think it is pretty common in games as well (this is how Raylib's renderer works). You could push frames as fast possible, but you will chew up the CPU doing inefficient work and overload the hardware needlessly. If you have something like a physics engine you may also want to design a physics loop that runs every X milliseconds (again, if you let it run as fast as possible, the game world will be in super speed; there have been reports of bad developers who couple their render loop with the physics loop and once the framerate becomes uncapped, the game becomes unplayable).
- monocasa 4y agoBetter than that is a vsync event to wait on.
- bruce343434 4y agoWhat if you're developing an authoritative game server? There will be no vsync event.
- heavenlyblue 4y ago> again, if you let it run as fast as possible, the game world will be in super speed Not really, you can calculate how long had passed since the last physics recalculation and use that as a delta between frames.
- zamalek 4y agoIt's for physics. Writing a [stable] fixed time step simulation is significantly simpler than variable time step. Tons of games do it (console ports are guilty of fixed 30FPS), and it really sucks.
- derefr 4y agoIn most cases there's no reason to couple physics to graphics. The game "is" the physics engine (esp. in cases of networked games); while the graphics engine is simply an "observer" of the output of the physics engine, trying to interpolate/extrapolate graphics frames given a stream of physics frames. Do physics at 30FPS in one thread; draw graphics as fast as you can in a separate thread; and at the beginning of each render frame, get the latest physics integration-state, and how much time it's been since it was calculated, and extrapolate object displacements+rotations (incl. the camera) linearly from existing velocities. Those changes will be so small, and corrected by the next physics-step so soon, that it'll almost always look correct. The only reason to not do this, is if you can't afford concurrent execution (i.e. you only have a single CPU core and interrupts are expensive), and you also don't have a practical Lowest Common Multiple frequency to run an event-loop at. Which is something that was maybe true in the gameboy era, but hasn't been true for at least 30 years.
- danhau 4y ago> Do physics at 30FPS in one thread; draw graphics as fast as you can in a separate thread This sounds like a race condition nightmare. How would one guarantee synchronization? One big mutex for the entire physics state? Or don‘t bother and read whatever values you get in the render thread? I suppose a single threaded context you would automatically get something like a global mutex, just without the mutex, thanks to the leap frogging nature (frame, frame, frame, tick, frame, frame, frame, tick, …). At least with a fixed timestep.
- heavenlyblue 4y agoJust have 2 snapshots of all of the positions of all elements in the game (which is trivial in terms of storage requirements), just the same way you have it with double-buffering for the pixels on the screen.
- boardwaalk 4y agoThere are good reasons to do things at certain times (like polling for input or rendering) or slowing down the render loop (adjustable frame rate cap not necessarily the same as refresh rate), and that's really the end of that. Thinking this might be "intentionally hard" rather than "non-realtime operating systems are just sloppy" is silly.
- masklinn 4y ago> This, though, seems to be trying to sleep to slow down frame rate. What the? Maybe it's intended as some way to make all users' experiences suck equally even if they have fast boxes. Maybe. Or to conserve power, why burn CPU and render at hundreds of fps when the output is limited to 60 or even 30? I don’t need my machine loaded at 100% when playing slay the spire or dungeons of into the breach.
- comex 4y agoWhich is why you use vsync, ensuring the game renders once for each frame your monitor shows, as opposed to having the game just guess when it should present frames - which can produce tearing or be at the wrong frame rate entirely.
- iKlsR 4y agoUnrelated but a "sleep story" nonetheless. Several years ago worked on a desktop Qt app and I spent several weeks juicing platform flags, caching stuff on save to start faster, slimming down the mainwindow classes, everything I could to get the app to boot in under several 500ms down from 5+s only to be told by my boss that it was too fast and he wanted to see the splash screen so after all that to this day, there is a `sleep(rand(x, y))` call somewhere in that codebase.
- wongarsu 4y agoYou could argue that graphics code should just activate VSYNC and run as fast as possible otherwise. The article hints that that doesn't always work in practice, but even if we pretend that it does that still leaves physics engines and other simulation code. That kind of code usually works best with a fixed "framerate"/tickrate, but for performance reason that rate is set at something like 20 ticks per second. If your framerate is a multiple of that and graphics and physics runs on the same thread that's trivial (e.g. do physics every third frame for 60fps), but what if you move it to a different thread and some of your users have 75hz monitors? Now you have to sleep, or waste resources.