From b1179a44fbd1cf514c2bed56bf235fbe456c8c7b Mon Sep 17 00:00:00 2001 From: Duck912 <350528965+Duck912@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:56:12 -0500 Subject: video_core: clear the DLSS output when Streamline writes nothing to it The upscaler records its work on the worker thread and reports success once it is queued. When Streamline then rejects the DLSS options, gives no frame token or fails the evaluation, nothing is written to the output image, but the caller still draws it into the game's image that frame: undefined contents after a resize, the previous frame otherwise. FSR 3.1 takes over from the next frame. Clear the output on those paths, with barriers around the clear, so that frame is black instead. Found by CodeRabbit review. Assisted-by: Claude Opus 5.5 (implementation) Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com> --- .../renderer_vulkan/vk_streamline_dlss.cpp | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src') diff --git a/src/video_core/renderer_vulkan/vk_streamline_dlss.cpp b/src/video_core/renderer_vulkan/vk_streamline_dlss.cpp index aef32735f9..4fa9e240c4 100644 --- a/src/video_core/renderer_vulkan/vk_streamline_dlss.cpp +++ b/src/video_core/renderer_vulkan/vk_streamline_dlss.cpp @@ -260,6 +260,40 @@ bool StreamlineDlssUpscaler::Upscale(const Inputs& in) { output_view_handle = *output_view](vk::CommandBuffer cmdbuf) { const Streamline::Api& api = Streamline::GetApi(); const sl::ViewportHandle viewport{0u}; + // The caller draws the output this frame even when DLSS writes nothing to it: clear it + // then, rather than show undefined contents (FSR 3.1 takes over from the next frame). + const auto clear_output = [&] { + const VkImageSubresourceRange range{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + const auto barrier = [&](VkAccessFlags src_access, VkAccessFlags dst_access) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = dst_access, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = output_image_handle, + .subresourceRange = range, + }; + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + barrier(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT)); + cmdbuf.ClearColorImage(output_image_handle, VK_IMAGE_LAYOUT_GENERAL, + VkClearColorValue{}, range); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, + barrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)); + }; if (options_changed) { const auto set = reinterpret_cast(set_options); if (const sl::Result result = set(viewport, options); result != sl::Result::eOk) { @@ -268,6 +302,7 @@ bool StreamlineDlssUpscaler::Upscale(const Inputs& in) { "Streamline rejected the DLSS options (result {}); using FSR 3.1", static_cast(result)); } + clear_output(); return; } } @@ -278,6 +313,7 @@ bool StreamlineDlssUpscaler::Upscale(const Inputs& in) { LOG_ERROR(Render_Vulkan, "Streamline gave no frame token (result {}); using FSR 3.1", static_cast(result)); } + clear_output(); return; } @@ -377,6 +413,9 @@ bool StreamlineDlssUpscaler::Upscale(const Inputs& in) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, output_written, {}, to_general); + if (result != sl::Result::eOk) { + clear_output(); + } }); // DLSS binds its own pipelines and descriptors on the command buffer. scheduler.InvalidateState(); -- cgit v1.2.3