Page 1 of 1
V-sync - Windows, DirectX
Posted: Tue Mar 19, 2019 11:24 am
by posila
If you have Desktop Window Manager (Aero in Vista and Win 7, always in Win 8+), Factorio should be presented to screen by DWM, always with v-sync, no matter what is the setting in Factorio. So there
shouldn't be any tearing regadless of "Wait for V-sync" options. In fact, when Factorio 0.16 detected desktop composition was enabled, v-sync would be turned off by default.
So why does 0.17 turn it on by default? During migration to DirectX 11, I spent little bit of time looking into stuttering issues Factorio has and wanted to see if we can fix it in the new rendering backend. I found out two things
- Factorio is bad timing its frames. Frame times oscilate between ~14 ms to ~18 ms, which averages out to desired 16.67 ms (60 FPS/UPS).
- Desktop refresh rate often is not 60 Hz but 59.95 Hz. Which means Factorio is possibly generating frames slightly faster than screen is displaying them, which means update and render is slowly getting desynchronized with VBLANK.
(2) means that after some time, render will just about finish when VBLANK event hits, but because of (1) this won't happen consistently, so some frames it just misses VBLANK and other times it manages to push out two frames between two VBLANKs. So until render desyncs from VBLANK again, the game is at a state when it shows a same image for two frames or randomly drops a frame, causing stutter.
If v-sync is turned on, the game will fill present queue and and Present() starts to block, this fixes the stutter and will cause the game just drop a frame occasionally, at least in theory (somewhat supported by my observations/measurements).
Obviously the best solution would be to entirely divorce update and rendering, but I don't see that happening in 2D Factorio. Render preparation step would have to capture states of entities to be rendered instead of capturing "sprite draw orders", which is way too much work to do when we are trying to finish 1.0.
Re: V-sync - Windows, DirectX
Posted: Tue Mar 19, 2019 2:09 pm
by nuhll
So, what should we do? Enable it, disable it? or does it not matter?
Re: V-sync - Windows, DirectX
Posted: Tue Mar 19, 2019 2:27 pm
by posila
My current thought is ... keep it enabled unless you have wierd performance issues, and than enable disable it.
I created the thread for disscusion how it should actually work. TruePikatchu and I have been highjacking bug reports for v-sync discussions, and didn't want to move to PMs in case some other people have some experience with this and would like to share.
Re: V-sync - Windows, DirectX
Posted: Tue Mar 19, 2019 4:02 pm
by nuhll
posila wrote: ↑Tue Mar 19, 2019 2:27 pm
My current thought is ... keep it enabled unless you have wierd performance issues, and than enable it.
I created the thread for disscusion how it should actually work. TruePikatchu and I have been highjacking bug reports for v-sync discussions, and didn't want to move to PMs in case some other people have some experience with this and would like to share.
U mean if issues, disable it?
Does it has anything to do with gsync?
Re: V-sync - Windows, DirectX
Posted: Tue Mar 19, 2019 4:05 pm
by posila
nuhll wrote: ↑Tue Mar 19, 2019 4:02 pmU mean if issues, disable it?
Oh, yes. Fixed.
nuhll wrote: ↑Tue Mar 19, 2019 4:02 pmDoes it has anything to do with gsync?
Sometimes. G-sync requires v-sync to be off to work properly, as far as I know.
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 1:54 am
by TruePikachu
If it helps with understanding the "bad frame timing", I recalled seeing (and just now confirmed) that the main loop (under non-DWM, at least, but it should be the same either way) wakes up from the conclusion of a DxgkWaitForVerticalBlankEvent, and goes to sleep by waiting for another one. I don't have a backtrace right this moment pinning down exactly where in the codebase the function is called, but it can help to explain the jitter in the tick time, especially when the refresh rate isn't exactly 60Hz. I believe this might also have been the case back in DX9, if you replace "DxgkWaitForVerticalBlankEvent" with "keep checking what the current scanline is".
This actually makes me wonder, now, what the behaviour would be on not-approximately-60Hz refresh rate; there's a growing trend of 144Hz (significantly faster), and my laptop display supports 40Hz (significantly slower)...
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 8:40 am
by orzelek
TruePikachu wrote: ↑Wed Mar 20, 2019 1:54 am
If it helps with understanding the "bad frame timing", I recalled seeing (and just now confirmed) that the main loop (under non-DWM, at least, but it should be the same either way) wakes up from the conclusion of a DxgkWaitForVerticalBlankEvent, and goes to sleep by waiting for another one. I don't have a backtrace right this moment pinning down exactly
where in the codebase the function is called, but it can help to explain the jitter in the tick time, especially when the refresh rate isn't exactly 60Hz. I believe this might also have been the case back in DX9, if you replace "DxgkWaitForVerticalBlankEvent" with "keep checking what the current scanline is".
This actually makes me wonder, now, what the behaviour would be on not-approximately-60Hz refresh rate; there's a growing trend of 144Hz (significantly faster), and my laptop display supports 40Hz (significantly slower)...
I tried to play game on 144Hz display in 0.16 times and it was.. stuttery. I'm running Win 10 so desktop composition is present. And setting of vsync was not helping with that - it felt like game would frequently stutter anyway.
I did not try on 0.17 - I can play around and check.
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 10:55 am
by Bilka
I recall that at some point in early 0.17 development I opened the game and something felt off about the rendering, it felt... irregular. I would guess it was the variable frame times, because turning on "wait for v-sync" (despite using Win 10 = automatic vsync) fixed this issue for me. I still have v-sync on to this day just because of that. So if that issue is fixable, it would be great to get it fixed so that nobody is forced to use a certain v-sync option.
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 12:59 pm
by TruePikachu
orzelek wrote: ↑Wed Mar 20, 2019 8:40 am
I tried to play game on 144Hz display in 0.16 times and it was.. stuttery.
No matter what, if your refresh rate isn't a factor or multiple of 60Hz, things will appear at least a bit stuttery; usually, that stutter is caused by frames being displayed for different amounts of time (e.g. frame A displayed for 2 vsync intervals, frame B displayed for 3 -- do note that the vsync setting will never affect this).
My wondering was more about the game's tick timing.
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 1:46 pm
by BlueTemplar
Have you tried 120 Hz ?
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 2:04 pm
by TruePikachu
You know what, today I'll try to get Direct3D working in Visual Studio, and start writing up a simple "proof-of-concept" tool that can be used to test things related to rendering in a controlled environment. I was just thinking about what exactly "vsync" should mean in the context of Factorio, and decided that it's probably a mistake to synchronize the main loop to the vblank cycle, if only because it might be coupling the main loop closer to the render side than it strictly needs to be. (If the swap chain has the correct refresh rate set up, then it should be sufficient to just queue frames to it from the main loop and `std::this_thread::sleep_until` the time the next tick should execute at, though experimentation might be needed to see if it remains stable when the potential UPS falls below 60 in e.g. megabases.)
Re: V-sync - Windows, DirectX
Posted: Wed Mar 20, 2019 4:15 pm
by Jap2.0
So why is everyone using 144 Hz instead of 120? It seems like that would give most of the benefits without screwing up compatibility with a ton of other things.
Re: V-sync - Windows, DirectX
Posted: Sun Mar 24, 2019 4:09 am
by TruePikachu
Was just looking at some traces, things seem to be, as always, complicated.
I'm not 100% sure, but it appears that between the last time I looked at traces from Factorio and today, stuff might have been changed to use `std::this_thread::sleep_until` instead of `WaitForVBlank`, which is probably a step in the right direction, though it appears that it might (based on observations I've made today) have had a side effect of reducing how stable the main loop's timing is based on the activities of other processes. Specifically, it appears that it might be the case that `WaitForVBlank` resumes execution via interrupt, while `std::this_thread::sleep_until` is resuming via the thread scheduler -- I noticed several cases where, when Factorio's main loop should have been resumed, some other process was doing work at the time and hadn't yielded yet.
I'm still not 100% sure what waiting for vsync would mean in the current Factorio environment, aside from having synchronized presentation of rendered frames. Do we continue to run the main loop at 60Hz, or do we run it based on the vblank interrupt and a steady clock? In theory, if the loop is running at vblank, we might have to do zero frames (where we just wait for the next vblank) or multiple frames, then render+present; this would keep the main loop synchronized to within one refresh interval, and only drifting as much as the steady clock does. This also means that, if the display has a less-than-60Hz refresh rate, we're not wasting any time doing rendering that can't be successfully presented. However, if the refresh rate is greater than 60Hz, we're going to occasionally wake up just to go back to sleep -- if we're waking via interrupt, we also could have just stolen the processor context from something else.
Re: V-sync - Windows, DirectX
Posted: Sun Mar 24, 2019 10:51 pm
by TruePikachu
Well, it appears that on an otherwise-idle system, the timing is
pretty good.
Code: Select all
Point count: 4500
Average oversleep (frames): 0.0220498
Variance: 0.001085
However, when you start putting load on the system, things can shoot up rapidly; here is the output when Blender is doing a render on one thread...
Code: Select all
Point count: 4500
Average oversleep (frames): 0.0585707
Variance: 0.0376429
...and two threads -- note that my system is only dual-core.
Code: Select all
Point count: 4500
Average oversleep (frames): 1.2599
Variance: 0.138822
Here's the C++ used for the test -- note that C++11 is required, possibly even C++14.
Code: Select all
#include <array>
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;
constexpr uint8_t thread_count = 5;
constexpr size_t points_per_thread = 60 * 15;
using frames = chrono::duration<double, ratio<1, 60>>;
const auto sleep_interval = frames(1);
class welford_stats {
size_t _count = 0;
double _mean = 0;
double _m2 = 0;
mutable mutex _lock;
public:
void accumulate(double v) {
lock_guard<mutex> lock(_lock);
++_count;
auto&& delta = v - _mean;
_mean += delta / _count;
auto&& delta2 = v - _mean;
_m2 += delta * delta2;
}
struct results {
size_t n;
double mean;
double variance;
};
results finalize() const {
lock_guard<mutex> lock(_lock);
return {_count,_mean,_m2 / _count};
}
};
void thread_fn(welford_stats& results) {
chrono::steady_clock clock;
for(size_t i = 0;i < points_per_thread;++i) {
auto&& wake_time = clock.now() + sleep_interval;
this_thread::sleep_until(wake_time);
auto&& woke_time = clock.now();
auto&& overslept_time = chrono::duration_cast<frames>(move(woke_time) - move(wake_time));
results.accumulate(overslept_time.count());
}
}
int main() {
welford_stats accumulation;
vector<thread> threads;
threads.reserve(thread_count);
for(unsigned i = 0; i < thread_count;++i)
threads.emplace_back(thread_fn, ref(accumulation));
for(auto&& thread : threads)
thread.join();
auto&& results = accumulation.finalize();
cout << " Point count: " << results.n << endl;
cout << "Average oversleep (frames): " << results.mean << endl;
cout << " Variance: " << results.variance << endl;
return 0;
}
Re: V-sync - Windows, DirectX
Posted: Tue Mar 26, 2019 5:48 am
by Drithius
Having started playing again (last played before the render update, May 2018), I've been getting sporadic lag. My UPS is rock solid at 60, whereas my FPS holds steady at 60... until it decides it wants to hang out at 42 for some odd reason. I have a 144Hz G-Sync monitor but I run 85 Hz to throttle things down a bit (unless playing first person shooters).
I had about given up on this lag but apparently ENABLING V-Sync fixes it for me. No more mysterious lag with it on.
Re: V-sync - Windows, DirectX
Posted: Tue Mar 26, 2019 11:54 am
by bobucles
until it decides it wants to hang out at 42 for some odd reason. I have a 144Hz G-Sync monitor but I run 85 Hz
Well 42Hz is half of 85 so that might have something to do with it. If a VSYNC display can't update in 1 frame, it takes two.
Re: V-sync - Windows, DirectX
Posted: Tue Mar 26, 2019 1:08 pm
by TruePikachu
Additionally, there are known issues with g-sync under Factorio.
Re: V-sync - Windows, DirectX
Posted: Tue Mar 26, 2019 7:19 pm
by Jap2.0
Yeah, 60 doesn't go evenly into 82, which is probably your problem. I'm guessing you wouldn't have issues at 60 or 120.