aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuck912 <350528965+Duck912@users.noreply.github.com>2026-11-03 12:56:12 -0500
committerDuck912 <350528965+Duck912@users.noreply.github.com>2026-11-03 13:06:59 -0500
commitb1179a44fbd1cf514c2bed56bf235fbe456c8c7b (patch)
tree8239786a24433501a6472565c149701a2a1d4454
parent98cc44dadd7ab4b63cf0685360ac50738e8e5c3e (diff)
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>
-rw-r--r--src/video_core/renderer_vulkan/vk_streamline_dlss.cpp39
1 files changed, 39 insertions, 0 deletions
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<PFun_slDLSSSetOptions*>(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<int>(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<int>(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();