diff --git a/src/Features/Upscaling.cpp b/src/Features/Upscaling.cpp index b3a0190b6c..8f41bfcd91 100644 --- a/src/Features/Upscaling.cpp +++ b/src/Features/Upscaling.cpp @@ -1819,7 +1819,20 @@ void Upscaling::UpscaleDepth() // 3) Resource copies are skipped for aliased src/dst to reduce copy churn. // (1) Early validation exits - if (!IsUpscalingActive()) { + const bool depthUpscaleActive = IsUpscalingActive(); + const auto upscaleMethod = GetUpscaleMethod(); + const bool isVR = globals::game::isVR; + const bool vendorUpscaler = upscaleMethod == UpscaleMethod::kDLSS || upscaleMethod == UpscaleMethod::kFSR; + const bool fullResolutionMaskPath = + upscaleMethod == UpscaleMethod::kNONE || + upscaleMethod == UpscaleMethod::kTAA || + (vendorUpscaler && settings.qualityMode == 0); + const bool repairVRFullResolutionMask = + isVR && + fullResolutionMaskPath && + !depthUpscaleActive; + + if (!depthUpscaleActive && !repairVRFullResolutionMask) { return; } @@ -1827,7 +1840,8 @@ void Upscaling::UpscaleDepth() auto renderer = globals::game::renderer; auto context = globals::d3d::context; auto deferred = globals::deferred; - if (!state || !renderer || !context || !deferred || !deferred->linearSampler || !jitterCB || !upscaleRasterizerState || !upscaleBlendState || !upscaleDepthStencilState) { + if (!state || !renderer || !context || !deferred || !deferred->linearSampler || !jitterCB || !upscaleRasterizerState || !upscaleBlendState || + (depthUpscaleActive && !upscaleDepthStencilState)) { return; } @@ -1842,24 +1856,39 @@ void Upscaling::UpscaleDepth() auto& saoCameraZ = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGET::kSAO_CAMERAZ]; auto& underwaterMask = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGET::kUNDERWATER_MASK]; - if (!depth.texture || !depth.views[0] || !depthCopy.texture || !depthCopy.depthSRV || - !refractionNormals.texture || !refractionNormals.textureCopy || !refractionNormals.SRVCopy || !refractionNormals.RTV || !saoCameraZ.RTV || + if (!depth.texture || !depthCopy.texture || !depthCopy.depthSRV || !underwaterMask.texture || !underwaterMask.textureCopy || !underwaterMask.SRVCopy || !underwaterMask.RTV) { return; } - if (globals::game::isVR && (!depthCopy.views[0] || !depthCopy.stencilSRV)) { + if (depthUpscaleActive && + (!depth.views[0] || !refractionNormals.texture || !refractionNormals.textureCopy || !refractionNormals.SRVCopy || !refractionNormals.RTV || !saoCameraZ.RTV)) { + return; + } + // stencilSRV + views[0] are both upscale-path-only: the depth-upscale + // draw binds depthCopy as a stencil SRV input and depth.views[0] as DSV. + // The full-resolution mask repair never touches either, so don't disable + // the VR fix on setups where stencil SRV creation is unavailable. + if (depthUpscaleActive && isVR && (!depthCopy.stencilSRV || !depthCopy.views[0])) { return; } auto* fullscreenVS = GetUpscaleVS(); - auto* depthUpscalePS = GetDepthRefractionUpscalePS(); + auto* depthUpscalePS = depthUpscaleActive ? GetDepthRefractionUpscalePS() : nullptr; auto* underwaterMaskPS = GetUnderwaterMaskUpscalePS(); - if (!fullscreenVS || !depthUpscalePS || !underwaterMaskPS) { + if (!fullscreenVS || !underwaterMaskPS || (depthUpscaleActive && !depthUpscalePS)) { return; } state->BeginPerfEvent("Render Target Upscaling"); + // Unbind any prior render targets before issuing CopyResource on depth/ + // depthCopy. Upscale() does this for the standard upscale path, but + // UpscaleDepth() can now be invoked standalone from Main_PostProcessing + // (kNONE/kTAA VR path) without going through Upscale() first — match the + // same precondition here to avoid a debug-layer hazard when depth happens + // to still be bound as a DSV from a prior pass. + context->OMSetRenderTargets(0, nullptr, nullptr); + // Set up Input Assembler for fullscreen triangle (no vertex/index buffers needed) context->IASetInputLayout(nullptr); context->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr); @@ -1887,10 +1916,10 @@ void Upscaling::UpscaleDepth() context->PSSetSamplers(0, ARRAYSIZE(samplers), samplers); // Set up jitter/depth-kernel constant buffer for upscaling - JitterCB jitterData; + JitterCB jitterData{}; jitterData.jitter = jitter; // (2) Wide-kernel hysteresis - { + if (depthUpscaleActive) { constexpr float kEnterWideKernelRatio = 1.55f; constexpr float kExitWideKernelRatio = 1.45f; const float minScale = std::max(std::min(resolutionScale.x, resolutionScale.y), FLT_EPSILON); @@ -1907,7 +1936,6 @@ void Upscaling::UpscaleDepth() } jitterData.useWideKernel = depthUpscaleUseWideKernel ? 1.0f : 0.0f; - jitterData.pad0 = 0.0f; } jitterCB->Update(jitterData); @@ -1921,7 +1949,7 @@ void Upscaling::UpscaleDepth() } }; - { + if (depthUpscaleActive) { TracyD3D11Zone(globals::state->tracyCtx, "Upscaling - Depth Upscale"); // Sometimes this is not already copied e.g. map menu. @@ -1929,7 +1957,7 @@ void Upscaling::UpscaleDepth() copyIfNonAliased(depthCopy.texture, depth.texture); // Clear stencil to be 0xFF - if (globals::game::isVR) { + if (isVR) { context->ClearDepthStencilView(depthCopy.views[0], D3D11_CLEAR_STENCIL, 1.0f, 0xFF); } @@ -1944,11 +1972,16 @@ void Upscaling::UpscaleDepth() // kSAO_CAMERAZ is at quarter-stereo resolution in VR; the full-stereo viewport would // corrupt only the top-left quarter. The engine's ISSAOCameraZ pass populates it correctly. ID3D11RenderTargetView* rtvs[] = { refractionNormals.RTV, - globals::game::isVR ? nullptr : saoCameraZ.RTV }; + isVR ? nullptr : saoCameraZ.RTV }; context->OMSetRenderTargets(2, rtvs, depth.views[0]); context->PSSetShader(depthUpscalePS, nullptr, 0); context->Draw(3, 0); + } else { + TracyD3D11Zone(globals::state->tracyCtx, "Upscaling - Full Resolution Underwater Mask Depth Copy"); + + // Full-resolution paths only need to refresh the underwater mask depth source. + copyIfNonAliased(depthCopy.texture, depth.texture); } { @@ -1974,8 +2007,10 @@ void Upscaling::UpscaleDepth() context->Draw(3, 0); } - // Now propagate the upscaled depth to kMAIN_COPY so downstream VR passes see it. - if (globals::game::isVR) { + // Propagate the upscaled depth to kMAIN_COPY so downstream VR passes see + // it. Skipped on the full-resolution path because the else branch above + // already refreshed depthCopy from depth and nothing has touched it since. + if (isVR && depthUpscaleActive) { TracyD3D11Zone(globals::state->tracyCtx, "Upscaling - Depth VR Propagate"); copyIfNonAliased(depthCopy.texture, depth.texture); } @@ -2045,8 +2080,11 @@ void Upscaling::Main_PostProcessing::thunk(RE::ImageSpaceManager* a_this, uint32 if (upscaling.ShouldUseFrameGenerationThisFrame()) upscaling.CopySharedD3D12Resources(); - if (upscaleMethod != UpscaleMethod::kNONE && upscaleMethod != UpscaleMethod::kTAA) + if (upscaleMethod != UpscaleMethod::kNONE && upscaleMethod != UpscaleMethod::kTAA) { upscaling.PerformUpscaling(); + } else if (globals::game::isVR) { + upscaling.UpscaleDepth(); + } if (upscaleMethod == UpscaleMethod::kDLSS) upscaling.ApplySharpening();