V-sync - Windows, DirectX

Post all other topics which do not belong to any other category.
posila
Factorio Staff
Factorio Staff
Posts: 5349
Joined: Thu Jun 11, 2015 1:35 pm
Contact:

V-sync - Windows, DirectX

Post 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
  1. 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).
  2. 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.
nuhll
Filter Inserter
Filter Inserter
Posts: 946
Joined: Mon Apr 04, 2016 9:48 pm
Contact:

Re: V-sync - Windows, DirectX

Post by nuhll »

So, what should we do? Enable it, disable it? or does it not matter?
posila
Factorio Staff
Factorio Staff
Posts: 5349
Joined: Thu Jun 11, 2015 1:35 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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.
nuhll
Filter Inserter
Filter Inserter
Posts: 946
Joined: Mon Apr 04, 2016 9:48 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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?
posila
Factorio Staff
Factorio Staff
Posts: 5349
Joined: Thu Jun 11, 2015 1:35 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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.
User avatar
TruePikachu
Filter Inserter
Filter Inserter
Posts: 978
Joined: Sat Apr 09, 2016 8:39 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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)...
orzelek
Smart Inserter
Smart Inserter
Posts: 3923
Joined: Fri Apr 03, 2015 10:20 am
Contact:

Re: V-sync - Windows, DirectX

Post 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.
Bilka
Factorio Staff
Factorio Staff
Posts: 3309
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: V-sync - Windows, DirectX

Post 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.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.
User avatar
TruePikachu
Filter Inserter
Filter Inserter
Posts: 978
Joined: Sat Apr 09, 2016 8:39 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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.
User avatar
BlueTemplar
Smart Inserter
Smart Inserter
Posts: 3091
Joined: Fri Jun 08, 2018 2:16 pm
Contact:

Re: V-sync - Windows, DirectX

Post by BlueTemplar »

Have you tried 120 Hz ?
BobDiggity (mod-scenario-pack)
User avatar
TruePikachu
Filter Inserter
Filter Inserter
Posts: 978
Joined: Sat Apr 09, 2016 8:39 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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.)
Jap2.0
Smart Inserter
Smart Inserter
Posts: 2375
Joined: Tue Jun 20, 2017 12:02 am
Contact:

Re: V-sync - Windows, DirectX

Post 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.
There are 10 types of people: those who get this joke and those who don't.
User avatar
TruePikachu
Filter Inserter
Filter Inserter
Posts: 978
Joined: Sat Apr 09, 2016 8:39 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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.
User avatar
TruePikachu
Filter Inserter
Filter Inserter
Posts: 978
Joined: Sat Apr 09, 2016 8:39 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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;
}
Drithius
Manual Inserter
Manual Inserter
Posts: 3
Joined: Thu May 03, 2018 3:06 am
Contact:

Re: V-sync - Windows, DirectX

Post 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.
bobucles
Smart Inserter
Smart Inserter
Posts: 1708
Joined: Wed Jun 10, 2015 10:37 pm
Contact:

Re: V-sync - Windows, DirectX

Post 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.
User avatar
TruePikachu
Filter Inserter
Filter Inserter
Posts: 978
Joined: Sat Apr 09, 2016 8:39 pm
Contact:

Re: V-sync - Windows, DirectX

Post by TruePikachu »

Additionally, there are known issues with g-sync under Factorio.
Jap2.0
Smart Inserter
Smart Inserter
Posts: 2375
Joined: Tue Jun 20, 2017 12:02 am
Contact:

Re: V-sync - Windows, DirectX

Post 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.
There are 10 types of people: those who get this joke and those who don't.
Post Reply

Return to “General discussion”