Re-anchor D3D9 swapchain dimensions across ctor and Reset paths#141
Open
BinqAdams wants to merge 1 commit intoNVIDIAGameWorks:mainfrom
Open
Re-anchor D3D9 swapchain dimensions across ctor and Reset paths#141BinqAdams wants to merge 1 commit intoNVIDIAGameWorks:mainfrom
BinqAdams wants to merge 1 commit intoNVIDIAGameWorks:mainfrom
Conversation
D3D9SwapChainEx::m_originalWidth / m_originalHeight is captured from the first CreateDevice's BackBuffer dims and never updated, while NormalizePresentParameters computes m_widthScale = pp->BackBufferWidth / m_originalWidth on every Reset. When the host process creates the device at one resolution and later Resets to another (a common pattern for engines that initialize at a default size then apply the user-selected resolution from a settings menu), m_widthScale becomes non-1.0 (or Inf when BackBufferWidth=0 triggers D3D9 auto-derive). That scale multiplies every D3DVIEWPORT9 rect inside D3D9DeviceEx::SetViewport, producing a Vulkan viewport that overflows the backbuffer attachment - the visible result is the top-left quadrant of the rendered scene with the rest clipped, or CreateDevice failing with D3DERR_NOTAVAILABLE for the BB=0 path (NaN/Inf VkViewport rejection). Re-anchor m_originalWidth/Height in three sites: 1. D3D9SwapChainEx ctor BB=0 path. After NormalizePresentParameters fills in the auto-derived BackBuffer dims, if the init list captured 0 from the caller, re-anchor to the post-Normalize values and reset m_widthScale/m_heightScale to 1.0. 2. D3D9SwapChainEx::Reset. Re-anchor to the new BackBuffer dims before NormalizePresentParameters runs, so m_widthScale settles at 1.0 for the new size. 3. D3D9SwapchainExternal::Reset (the bridge / RemixAPI path). Mirrors the same fix; the override would otherwise leave m_originalWidth pinned to the first CreateDevice's value. The DXVK_RESOLUTION_WIDTH/HEIGHT upscale feature is preserved by gating each re-anchor on the env vars being empty - when they're set, the engine-supplied dims are intentionally overwritten by the env values to produce an upscale ratio, and re-anchoring would defeat that.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Re-anchor
m_originalWidth/m_originalHeightin three swapchain code paths som_widthScale/m_heightScalesettle correctly whenCreateDeviceand a subsequentResetuse different backbuffer dims.Motivation
D3D9SwapChainEx::m_originalWidth/m_originalHeightis captured from the firstCreateDevice'sBackBufferdims (in the ctor init list atd3d9_swapchain.cpp:327-328) and never updated, whileNormalizePresentParametersrecomputesm_widthScale = pp->BackBufferWidth / m_originalWidthon everyReset. When the host process creates the device at one resolution and laterResets to another (a common pattern for engines that initialize at a default size, then apply the user-selected resolution from a settings menu),m_widthScalebecomes non-1.0 (orInfwhenBackBufferWidth=0triggers D3D9 auto-derive).That scale multiplies every
D3DVIEWPORT9rect insideD3D9DeviceEx::SetViewport, so:640 → 1280makesm_widthScale = 2.0): the Vulkan viewport overflows the backbuffer attachment — the visible result is the top-left quadrant of the rendered scene with the rest clipped.BackBufferWidth=0ctor path:m_widthScale = NEW_BB / 0 = Inf, which propagates intoSetViewport's per-rect multiply and produces aNaN/InfVkViewportthat failsCreateDevicewithD3DERR_NOTAVAILABLE.The
D3D9SwapchainExternalsubclass overridesReset, so a fix limited toD3D9SwapChainEx::Resetdoesn't reach the bridge / RemixAPI path.What Changed
src/d3d9/d3d9_swapchain.cpp: re-anchorm_originalWidth/m_originalHeightin three sites:D3D9SwapChainExctor BB=0 path. AfterNormalizePresentParametersfills in the auto-derived backbuffer dims, if the init list captured0from the caller, re-anchor to the post-Normalize values and resetm_widthScale/m_heightScaleto1.0.D3D9SwapChainEx::Reset. Re-anchor to the new backbuffer dims beforeNormalizePresentParametersruns, som_widthScalesettles at1.0for the new size.D3D9SwapchainExternal::Reset(bridge / RemixAPI path). Mirror the same fix; the override would otherwise leavem_originalWidthpinned to the firstCreateDevice's value.The
DXVK_RESOLUTION_WIDTH/DXVK_RESOLUTION_HEIGHTupscale feature is preserved by gating each re-anchor on those env vars being empty — when they're set, the engine-supplied dims are intentionally overwritten by the env values to produce an upscale ratio, and re-anchoring would defeat that.Net diff:
src/d3d9/d3d9_swapchain.cpponly, +77.Testing
Verified behavior on three reproducers:
Resets to the user-selected resolution: pre-fix produced the top-left-quadrant clipping symptom in 2D content (menus, video, HUD); post-fix renders correctly across the new resolution.CreateDevicewithBackBufferWidth=BackBufferHeight=0(D3D9 auto-derive from HWND): pre-fix returnedD3DERR_NOTAVAILABLEfrom the firstSetViewportdue toInf/NaN; post-fix succeeds withm_widthScale = m_heightScale = 1.0.DXVK_RESOLUTION_WIDTH/DXVK_RESOLUTION_HEIGHTenv vars set: re-anchor is skipped, intentional upscale ratio is preserved.