Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement D3D waitable and tearable swapchains #485

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

smoogipoo
Copy link
Contributor

@smoogipoo smoogipoo commented Mar 24, 2023

Perhaps to be seen as required for #484 since Microsoft recommend this while using the flip model.

This PR implements two things:

  • GraphicsDevice.AllowTearing, which only has an effect when VSync is disabled. In combination with the flip model, this allows frames to be pushed to the GPU as fast as possible and results in the lowest frame latency possible. It's akin to the OpenGL-esque "exclusive fullscreen" model of presentation.
  • GraphicsDevice.WaitForNextFrameReady(). This can be placed at the very start of the draw procedure causing it to wait until the GPU is ready to begin rendering the next frame. As documented here, this doesn't come with the overhead of an additional frame of latency that VSync does.

Input lag testing (PresentMon/CapFrameX):

Mode Input lag [Bound]
Default 29.3ms [24.5ms ~ 34.0ms]
AllowTearing 2ms [0.8ms - 3.0ms]
WaitForNextFrameReady() 12ms [5.4ms - 19.3ms]

One thing to note is that in this PR I've reduced the maximum frame latency to 1, which is not always advisable and is usually a user-controlled value. This value is based on our own requirements so I'm wondering how to expose it in a more general way way from Veldrid.

@smoogipoo smoogipoo changed the title Allow D3D devices to tear if requested Implement D3D waitable and tearable swapchains Mar 25, 2023
@smoogipoo
Copy link
Contributor Author

I've expanded on this PR a bit further because I wanted to implement waitable swapchains which changes the flow a bit (recreating the swapchain).

@mellinoe
Copy link
Collaborator

I'm a bit torn on this one (pun intended 😄 ) -- I think the direction is reasonable but the details might need some tweaking.

  • I think it's too D3D-specific whereas this is mostly applicable across the board. I'm also not sure "AllowTearing" is the right level of abstraction because Veldrid doesn't really make any guarantees about tearing anyways (which kind of means we can do whatever we want as its not documented). It's probably feasible to just straight up support different PresentMode options across backends, with some capability checks as I know they are not all universally available.
  • The Swapchain synchronization story is really bad right now (thus the lack of a WaitForNextFrameReady method, at least on the main branch), but there is already a Fence abstraction. The right way to expose this part is through signaling/waiting on a Fence. I'm a little skeptical that this will be useful by itself without a more complete Swapchain acquire/submission/present synchronization story (see the sample-gallery branch for where I'm thinking it should go), but I don't think it hurts by itself.
  • Exposing the "MainSwapchain" stuff was mostly a mistake, and this should be exposed through the Swapchain object itself. There's no functional difference here except it lets you manipulate this for non-main swapchains that you happen to create. Anything dealing with the "MainSwapchain" was built before Swapchains were an actual first-class object.

@smoogipoo
Copy link
Contributor Author

smoogipoo commented Mar 26, 2023

It's probably feasible to just straight up support different PresentMode options across backends

Is the suggestion to have tearing always turned on because Veldrid can do whatever it wants? I have two concerns here:

  1. I think fullscreen tearing is a very specific use-case. A few games I've tested (WoWClassic/Apex Legends/Diablo4) appear to have this turned off. Having it be turned on in general would be fine and match MS's recommendations but:
  2. It seems like if you don't use a waitable object for a period of time that the waitable object stops working. I don't know what causes this but it's why I've made the tearing flag recreate the swapchain. For us it meant that switching from fs+tearing to windowed meant that we could no longer benefit from the waitable object.

I'd need to dig deeper to see what causes (2), but if my interpretation of your suggestion is accurate then I'm willing to see if I can get anywhere.

The right way to expose this part is through signaling/waiting on a Fence

Sounds good. How would you expose the fence? Another method like CreateNextFrameFence()?

should be exposed through the Swapchain object itself

Agree.

@smoogipoo
Copy link
Contributor Author

We're going to continue working on this to our/our users' expectations, resolving issues as we go, and keep this PR updated as things are changed around to fit that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants