app,internal/d3d11: present the D3D11 swapchain with the flip model - #174
app,internal/d3d11: present the D3D11 swapchain with the flip model#174dkrisman wants to merge 1 commit into
Conversation
eliasnaur
left a comment
There was a problem hiding this comment.
Looks good, a few comments below.
Please note in the commit description that this change breaks compatibility with Windows versions < 10. I think that's ok given that recent Go versions also require Windows 10.
| BufferCount: 1, | ||
| BufferCount: 2, |
There was a problem hiding this comment.
Are you sure changing BufferCount to 2 won't fix DXGI_SWAP_EFFECT_DISCARD? The documentation says
DXGI_SWAP_EFFECT_DISCARD ... This flag is valid for a swap chain with more than one back buffer
If so, please submit this change as a separate bugfix commit, with the switch to DXGI_SWAP_EFFECT_FLIP_DISCARD on top.
There was a problem hiding this comment.
I tested this on the affected hardware (Arc 140V): DXGI_SWAP_EFFECT_DISCARD with BufferCount 2 still presents nothing - the window stays blank exactly as with BufferCount 1, while FLIP_DISCARD renders correctly in the same harness (minimal solid-fill window, automated pixel capture, repeated runs). Buffer count alone is not a fix on this hardware, so the change stays a single commit, with the negative result noted in the commit message.
There was a problem hiding this comment.
Alright. Perhaps this is how you tested it, but: does rendering work on your setup if you keep everything else (the sRGB changes etc.) and only keep DXGI_SWAP_EFFECT_DISCARD?
| // CreateRenderTargetViewDesc is CreateRenderTargetView with an explicit | ||
| // view description, which is required to view a backbuffer through a | ||
| // different (for example sRGB) format than it was created with. | ||
| func (d *Device) CreateRenderTargetViewDesc(res *Resource, desc *RENDER_TARGET_VIEW_DESC) (*RenderTargetView, error) { |
There was a problem hiding this comment.
Don't introduce a new function. Just change the existing CreateRenderTargetView to accept a descriptor. Pass the descriptor by value, not pointer.
There was a problem hiding this comment.
CreateRenderTargetViewDesc is gone; CreateRenderTargetView takes RENDER_TARGET_VIEW_DESC by value. The texture render-target path passes its texture format explicitly, which resolves to the same view as the previous NULL descriptor (resource format, mip 0). gio's test suite passes, including gpu/headless and gpu/internal/rendertest on the D3D11 backend.
| OutputWindow: hwnd, | ||
| Windowed: 1, | ||
| SwapEffect: DXGI_SWAP_EFFECT_DISCARD, | ||
| SwapEffect: DXGI_SWAP_EFFECT_FLIP_DISCARD, |
There was a problem hiding this comment.
I vaguely remember DXGI_SWAP_EFFECT_FLIP_DISCARD not working great with window resizes. Do you see any artifacts during resizing, or is the content perfectly synchronized (that is, not stretching, padding etc.)?
There was a problem hiding this comment.
No stretching in automated probes on the Arc 140V (60 programmatic size changes plus an interactive corner drag through the modal resize loop, about 100 rect-verified captures - content covered the client area in every one). Resizing by hand, though, a transient white band (a frame or two) can appear along the shrinking edge - bottom when shrinking vertically, right when shrinking horizontally - but only within the first ~5 seconds of resizing after application start; after that it never reproduces. IDXGIDevice1::SetMaximumFrameLatency(1) makes no difference, so it does not look like present-queue depth. I cannot compare against DISCARD on this hardware because the blt path presents nothing at all there. I can dig into the startup-window artifact here or as a follow-up, whichever you prefer.
There was a problem hiding this comment.
Obviously, no rendering output is worse than flickering during resize. However, resize flicker is such a jarring artifact that I want to push back against switching to DXGI_SWAP_EFFECT_FLIP_DISCARD without adressing resizing.
I see two ways:
- Switching to DXGI_SWAP_EFFECT_FLIP_DISCARD and ensuring synchronization with window resize repainting. This is the better way for performance, future compatibility, but probably also the most intrusive. Gemini suggests some combination of
DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT, SetMaximumFrameLatency(1) and a ID3D11Query (inserted after Present and then waited on). - Fixing DXGI_SWAP_EFFECT_DISCARD on your setup. It surprises me that your graphics driver completely breaks DXGI_SWAP_EFFECT_DISCARD, because surely there must be a bunch of legacy programs that uses DXGI_SWAP_EFFECT_DISCARD that would also break. For example, does a minimal D3D11 example also break on your setup? If not, perhaps a fix can be bisected.
The swapchain was created with DXGI_SWAP_EFFECT_DISCARD, the legacy blt presentation model. On an Intel Arc 140V (Lunar Lake) a blt-model present puts nothing on screen: the window stays blank while the renderer draws every frame. Raising BufferCount alone does not help - DISCARD with BufferCount 2 still presents nothing on that hardware - but switching to the flip model does. Use DXGI_SWAP_EFFECT_FLIP_DISCARD, the presentation model Microsoft recommends for new applications. Flip imposes two constraints: the backbuffer format may not be sRGB, and BufferCount must be at least 2. The format constraint would drop the automatic linear-to-sRGB conversion on write, so the backbuffer is now created as DXGI_FORMAT_R8G8B8A8_UNORM and viewed through an explicit DXGI_FORMAT_R8G8B8A8_UNORM_SRGB render target view, preserving the previous gamma behavior exactly. Creating that view needs a D3D11_RENDER_TARGET_VIEW_DESC, so CreateRenderTargetView now takes a RENDER_TARGET_VIEW_DESC by value and the texture render target path supplies its texture format explicitly, which resolves to the same view as before. DXGI_SWAP_EFFECT_FLIP_DISCARD requires Windows 10, so this drops support for Windows 8.x and earlier, consistent with recent Go releases, which also require Windows 10. Signed-off-by: Devon Krisman <gio@krisman.dev>
bfb1530 to
98a6336
Compare
|
The commit message now notes the Windows 10 requirement: FLIP_DISCARD is Windows 10+, so this drops Windows 8.x, in line with recent Go releases. The update also reworks the RTV binding and records the BufferCount test result, per the comments below. |
|
https://raphlinus.github.io/rust/gui/2019/06/21/smooth-resize-test.html talks about the problem more. It seems the problem is thorny on Windows (we had a similar resizing issue on macOS until someone figured out the magic recipe for synchronization). |
The D3D11 backend still presents with the legacy blt model
(DXGI_SWAP_EFFECT_DISCARD, BufferCount 1). On recent drivers this can render
nothing at all: on 2025+ Intel Arc (Lunar Lake 140V) the window stays blank
while frames are drawn. The flip model has been Microsoft's recommended
presentation path since Windows 8.1, and DXGI_SWAP_EFFECT_FLIP_DISCARD
(Windows 10+) is the variant DXGI documentation steers new code toward.
Changes:
CreateSwapChainuses DXGI_SWAP_EFFECT_FLIP_DISCARD with BufferCount 2(the flip model requires at least two buffers).
DXGI_FORMAT_R8G8B8A8_UNORM and the render target view is created over it
with DXGI_FORMAT_R8G8B8A8_UNORM_SRGB via a new
CreateRenderTargetViewDescbinding (flattened RENDER_TARGET_VIEW_DESC).Linear-to-sRGB conversion on write is preserved, so gamma behavior is
unchanged.
which preserves the non-sRGB buffer format.
Compatibility: DXGI_SWAP_EFFECT_FLIP_DISCARD requires Windows 10. If Gio
still targets Windows 8.x with D3D11, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
(Windows 8+) is a drop-in alternative; happy to switch or add a fallback if
that floor matters.
Verified on Windows 10 (LTSC 2021, Intel and NVIDIA adapters) and on the
Intel Arc / Lunar Lake 140V machine that motivated the change: the
previously blank window renders correctly, resize/minimize/restore behave,
and colors are unchanged against the blt-model build on hardware where both
work.