🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Issue with DirectX12 timestamps

Started by
3 comments, last by hplus0603 4 years, 2 months ago

I've been adding timestamp queries to my DX12 renderer and noticed something weird. This can get long to explain so I'll try to mention as little as possible (I'll just add further information if it's deemed necessary).

In short, I noticed that I can usually safely compare timestamp results between different command lists as well as inside them and they return sensible values. However, should I compare a timestamp from a previous command list with any timestamp that was queried inside a command list that eventually barriers the back buffer into the _PRESENT state, then there's a sudden jump in timestamp towards the VSYNC expected time.

A bit of pseudocode may help explain:

<commandlist0>
	Draw()
	EndQuery(TIMESTAMP, 0)
	Draw()
	EndQuery(TIMESTAMP, 1)
</commandlist0>
<commandlist1>
	Draw()
	EndQuery(TIMESTAMP, 2)
</commandlist1>
<commandlist2>
	Draw()
	EndQuery(TIMESTAMP, 3)
	Draw()
	EndQuery(TIMESTAMP, 4)
	Barrier(backBuffer, PRESENT_STATE)
</commandlist2>
Present(1, 0)

Now, when running on a 60Hz monitor the results (in ms) of comparing 0 to 1, 1 to 2, 2 to 3 and 3 to 4 are something along the lines of:
0.1, 0.15, 16.6, 0.1

If I move the window to a 120Hz screen the previous becomes:
0.1, 0.15, 8.1, 0.1

However, if the Present function changes to Present(0, 0) I get something like this:
0.1, 0.15, 0.12, 0.1

Can someone shed some light into this? Thanks!

Advertisement

I imagine that you expect the stall to happen between 4 and 1, rather than between 3 and 4? E g, you'd expect the “present” synchronization to be entire contained in the Present() call?

The driver is free to order the commands in whatever order it wants, that still provides the defined output result.

The FIFO may become full, and it blocks before finishing all the commands, for example. Or the specifics of the drawing or some state that you configure may cause a stall, to resolve internal buffer data, move data around, or whatever. Even though D3D12 is “lower level” than previous versions of the API, it's not actually what the hardware is doing, and different hardware needs different translations inside the driver.

In short: When you have a fixed frame rate, the driver needs to wait SOMEWHERE, and exactly where it ends up waiting, may vary based on hardware and driver choice. When you don't use a frame rate lock, then the hardware can run as fast as it wants, and thus it doesn't need to wait in any particular spot.

Also, display compositing may affect this. Are you using exclusive or windowed mode? Are you on a desktop with a display hooked straight into the graphics card, or on a laptop where the graphics card may “present” by blitting rendered buffers to the built-in-graphics framebuffer, to then be presented to the screen?

enum Bool { True, False, FileNotFound };

@hplus0603 hi, thanks for the quick reply!

Yes, that's exactly it, I expected that, by putting the queries throughout the middle of the frame and the last one right before the actual Present call, the timing would be done between draw calls without the need to include the vsync time.

My initial idea was for a renderer that calculates how much time it actually took for the GPU to render whatever the user of the renderer did by taking the time at the start of the frame and then at the end of the frame, right before it needs to vsync, however I was always getting the largest possible refresh value which kind of defeated the point.

After further research it does seem like the “hit” in timestamp comes when bringing the backbuffer from STATE_PRESENT to RENDER_TARGET. I'm probably missing something but in theory that backbuffer should have already been freed as I'm using a MoveToNextFrame function to sync between frames…

Also: I work on windowed mode and using the DXGI_SWAP_EFFECT_FLIP_DISCARD flip mode.

in theory that backbuffer should have already been freed

That makes sense – to unblock the rendering at that point, it needs to create extra backbuffers that are “idle” between frames. The driver probably optimizes, knowing that the previous frame was presented with present interval 1.

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement