Из за чего происходит неровный фрейм тайм даже если поставить лок на 60 фпс и он всегда будет постоянным?
Может быть потому, что разное время ушло на отрисовку каждого из этих 60 кадров. Грубо говоря — на первый радр могло уйти 500мс, а на остальные — уже по 8мс. Итого за одну секунду более 60 кадров, но первые полсекунды висел один и тот же кадр — и фреймрейт выглядит крайне неровным.
Лок на фпс может убрать прыжки фпс, но на фрейм тайме никак не отразится. Вроде это уже чисто от производительности игры зависит.
Так я вот это и не пойму, почему это не убрирает неровность фреймтайма
Лок фпс только потолок фпс ограничивает. На фрейм тайм он не влияет.
Как можно ограничить фпс и не повлиять на фреймтайм, когда это одна и та же характеристика, если речь про мгновенный фпс?
Поэтому я и пишу про мгновенный фпс. И, вообще говоря, лок фпса работает как раз на уровне фреймтайма
Вот неограниченный фпс (1) и ограниченный на 120 (2). Повлиял лок на фреймтайм или нет?
А теперь читаем, что написанно в посте:
При учете,конечно, что меньше 60 фпс в игре выдавать никак не будет
Если будут просадки фпса (мгновенные, которые в 1 и 0.1% видны), то в какой-то момент на счетчике можно будет увидеть меньше 60 фпс, а значит это условие не соблюдается.
Меня график устраивает)
то это можно списать на множество факторов- движок игры, пропускная способность оперативки, на кэш, на ошибки считывания данных самой программой.
Вот про это и надо писать автору, а не бред про то, что лок фпса на фреймтайм не влияет. Либо явно писать, что лок фпса не влияет на _пики_ фреймтайма. Но как раз единственное, что лок делает — ограничивает минимальный фреймтайм
При чем тут вообще потребление и монитор, вопрос исключительно про лок и фреймтайм. Если лок не влияет на фреймтайм, почему на первой картинке у меня фреймтайм 1.1 мс, а на второй 8.2?
Когда я говорю про фреймтайм, я подразумеваю время отрисовки кадра (aka frame time), а не эфемерные графики
на мониторе в 60гц нет смысла показывать кадры меньше 16.6 сек, разница не будет видна.
Выставляя разные значения фпс лока, и время кадра будет разным.
Вот про это и надо писать автору, а не бред про то, что лок фпса на фреймтайм не влияет.
Он и не влияет
Круто стыкуются друг с другом 🙂
Смотря как выбирать точки отсчета и оси 🙂 Все-таки зеленый и синий график выглядят по-разному. Но это уже придирки к словам
Т.е. если вы ловите статтеры, то ограничение фпса никак на эти статтеры не повлияют
А с этим я и не спорил. И вообще вопрос не в этом, ведь очевидно (по крайней мере для меня), что автор поста спрашивал про ситуацию, когда статтеров нет.
Для сингловых игр снижать инпутлаг ниже 16мс не имеет никакого значения
Разница есть? Есть. Киберкотлеты с 240 Гц мониторами тоже стараются выжимать как можно больше фпс.
Т.е. если вы ловите статтеры, то ограничение фпса никак на эти статтеры не повлияют. Статистика 1% лоу как была так и останется.
А я про это выше и писал:
Либо явно писать, что лок фпса не влияет на _пики_ фреймтайма. Но как раз единственное, что лок делает — ограничивает минимальный фреймтайм
The Elusive Frame Timing
Finally, an explanation for why some games stutter on your PC (and a glimpse of hope that this might stop happening in the near future)
![]()
Stu-tu-tu-tutter
You’ve been waiting for the next installment of your favorite PC game series for so long, and now it’s finally here. This time, you want to make sure you can enjoy it fully right from the start, so you invested time and money to prepare meticulously. You’ve upgraded your CPU, installed a bleeding edge GPU, added more RAM — heck, you even prepared an SSD RAID. It must be silk-smooth right from the intro screen.
Your pre-order has finally unlocked and has just finished installing. Nervously, you are starting it for the first time. So far, so good — the game is running at 60 frames per second. Or at least the frame counter from the latest GPU tuner overlay says so. But, something is not right. You flick your mouse around in sharp, deliberate movements. You side-strafe left and right quickly… and… It stutters! IT FRIGGIN’ STUTTERS! Argh, how can it be? How can it stutter at 60 frames-per-bloody-second?
It may sound ridiculous, if it never happened to you. But if you’ve ever experienced it, you almost certainly hate stutter with a passion. Stutter in games. Not the plain old “lag”. Not the low frame rate. But just “stutter”, happening at high frame rates, on perfect, super fast configurations. What is it, where does it come from, and is there any way in this world to get rid of it? Let me tell you a story…
Stutter, smoothness, performance… it’s all the same, right?
Video games have been running at 60 fps since the days of first arcade machines, back in the ‘70s. Normally, it was expected that the game runs at exactly the same frame rate that the display uses. It wasn’t until the popularization of 3D games that we first started accepting lower frame rates. Way back in the ‘90s, when “3D cards” (that was before we started calling them “GPUs”) started to replace software rendering, people used to play games in 20 fps and considered 35 fps speeds for serious competitive netplay. I’m not kidding.
Nowadays, we have super fast machines, and “of course we can run at 60”. Yet… there seems to be more players unhappy with game performance than ever before. How can that be? Well, it’s not that the games are not actually running fast enough. It’s that they are stuttering even though they run fast!
If you look at a few game forums, you are likely to find something like this:
One might think those are isolated cases, but look at Google search stats:
(Note that these are relative values. It’s not that people are searching for stutter more than for frame rate in general. It’s that while frame rate query is stagnating, searches for stutter are growing, especially recently.)
A decade of search for the cause of inexplicable stutter
I first saw this issue way back around 2003. We were working on Serious Sam 2, and people started reporting cases where they were testing something on an empty level and mouse-look and movement wasn’t smooth. It was accompanied with a very specific pattern in the frame-rate graph, which we started calling “heartbeat”.
We thought we had a bug somewhere in our code, but couldn’t find it. The issue would come and go seemingly at random — when restarting the app, rebooting the machine… you’d change some performance option and it would be gone. Then you’d change that option back, but the problem wouldn’t come back. It was like a ghost.
Apparently, we were not the only ones with this issue. Seeing similar problems in other games we were playing, we started to think it was something in the drivers. But it happened across different GPU vendors. Even across different APIs (OpenGL, DirectX 9, DirectX 11…) — the only consistent thing was that it appeared on some machines, on some scenes… sometimes.
We’ve released several more games, while this weird thing was coming and going. It used to bother some users, and we would usually tell them to change some performance options — which sometimes helped, and sometimes not. Guess that’s life, eh?
Then suddenly, on a nice winter day, early in 2013, my colleague Dean called me to come and see yet another instance of this issue which he was, for the moment, able to relatively consistently reproduce — this time on a level from Serious Sam 3. We were tinkering around with the options on that scene, when it suddenly occurred to me. I realized what was causing this! And it was so simple that it’s no wonder it was escaping everyone’s attention for a decade.
By changing just one very simple game engine option, we were able to make this problem go away or come back, in this particular scene. But it was immediately obvious to both of us that solving this for good will probably take much, much more effort. Effort not just by us, but from the entire PC gaming ecosystem — GPU driver writers, API maintainers, OS vendors — everyone.
What’s been going on all along
I wish I could now show you an example based on the scene from Serious Sam 3 that Dean and I were looking at five years ago. Or even better, the original test scene in Serious Sam 2 where we first saw this. Unfortunately, this elusive beast moves from scene to scene as you change hardware. I do have a scene from The Talos Principle where I was able to reproduce this recently, and I took some videos, which we will now analyze in more detail.
But before we start, we must first make sure you can actually view 60 fps videos. In the below examples, make sure you set your viewer to 1080p60, as shown here:
If you set that correctly, and if your computer and web browser are capable of showing 60 fps videos, then the video below should play perfectly smoothly, without any stuttering whatsoever. If not, oh well — that’s why we are talking about this: many other applications manifest this same problem as well, not just games. For now, I can only tell you to try on some other machine. Or just read the text.
Now for the real thing. If you are experiencing the stutter, it most probably looks something like this:
Yes, that’s what it looks like when a game “stutters” even though it runs at 60 fps. You might have experienced something similar when playing any modern game, and you probably thought “the game is not optimized”. Well, let’s reconsider that theory (of such stutter being caused by the game rendering “slowly”). If a game is “too slow”, then it means at some points it will not be able to render one frame quickly enough, and the monitor will have to re-show the previous frame again. So, when we take a 60 fps video of it, the video must exhibit “dropped frames”. (These are the frames where the next frame wasn’t rendered in time so the same frame was shown twice.)
Now open the previous stuttering video (the “heartbeat”) again, pause the video and use the . (dot) key in the YouTube player to move frame by frame. Try to find where the same frame is shown twice. Go on, try it out. I’ll wait here…
So, did you find it? No? Now that’s weird, isn’t it…
It looks very not-smooth when you play the entire animation as a whole, but when you go frame-by-frame, there are no discontinuities!
How can that be?
Let’s examine this in more details. Here is a side-by-side comparison of the ideal smooth video, and the one with the heartbeat stutter, played back at 1/20th of original speed, so you can see individual frames:
You can notice two things: First, that they indeed run at the same rate — whenever there’s a new frame in the top (correct), there is also a new frame in the bottom (heartbeat). Second, they seem to move a bit differently for some reason — there’s a noticeable “tear” in the middle of the image, and it is oscillating between being more and less apart.
A careful eye may observe one more curious detail: The bottom image — the stuttering one — which is supposedly “slower”… is actually going “ahead” of the correct one. Strange, huh?
If we take a look at a few consecutive frames, and their timings (notice that the videos I’ve been showing so far all have precise timers (precise to 1/10,000th of a second), we can observe something very interesting: The first two frames are perfectly in sync, but then the third one…
Six consecutive frames from the comparison video, with precise timing. Top is correct, bottom is heartbeat stutter.
…on the third frame the tree on the “slower” video is significantly ahead of its counterpart on the correct video (circled in red). You can also notice that this frame apparently took a longer time (circled in yellow).
Wait, wait, wait… if a video is “slower”, and the frame “took more time” how can it be ahead?
Well, to explain this, you have to understand how games, and other 3D interactive applications are actually doing their animation and rendering nowadays. (Experienced developers will excuse me if I’m boring them with things they know here, but I have to make sure all the gamers that might be interested in this can follow the text.)
A brief history of frame timing
A long time ago, in a galaxy far, far away… When developers made first video games, they would normally design for the exact frame rate that the display runs on. In the NTSC regions which run TVs at 60 Hz, it would mean 60 fps, in PAL/SECAM regions which run TVs at 50 Hz, it would mean 50 fps. They would never even exercise a thought of perhaps “dropping a frame”.
Most games were very streamlined and simplified concepts, running on fixed hardware — usually an arcade console, or a well known “home micro-computer”, like ZX Spectrum, C64, Atari ST, Amstrad CPC 464, Amiga, etc. Basically, one designed, implemented and tested for a particular machine and particular frame rate, and was 100% sure that it would never drop a frame anywhere.
Velocities of objects were also stored in “frame” units. So you wouldn’t say how many pixels per second a character would move, but how many pixels per frame. In Sonic The Hedgehog for Sega Genesis, e.g. rolling speed is known to be exactly 16 pixels per frame. Many games even had separate versions for PAL and NTSC regions where animations were hand-drawn specifically for 50 fps and 60 fps respectively. Basically, running at any other frame rate was not an option.
As games started running on more varied machines — notably PCs with expandable and upgradeable hardware — one couldn’t be sure which frame rate the game will run on anymore. Compounding that fact was the fact that games became more complicated and unpredictable — most notably 3D games can have large variances in scene complexities, sometimes even player-driven variances. E.g. everyone loves shooting at a stack of fuel barrels — causing a huge explosion, nice fireworks… and an inevitable frame drop. But we don’t mind the frame drop there — because it’s so much fun.
So it can be hard to predict how long it will take to simulate and render one frame. (Note that on consoles today, we still have fixed hardware, but the games themselves are often quite unpredictable and complex anyway.)
If you cannot be sure which frame rate the game will be running at, you have to measure the current frame rate and continually adapt the game’s physics and animation speed. If one frame is taking 1/60th of a second (16.67 ms), and your character runs 10 m/s, then it moves by 1/6th of a meter in each frame. But if the frame is not 1/60th anymore, rather it suddenly started taking 1/30th of a second (33.33ms) — you have to start moving the character by 1/3rd of a meter (two times “faster”) per frame, so that it continues moving at the same apparent speed on screen.
How does a game do this? Basically —it measures time at the start of one frame, then on the start of the next one and calculates the difference. It’s quite a simple method, but it works very well. Sorry, it used to work very well. Back in the ’90s (remember those “35 fps speeds for serious competitive netplay” from the beginning), people were more than happy with this method. But at that time, a graphics card (remember, they weren’t even called GPUs then) was a very “thin” piece of hardware, and the main CPU had direct control over when things get to the screen. If you didn’t have a 3D accelerator, the CPU was even drawing the things directly. So it knew exactly when they are ending up on screen.
What is actually going on today
Over time, as we started having more complex GPUs, those GPUs became more and more “asynchronous”. That means that when the CPU gives a command to the GPU to draw something on the screen, the GPU just stores that command in a buffer, so that the CPU can go on with its own business while the GPU is rendering. That ultimately results in the situation where the CPU tells the GPU that “this is the end of the frame” and the GPU just stores this as a nice piece of data. But it doesn’t really treat it as something of much urgency. How could it — when it is still processing some of the previously issued commands. It will show the frame on the screen when it’s done with all the work it’s been given before.
So, when a game is trying to calculate the timing by subtracting timestamps at the start of two successive frames, the relevance of that is, to be blunt… quite dubious. Let’s get back to our example from those short videos. We had those frames with camera panning across some trees:
Now recall this thing with timing and movements. In the first two frames, the frame timing was 16.67ms (which is 1/60th of a second), and the camera moves by the same amount in the top and bottom cases, so the trees are in sync. In the third frame, (in the bottom, stuttering case) the game saw that the frame time is 24.8ms, (which is more than 1/60th of a second), so it thinks that the frame rate has dropped and rushes to move the camera a bit more… only to find on the next, fourth frame the timing is only 10.7ms, so the camera moves a bit less there, and the trees are now more or less in sync again. (They don’t completely recover until about two frames later when everything reconsolidates finally.)
What happens here is that the game measures what it thinks is start of each frame, and those frame times sometimes oscillate due to various factors, especially on a busy multitasking system like a PC. So at some points, the game thinks it didn’t make 60 fps, so it generates animation frames slated for a slower frame rate at some of the points in time. But due to the asynchronous nature of GPU operation, the GPU actually does make it in time for 60 fps on every single frame in this sequence.
This is what we see as a stutter — animation generated for a varying frame rate (heartbeat) being displayed at actual correct fixed frame rate.
So, basically, there’s no problem whatsoever — everything is running smoothly, it’s just that the game doesn’t know it.
This brings us to the point from the beginning of the article. When we finally figured out that this is what caused the problem (actually, it’s an illusion of a problem — there’s no problem in fact, right?), here’s what we did for a test:
In the first part of the video above, you can see the heartbeat issue from the beginning. Then we change a “magic” option and after that — everything becomes perfectly smooth!
What’s the magic option? In Serious Engine, we call this sim_fSyncRate=60 . In layman’s terms it basically means: “completely ignore all these timing shenanigans and pretend that we are always measuring steady 60 fps”. And it makes everything run smoothly — only because it was always running smoothly to begin with! The only reason why it ever looked stuttering is because the timing used for animation was wrong.
So that’s it? We just do that and everything is great?
Is the solution that simple?
Unfortunately… nope. That was only for a developer test. If we would stop measuring frame rate in real-world situations and just assume it is always 60, then when it does drop below 60 — and on a PC it will drop sooner or later for various reasons: OS running something in the background, power-saving or overheating protection down-clocking the GPU/CPU… who knows —then everything will slow down.
So, if we measure frame time, it stutters, if we don’t, everything can slow down at some points. What then?
The real solution would be to measure not when the frame has started/ended rendering, but when the image was shown on the screen.
So, how can the game know when a frame’s image is actually shown on screen? You might be surprised to learn that, in the current situation— there’s no way to do it!
Shocking, I know. One would expect this would be a basic feature of every graphics API. But it turns out that as things have been changing slowly here and there, everyone basically dropped the ball on this issue. We all forgot about the fine details of what is going on, kept doing basically what we were doing all the time, and the graphics APIs have evolved in all other aspects but this one: There’s no way for the application to know for sure when a frame was actually displayed on the screen. You can know when it finished rendering. But not when it got displayed.
What now?
Worry not, it’s not all that grim. Many people in the graphics ecosystem are currently busily working on implementing support for proper frame timing, under various names for different APIs. Vulkan API already has an extension called VK_GOOGLE_display_timing which was shown useful in a proof of concept implementation, but it is available only for a limited range of hardware and mostly on Android and Linux.
Work is now underway to provide such, and better facilities, hopefully in all the major graphics APIs. When? It’s hard to say, because the problem cuts quite deep into various OS subsystems.
I can promise you, though, that we at Croteam are advocating tirelessly for this problem to be fixed as soon as possible — and everyone in the interactive graphics ecosystem is very understanding and helpful.
We are looking forward to having this available to a broader public, and when that happens, we will provide an update for The Talos Principle that implements this feature.
Miscellaneous caveats and other details
Consider the above an end of the main text. The sections below are “bonus features”, mostly independent of one another and the main text. I will probably be updating the below as the situation changes, or if more complex questions pop up that require addressing in the near future.
“The Compositor”
One thing involved in all this behind the scenes is the concept called Compositing Window Manager, aka the compositor. It’s the system now present in every OS that makes it possible for windows to be transparent, have blurry backgrounds, shadows, Skype popups over them, etc. Compositors can even go really overboard and show your windows in 3D. To do that, a compositor takes over the control of the very last part of frame image presentation and decides what to do with it immediately before it ends up on the monitor. This kinda complicates things a bit more.
On some OSes, the compositor can be turned off in full-screen mode. But that’s not always possible, and even then —why wouldn’t we be able to run the game in the windowed mode?
Power and thermal management vs rendering complexity
We also have to take into account the fact that modern CPUs and GPUs don’t run at fixed clocks, but both have systems that throttle their speed up and down depending on the load, and on current temperature. So the game cannot just assume that the GPU and CPU will have same speed from frame to frame. On the other hand, the OS and drivers cannot expect that the game will have the same amount of work in each frame. Complex systems for communication between the two sides have to be designed so that this is all taken into account.
Couldn’t we just…
Probably not. 🙂 Usually, measuring GPU time is suggested as an alternative to display timing. But that doesn’t take into account presence of the compositor and the fact that none of the GPU rendering timers actually synchronize directly with the display refresh. What we need for perfect animation is definitely time when image was shown, not time when it finished rendering.
Что означают 1 % и 0,1 % при тестировании видеокарт и процессоров в играх
![]()
Все чаще при измерении производительности ПК в играх можно заметить показатели «0.1 % Low» и «1 % Low». Большинство неопытных пользователей не придают этому значения и по старинке смотрят только на средний FPS. На самом деле эти показатели очень важны и на них стоит обращать внимание. И вот почему.
Что такое средний FPS и Frame Time
Время, требуемое для отрисовки одного кадра называется Frame time или же «время кадра». Измеряется оно в миллисекундах, но обычно используют частоту кадров (Frame rate), которая обозначает количество кадров, отрисованных за единицу времени. Частота кадров же измеряется в количестве кадров в секунду — Frames per second или же FPS.
Главная единица, используемая при измерении производительности — средний FPS (AVG FPS) за весь промежуток времени. Средний FPS находится по формуле FPS = n/t, где n — количество кадров, отрисованное за все время, а t — время проведения теста. У среднего FPS есть недостаток, который не позволяют ему быть единственной единицей измерения в бенчмарках.
0.1 % минимальный и 1 % низкий FPS
При измерении FPS его среднее значение не является точной величиной, поэтому внимание стоит уделить другим — 1 % низкий и 0.1 % минимальный FPS. В нашем случае важно понимать, что время отрисовки кадра зависит от его сложности. Во время игры могут встречаться карты с большим количеством предметов и NPC в поле видимости игрока, на отрисовку которых будет уходить больше времени. Такие кадры могут задерживаться на экране, в результате чего картинка может фризить и испортить впечатление от игры. Проблема среднего FPS заключается в том, что при замерах время «длинных» кадров усредняется с «быстрыми», поэтому информация о первых теряется.
![]()
К примеру, за секунду было отрисовано 30 кадров с таким временем отрисовки в мс:
33, 57, 23, 13, 34, 68, 34, 40, 44, 16, 90, 27, 66, 87, 23, 37, 17, 23, 31, 21, 23, 20, 37, 12, 32, 36, 22, 14, 20, 10
В данном случае средний FPS равен 30 кадрам в секунду, а среднее время отрисовки кадра — 33,3 мс. Общая картина достаточно неплоха, но если взглянуть пристальнее, то можно заметить четыре кадра, время отрисовки которых в два, а то и в три раза больше среднего. Как и было сказано ранее, при высчитывании среднего времени отрисовки кадра и среднего FPS «долгие» кадры теряются на фоне «быстрых», в результате чего значения получаются неточными.
Было принято решение как-нибудь дополнить значения среднего FPS, чтобы лучше описать все кадры.
Существует такое понятие как процентиль, с английского — percentile (в русском языке чаще встречаются персентиль или перцентиль). В нашем случае это можно трактовать как значение, ниже которого находится определенный процент данных из общего набора. У нас 99-процентиль — это значение, ниже которого находятся 99 % данных из общего числа. И, если он равен 90 мс, то 99 % значений времени кадра из примера меньше 90 мс, а 1 % больше или равен этому числу.
![]()
В бенчмарках по договоренности используются 99.9- и 99-процентили. Поскольку обычно в качестве единицы измерения применяются FPS, то в данном случае используются обратные 0.1- и 1-процентили FPS. В народе их принято называть 0.1 % минимальный и 1 % низкий FPS. Обычно эти значения оказываются ниже среднего FPS, так как это часть данных, которая описывает редкие игровые события с многочисленным количеством объектов. Это говорит о том, что сложность кадров в сцене непостоянна. Плохо это только тогда, когда 0.1 % минимальный и 1 % низкий FPS «просаживаются» до неиграбельного уровня в результате чего картинка начинает подлагивать. Правда, оценить этот неиграбельный уровень статистически невозможно — для каждого он свой в связи с особенностями человеческого глаза и привычками геймера.
Математические объяснения недостатков среднего FPS (для любознательных)
Между временем кадра и частотой кадров есть математическая связь: значение FPS после отрисовки кадра — мгновенный FPS — обратно времени отрисовки этого кадра:
Поскольку время кадра обычно измеряется в миллисекундах, а частота кадров в единицах в секунду, вышеуказанная формула будет выглядеть вот так:
Например, кадр был отрисован за 25 мс, тогда получается, что мгновенный FPS по окончании его отрисовки был равен 1000/25 = 40 FPS.
Как уже было сказано ранее, средний FPS находится по формуле FPS = n/t. Кроме нее средний FPS можно найти так:
Где t — среднее время кадра, равное t = (t1 + t2 + t3 + … + t-нное)/n
n — общее количество кадров, t1, t2, t3 и т. д. — время отрисовки каждого кадра
То есть, средний FPS — величина, обратная среднему времени кадра. Подтверждается это тем, что время бенчмарка равняется сумме времени отрисовки всех кадров:
t1 + t2 + t3 + … + t-нное = t
Но почему же средний FPS — недостаточно точный показатель измерений? Так происходит, потому что средний FPS не является средним арифметическим значений мгновенного FPS:
FPS ≠ (FPS1 + FPS2 + FPS3 + … + FPS-нное)/n
FPS ≠ (1000/t1 + 1000/t2 + 1000/t3 + … + 1000/t-нное)/n
Для подтверждения этому приведем одну из вышеперечисленных формул:
FPS = 1000/t, где t — t = (t1 + t2 + t3 + … + t-нное)/n
FPS = 1000n/(t1 + t2 + t3 + … + t-нное)
Значения среднего FPS и среднего значения мгновенного FPS будут равны только в том случае, когда все кадры были отрисованы за одинаковые промежутки времени – t1 = t2 = t3 = t-нное = t, что на практике практически невозможно. В этом и заключается главный недостаток среднего FPS.
Средний FPS далеко не идеален, и при измерении производительности системы в играх ориентироваться только на него не стоит. 0.1% минимальный и 1% низкий FPS наоборот являются очень важными единицами измерения. Если говорить простым языком, то они показывают самые большие просадки FPS за время теста, портящие общее впечатление от игры.
Неуловимая проблема тайминга кадров

Наконец-то появилось объяснение того, почему некоторые игры тормозят на вашем PC (и луч надежды на то, что в ближайшем будущем они тормозить перестанут).
Т-т-тормоза
Вы с нетерпением ждали следующей части вашей любимой серии видеоигр для PC и она наконец вышла. На этот раз вы хотите насладиться ею во всей полноте, поэтому потратили деньги и время на тщательную подготовку. Вы заменили процессор, поставили сверхсовременную видеокарту, добавили ещё ОЗУ — чёрт возьми, даже купили RAID на SSD. Игра должна быть плавной с самой заставки.
Предзаказ наконец разблокирован и вы только что завершили установку. В нервном предвкушении вы впервые запускаете игру. Пока всё хорошо — она работает с частотой 60 кадров в секунду. Или, по крайней мере, так сообщает счётчик кадров тюнера GPU. Но что-то не так. Вы делаете мышью резкие, хаотичные движения. Стрейфитесь влево-вправо, и тут игра… начинает тормозить! Блин, да как такое возможно? Как она может тормозить при 60 кадрах в секунду?
Если такое с вами никогда не случалось, то это может показаться смешным. Но если вы их испытали, то, скорее всего, ненавидите тормоза всей душой. Тормоза в играх. Это не старый добрый «лаг». Не низкая частота кадров. Это просто «тормоза», происходящие при высоких частотах кадров на идеальных, супербыстрых машинах. Что это, откуда они взялись и как от них избавиться? Позвольте мне рассказать вам историю…
Тормоза, плавность, скорость… это ведь одно и то же?
Видеоигры работали с частотой 60 fps ещё со времён первых аркадных автоматов в 70-х годах. Обычно ожидается, что игра работает с той же частотой, которая используется дисплеем. Так было до популяризации 3D-игр, в которых впервые стала допустимой пониженная частота кадров. В 90-х, когда «3D-карты» (так мы называли их до того, как они стали «GPU«) начали заменять программный рендеринг, люди играли в игры при 20 fps, а 35 fps считались приличным значением для серьёзных сетевых боёв. Я не шучу.
Сегодня у нас есть супербыстрые машины и «разумеется, они могут работать при 60 fps«. Однако количество разочарованных скоростью игр пользователей как никогда велико. Как такое возможно? Проблема оказывается не в том, что игры не могут работать достаточно быстро, а в том, что они тормозят, даже когда могут работать быстро!
Если почитаете разные игровые форумы, то наверняка найдёте подобные сообщения:

Можно подумать, что это единичные проблемы, но посмотрите на статистику поисковых запросов Google:

За последние пять лет тормоза (stutter) стали (относительно) более серьёзной проблемой, чем скорость!
(Учтите, что это относительные значения. Они не значат, что в целом люди спрашивают о торможениях больше, чем о частоте кадров. Они значат, что запросы о частоте кадров (frame rate) остаются на том же уровне, а количество запросов о тормозах растёт, особенно в последнее время.)
Десяток лет в поисках причины необъяснимых тормозов
Пациент скорее жив, чем мёртв, просто тормозит чуть больше, чем нужно.
Впервые я столкнулся с этой проблемой ещё примерно в 2003 году. Мы работали над Serious Sam 2, и пользователи начали отправлять нам отчёты о том, что они тестировали что-то на пустом уровне, и при перемещениях мыши движения не были плавными. Это сопровождалось очень характерным паттерном на графике частоты кадров, который мы прозвали «кардиограммой».
Мы думали, что где-то в коде есть баг, но не могли его найти. Казалось, что проблема появляется и исчезает случайным образом — после перезапуска приложения, перезагрузки машины… а потом игрок менял какую-нибудь опцию скорости и она исчезала. Потом игрок снова включал эту опцию, но проблема не возвращалась. Она была похожа на призрака.
Очевидно, что эта проблема возникала не только у нас. Наблюдая за теми же проблемами в других играх, мы начали думать, что виноваты драйверы. Но это происходило на видеокартах разных производителей. Даже на разных API (OpenGL, DirectX 9, DirectX 11…) — единственным общим у них было то, что они появлялись на разных машинах, в некоторых сценах… иногда.

Несси, бигфут… почти столь же неуловимые, как и проблема с «кардиограммой».
Мы выпустили ещё несколько игр, но это странное поведение по-прежнему появлялось и исчезало. Некоторых пользователей оно раздражало, и мы рекомендовали им изменить опции скорости — иногда это помогало, иногда нет. Такова жизнь, не правда ли?
Но однажды, в отличный зимний день в начале 2013 года мой коллега Дин позвал меня, чтобы я увидел ещё один пример этой проблемы, который он на тот момент мог относительно стабильно воспроизводить. На этот раз проблема возникала на уровне из Serious Sam 3. Мы экспериментировали с опциями в этой сцене, пока до меня внезапно не дошло. Я понял, в чём была причина! И она была очень проста — неудивительно, что она ускользала от всех в течение десятка лет.
Изменив всего одну очень простую опцию игрового движка, мы смогли заставить эту проблему появляться и исчезать в этой конкретной сцене. Но нам стало сразу же очевидно, что на её качественное решение потребуется гораздо больше усилий. Усилий не только с нашей стороны, но и всей игровой экосистемы PC — программистов драйверов GPU, разработчиков API, поставщиков ОС — каждого.
В чём же была причина всё это время
Хотел бы я показать вам её на примере сцены из Serious Sam 3, которую мы с Дином исследовали пять лет назад. Или даже ещё лучше — на примере тестовой сцены из Serious Sam 2, в которой мы впервые её увидели. Но, к сожалению, после замены «железа» этот ускользающий зверь может переместиться в другую сцену. У меня есть сцена из The Talos Principle, в которой мне недавно удалось воспроизвести эту проблему, и я заснял несколько видео, позволившие проанализировать её более подробно.
Но прежде чем мы начнём, убедитесь, что вы на самом деле смотрите видео в 60 fps. Для просмотра представленных ниже примеров переключитесь в 1080p60, как показано на картинке:

Чтобы смотреть видео в 60 fps, переключитесь в YouTube на 1080p60.
Если вы всё сделаете правильно, а ваш компьютер и веб-браузер способны показывать видео с частотой 60 fps, то представленное ниже видео должно воспроизводиться плавно и без всяких тормозов. Если это не так, то именно поэтому мы и говорим об этом — многие другие приложения тоже демонстрируют такое поведение, не только игры. Пока я могу только рекомендовать вам попробовать посмотреть видео на какой-нибудь другой машине, или просто читать текст.
Проверка, проверка, раз, два, три… Вы должны видеть это видео в плавных 60 fps.
А теперь перейдём к делу. Если вы сталкиваетесь с тормозами, то вероятнее всего это выглядит примерно так:
Вот как выглядят «тормоза при 60 fps». Мы называем этот симптом «кардиограммой».
Да, именно так выглядят «тормоза», даже когда игра работает с 60 fps. Вы могли сталкиваться с чем-то подобным в любой современной игре, и вероятно думали, что «игра не оптимизирована». Так вот, вам стоит пересмотреть свою теорию (о том, что такие тормоза возникают из-за «медленного» рендеринга игры). Если игра «слишком медленная», то это значит, что в какие-то моменты она не сможет достаточно быстро отрендерить один кадр, а монитору придётся заново показывать предыдущий кадр. Поэтому когда мы записываем видео такой игры в 60 fps, то видим «пропущенные кадры». (Это такие кадры, при которых следующий кадр не был отрендерен вовремя, поэтому текущий кадр показывается дважды.)
Теперь снова запустите предыдущее видео с тормозами («кардиограммой»), поставьте его на паузу и нажимайте в проигрывателе YouTube клавишу . (точка), чтобы перемещаться от кадра к кадру. Попробуйте заметить, когда один и тот же кадр показывается дважды. Давайте, попробуйте, я подожду…
Ну как, нашли? Нет? Странно, не правда ли.
Как такое может быть?
Позвольте мне объяснить более подробно. Вот сравнение идеально плавного видео и видео с тормозами в виде «кардиограммы», воспроизводимые на 1/20 от исходной скорости, чтобы мы могли видеть отдельные кадры:
Сверху правильное видео с 60 fps, снизу «кардиограмма». Воспроизведение замедлено в 20 раз.
Можно заметить две вещи: во-первых, они действительно воспроизводятся с одинаковой частотой — когда сверху есть новый кадр, снизу тоже есть новый кадр. Во-вторых, они по какой-то причине движутся немного по-разному — есть заметный «разрыв» в середине изображения, который становится то более, то менее заметным.
Внимательный зритель может заметить и ещё одну любопытную деталь: нижнее — тормозящее — изображение — которое считается «медленным»… на самом деле «опережает» правильное. Странно, правда?
Если мы посмотрим на два соседних кадра и их тайминги (заметьте, что во всех показанных мной видео были точные таймеры (с точностью 1/10 000 секунды), то можем заметить нечто очень интересное: первые два кадра идеально синхронизированы, но третий…

Шесть идущих друг за другом кадров из видео с точными таймингами. Верхние — правильные, нижние — с «кардиограммой».
…на третьем кадре мы видим, что дерево в «тормозящем» видео значительно опережает свою копию из правильного видео (обведено красным). Также можно заметить, что на этот кадр, похоже, ушло больше времени (обведено жёлтым).
Постойте… если видео «медленнее», и на кадр «ушло больше времени», то как он может опережать правильный?
Чтобы разобраться в этом, вам нужно понять, как в наше время игры и другие трёхмерные интерактивные приложения реализуют анимации и рендеринг. (Опытные разработчики должны простить меня за то, что я надоедаю им с очевидными вещами, но мне нужно быть уверенным, что текст смогут понять все читающие этот текст игроки.)
Краткая история таймингов кадра
Давным-давно, в далёкой-далёкой галактике… Когда разработчики создавали первые видеоигры, они обычно подстраивались под точную частоту кадров, с которой работает дисплей. В регионах NTSC, где телевизоры работали при 60 Гц, это означало 60 fps, в регионах PAL/SECAM, где телевизоры работали при 50 Гц, это означало 50 fps. Они даже и думать не могли о какой-то возможности «пропуска кадра».
Большинство игр было очень вылизанными и упрощёнными концептами, работавшими на конкретном оборудовании — обычно на аркадном компьютере, или на «домашнем микрокомпьютере», наподобие ZX Spectrum, C64, Atari ST, Amstrad CPC 464, Amiga и т.д. По сути, разработчик создавал дизайн, реализовывал и тестировал игру под конкретную машину и конкретную частоту кадров, и был на 100% уверен, что она никогда не пропустит ни кадра.
Скорости объектов тоже хранились в единицах «кадров». Поэтому говорили, не на сколько пикселей в секунду должен двигаться персонаж, а на сколько пикселей за кадр. Известно, что в Sonic The Hedgehog для Sega Genesis скорость вращения, например, была равна 16 пикселям за кадр. У многих игр даже были отдельные версии для регионов PAL и NTSC, в которых анимации вручную рисовались специально под 50 fps и 60 fps. По сути, запуск при любой другой частоте кадров даже не рассматривался.
Когда игры начали работать на более разнообразных машинах — в частности, на PC с расширяемым и заменяемым «железом» — разработчики больше не могли знать, с какой частотой кадров теперь будет работать игра. Дополните это тем фактом, что игры стали более сложными и непредсказуемыми. Самыми выдающимися в этом плане стали 3D-игры, отличавшиеся разнообразием сложности сцен, иногда даже зависящей от игрока. Например, всем нравится стрелять в бочки с топливом — при этом происходит огромный взрыв, красивые эффекты… и неизбежная просадка кадров. Но здесь никто не против пропуска кадров, потому что это так интересно.
Поэтому было очень сложно предсказать, сколько займёт симуляция и рендеринг одного кадра. (Стоит заметить, что на современных консолях «железо» по-прежнему неизменно, но сами игры всё равно часто непредсказуемы и сложны.)
Если мы не можем быть уверенными, при какой частоте кадров будет работать игра, нам приходится измерять текущую частоту кадров и постоянно адаптировать физику и скорость анимаций игры. Если один кадр занимает 1/60 секунды (16,67 мс), а персонаж бежит со скоростью 10 м/с, то в каждом кадре он перемещается на 1/6 метра. Но если кадр перестаёт занимать 1/60 секунды, а вместо этого внезапно начал занимать 1/30 секунды (33,33 мс), то надо начать перемещать персонажа на 1/3 метра (в два раза «быстрее») за кадр, чтобы на экране он продолжал двигаться с кажущейся постоянной скоростью.
Как же игра это делает? По сути она измеряет время в начале одного кадра, а затем в начале следующего и вычисляет разность. Это довольно простой способ, но он очень хорошо работает. То есть простите, он работал хорошо. В 90-х (вспомните фразу «35 fps считались приличным значением для серьёзных сетевых боёв» из начала статьи), людей более чем устраивал этот способ. Но в то время графическая карта (не забывайте, их даже ещё не называли GPU) была очень «тонким» элементом «железа», и попаданием объектов на экран непосредственно управлял основной ЦП. Если в компьютере не было 3D-ускорителя, процессор даже сам отрисовывал эти объекты. Поэтому он точно знал, когда они появятся на экране.
Что происходит сегодня
Со временем у нас появились более сложные GPU, и они становились всё более и более «асинхронными». Это значит, что когда ЦП отдаёт GPU команду отрисовать что-то на экране, то GPU просто сохраняет эту команду в буфер, чтобы ЦП занимался своими делами, пока GPU выполняет рендеринг. В результате это привело к тому, что когда ЦП сообщает GPU, что «это конец кадра», GPU просто сохраняет сообщение как ещё один фрагмент данных. Но он не относится к нему как чему-то особо срочному. Да и как он может — ведь ему по-прежнему нужно обрабатывать часть из ранее переданных команд. Он покажет кадр на экране, когда закончит всю работу, которую ему дали раньше.
Поэтому когда игра пытается вычислить тайминги, вычитая метки времени начала двух кадров, то актуальность этого, грубо говоря… весьма сомнительна. Давайте вернёмся к нашему примеру из коротких видео. У нас есть эти кадры, где камера перемещается вдоль деревьев:

Шесть идущих друг за другом кадров из видео с точными таймингами. Верхние — правильные, нижние — с «кардиограммой».
Теперь вспомните то, что я говорил о таймингах и перемещениях. В первых двух кадрах тайминг кадра равен 16,67 мс (то есть 1/60 секунды), и камера движется на одну и ту же величину и сверху, и снизу, поэтому деревья синхронизированы. На третьем кадре (внизу) игра увидела, что время кадра равно 24,8 мс (что больше 1/60 секунды), поэтому она думает, что частота кадров снизилась и торопится переместить камеру чуть дальше… только для того, чтобы обнаружить, что тайминг четвёртого кадра составляет всего 10,7 мс, поэтому камера движется здесь чуть медленнее, и деревья снова более-менее синхронизируются. (Синхронизация восстановится полностью только спустя два кадра.)
Здесь происходит следующее: игра измеряет то, что она считает началом каждого кадра, а эти тайминги кадров иногда по разным причинам колеблются, особенно на такой высоконагруженной многозадачной системе, как PC. Поэтому в некоторые моменты игра думает, что не успевает обеспечить 60 fps, и в какие-то моменты генерирует кадры анимации, рассчитанные на меньшую частоту кадров. Но из-за асинхронной природы работы GPU он на самом деле успевает обеспечить 60 fps для каждого отдельного кадра этой последовательности.
То есть, по сути, здесь нет никакой проблемы — всё действительно работает плавно, просто игра об этом не знает.
При этом мы возвращаемся к началу статьи. Наконец обнаружив причину проблемы (на самом деле, это иллюзия проблемы — ведь её на самом деле нет, так?), вот что мы сделали для проверки:
Сначала мы наблюдаем «кардиограмму», а затем используем небольшой трюк, чтобы избавиться от неё.
В первой части видео вы с самого начала можете увидеть проблему с «кардиограммой». Затем мы изменяем «магическую» опцию, после чего всё становится идеально плавным!
Что же это за волшебная опция? В Serious Engine мы называем её sim_fSyncRate=60 . Если говорить простыми словами, то это значит «полностью игнорировать все эти заморочки с таймингами и притвориться, что мы всегда замеряем стабильные 60 fps». И благодаря этому всё работает плавно — просто потому, что всё и так работало плавно! Единственная причина, по которой всё выглядело тормозящим — это ошибочные тайминги, используемые для анимации.
И на этом всё? Достаточно сделать так и всё станет замечательно?
Неужели решение настолько простое?
К сожалению — нет. Это всего лишь тест разработчиков. Если мы перестанем замерять частоту кадров в реальных ситуациях и просто будем предполагать, что она всегда равна 60, то когда она упадёт ниже 60 — а на PC она рано или поздно обязательно упадёт по тем или иным причинам: ОС запустит какой-нибудь фоновый процесс, включится экономия энергии или защита от перегрева GPU/ЦП… кто знает — то всё замедлится.
Итак, если мы изменяем частоту кадров, то возникают торможения, если нет — то всё в какие-то моменты может замедляться. Что же нам делать?
Реальное решение заключается в том, чтобы измерять не начало/завершение рендеринга кадра, а время отображения картинки на экране.
Это шокирует, я понимаю. Можно было ожидать, что это базовая функция каждого графического API. Но оказывается, в процессе постепенных улучшений тут и там все, в сущности, упустили сам смысл проблемы. Мы все забыли о тонких деталях своей работы, продолжая повторять одно и то же, а графические API эволюционировали во всех других аспектах, кроме этого: приложение никаким образом не может точно узнать, что кадр на самом деле отображается на экране. Мы можем узнать, когда закончится его рендеринг, но не когда он будет показан.
Что дальше?
Но не волнуйтесь, всё не так мрачно. Многие люди в графической экосистеме активно работают над реализацией поддержки правильного тайминга кадров для разных API. Для Vulkan API уже есть расширение под названием VK_GOOGLE_display_timing , которое продемонстрировало свою полезность в реализации proof of concept. Однако оно доступно только для ограниченного ассортимента оборудования, в основном на Android и Linux.
Уже ведётся работа по созданию аналогичных и более качественных систем для всех основных графических API. Когда она закончится? Сложно сказать, потому что проблема лежит довольно глубоко внутри различных подсистем ОС.
Однако я заверяю вас, что Croteam безустанно работает над тем, чтобы эта проблема была решена как можно скорее, и все участники экосистемы интерактивной графики понимают и поддерживают наши усилия.
Мы стремимся сделать это решение доступным для более широкой аудитории, и когда это случится, мы подготовим апдейт The Talos Principle с реализацией этой функции.
Различные помехи и другие подробности
Последнюю фразу можно считать концом основного текста. Ниже представлены «бонусные разделы», практически не связанные друг с другом и основным текстом. Вероятно, я буду обновлять их, когда ситуация станет меняться, или в случае возникновения более сложных вопросов, требующих решения в ближайшем будущем.
«Композитор»

Эффект матового стекла? Да, для него нам определённо нужен композитор. Он просто-таки необходим, правда?
За кулисами со всем этим связан концепт под названием «композитный менеджер окон», или композитор. Это система, существующая теперь во всех ОС, которая позволяет окнам быть прозрачными, иметь размытый фон, тени, всплывающие поверх окна Skype и т.д. Композиторы даже могут отображать окна в 3D. Для этого композитор перехватывает управление над последним этапом создания картинки кадра и решает, что ему сделать с ней, прежде чем она попадёт на экран монитора. Это ещё больше всё усложняет.
В некоторых ОС композитор можно отключить в полноэкранном режиме. Но это не всегда возможно, а даже если и возможно — то разве можно запретить запускать игру в оконном режиме?
Управление питанием и тепловыделением против сложности рендеринга
Мы также должны учитывать, что современные ЦП и GPU не работают с фиксированной частотой, а имеют системы, изменяющие их скорости в соответствии с нагрузкой и температурой. Поэтому игра не может просто предполагать, что GPU и ЦП будут иметь одинаковую скорость в каждом кадре. С другой стороны, ОС и драйверы не могут ожидать, что у игры в каждом кадре будет одинаковый объём работы. Чтобы учесть это, необходимо разработать сложные системы для общения этих двух сторон.