From 99e95eebb14fa987906b6f47c65c4ccd4bb596bc Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Jul 2026 02:18:00 -0400 Subject: [PATCH 01/23] [vulkan] Initial dynamic rendering implementation --- src/video_core/renderer_vulkan/blit_image.cpp | 124 +++++++++++++----- src/video_core/renderer_vulkan/blit_image.h | 28 ++-- .../renderer_vulkan/vk_graphics_pipeline.cpp | 45 ++++++- .../renderer_vulkan/vk_rasterizer.cpp | 1 + .../renderer_vulkan/vk_scheduler.cpp | 63 ++++++++- src/video_core/renderer_vulkan/vk_scheduler.h | 4 +- .../renderer_vulkan/vk_texture_cache.cpp | 70 +++++++++- .../renderer_vulkan/vk_texture_cache.h | 32 +++++ .../vulkan_common/vulkan_device.cpp | 5 + src/video_core/vulkan_common/vulkan_device.h | 6 + .../vulkan_common/vulkan_wrapper.cpp | 8 ++ src/video_core/vulkan_common/vulkan_wrapper.h | 10 ++ 12 files changed, 339 insertions(+), 57 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 89083d29fb..079dee53a5 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -545,6 +545,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) { const VkRenderPass render_pass = framebuffer->RenderPass(); + if (!render_pass) { + framebuffer->BeginRendering(cmdbuf); + return; + } const VkFramebuffer framebuffer_handle = framebuffer->Handle(); const VkExtent2D render_area = framebuffer->RenderArea(); const VkRenderPassBeginInfo renderpass_bi{ @@ -561,6 +565,31 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) }; cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); } + +void EndRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) { + if (framebuffer->RenderPass()) { + cmdbuf.EndRenderPass(); + } else { + cmdbuf.EndRendering(); + } +} + +[[nodiscard]] VkPipelineRenderingCreateInfo MakePipelineRenderingCreateInfo( + const Framebuffer* framebuffer) { + return VkPipelineRenderingCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .pNext = nullptr, + .viewMask = 0, + .colorAttachmentCount = framebuffer->NumColorAttachments(), + .pColorAttachmentFormats = framebuffer->ColorAttachmentFormats().data(), + .depthAttachmentFormat = framebuffer->HasAspectDepthBit() + ? framebuffer->DepthAttachmentFormat() + : VK_FORMAT_UNDEFINED, + .stencilAttachmentFormat = framebuffer->HasAspectStencilBit() + ? framebuffer->DepthAttachmentFormat() + : VK_FORMAT_UNDEFINED, + }; +} } // Anonymous namespace BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, @@ -623,10 +652,12 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV const BlitImagePipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .operation = operation, + .color_formats = dst_framebuffer->ColorAttachmentFormats(), + .depth_format = dst_framebuffer->DepthAttachmentFormat(), }; const VkPipelineLayout layout = *one_texture_pipeline_layout; const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); + const VkPipeline pipeline = FindOrEmplaceColorPipeline(key, dst_framebuffer); const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D); RecordShaderReadBarrier(scheduler, src_image_view); @@ -651,9 +682,11 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView const BlitImagePipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy, + .color_formats = dst_framebuffer->ColorAttachmentFormats(), + .depth_format = dst_framebuffer->DepthAttachmentFormat(), }; const VkPipelineLayout layout = *one_texture_pipeline_layout; - const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); + const VkPipeline pipeline = FindOrEmplaceColorPipeline(key, dst_framebuffer); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region, src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) { @@ -666,7 +699,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView nullptr); BindBlitState(cmdbuf, layout, dst_region, src_region, src_size); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); + EndRenderPass(cmdbuf, dst_framebuffer); }); } @@ -747,10 +780,12 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, const BlitImagePipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .operation = operation, + .color_formats = dst_framebuffer->ColorAttachmentFormats(), + .depth_format = dst_framebuffer->DepthAttachmentFormat(), }; const VkPipelineLayout layout = *two_textures_pipeline_layout; const VkSampler sampler = *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key); + const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key, dst_framebuffer); const VkImageView src_depth_view = src_image_view.DepthView(); const VkImageView src_stencil_view = src_image_view.StencilView(); @@ -772,25 +807,25 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass()); + ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer); Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass()); + ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer); Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass()); + ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer); Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass()); + ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer); Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view); } @@ -801,35 +836,35 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, LOG_WARNING(Render_Vulkan, "ConvertABGR8ToD24S8 requires shader_stencil_export, skipping"); return; } - ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(), + ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer, convert_abgr8_to_d24s8_frag); Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(), + ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer, convert_abgr8_to_d32f_frag); Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { - ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(), + ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer, convert_d32f_to_abgr8_frag); ConvertDepthStencil(*convert_d32f_to_abgr8_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { - ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(), + ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer, convert_d24s8_to_abgr8_frag); ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { - ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(), + ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer, convert_s8d24_to_abgr8_frag); ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); } @@ -840,8 +875,10 @@ void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_ma const BlitImagePipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult, + .color_formats = dst_framebuffer->ColorAttachmentFormats(), + .depth_format = dst_framebuffer->DepthAttachmentFormat(), }; - const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key); + const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key, dst_framebuffer); const VkPipelineLayout layout = *clear_color_pipeline_layout; scheduler.RequestRenderpass(dst_framebuffer); scheduler.Record( @@ -867,8 +904,10 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool .stencil_mask = stencil_mask, .stencil_compare_mask = stencil_compare_mask, .stencil_ref = stencil_ref, + .color_formats = dst_framebuffer->ColorAttachmentFormats(), + .depth_format = dst_framebuffer->DepthAttachmentFormat(), }; - const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key); + const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key, dst_framebuffer); const VkPipelineLayout layout = *clear_color_pipeline_layout; scheduler.RequestRenderpass(dst_framebuffer); scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) { @@ -1140,12 +1179,14 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer scheduler.InvalidateState(); } -VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) { +VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key, + const Framebuffer* framebuffer) { const auto it = std::ranges::find(blit_color_keys, key); if (it != blit_color_keys.end()) { return *blit_color_pipelines[std::distance(blit_color_keys.begin(), it)]; } blit_color_keys.push_back(key); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*full_screen_vert, *blit_color_to_color_frag); const VkPipelineColorBlendAttachmentState blend_attachment{ @@ -1173,7 +1214,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = key.renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1195,17 +1236,19 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe return *blit_color_pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key) { +VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key, + const Framebuffer* framebuffer) { const auto it = std::ranges::find(blit_depth_stencil_keys, key); if (it != blit_depth_stencil_keys.end()) { return *blit_depth_stencil_pipelines[std::distance(blit_depth_stencil_keys.begin(), it)]; } blit_depth_stencil_keys.push_back(key); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = key.renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1227,12 +1270,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip return *blit_depth_stencil_pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) { +VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key, + const Framebuffer* framebuffer) { const auto it = std::ranges::find(clear_color_keys, key); if (it != clear_color_keys.end()) { return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)]; } clear_color_keys.push_back(key); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag); const VkPipelineColorBlendAttachmentState color_blend_attachment_state{ .blendEnable = VK_TRUE, @@ -1258,7 +1303,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = key.renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1281,12 +1326,13 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel } VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( - const BlitDepthStencilPipelineKey& key) { + const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer) { const auto it = std::ranges::find(clear_stencil_keys, key); if (it != clear_stencil_keys.end()) { return *clear_stencil_pipelines[std::distance(clear_stencil_keys.begin(), it)]; } clear_stencil_keys.push_back(key); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag); const auto stencil = VkStencilOpState{ .failOp = VK_STENCIL_OP_KEEP, @@ -1314,7 +1360,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = key.renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1462,25 +1508,29 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline return *msaa_copy_pipelines.back(); } -void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { - ConvertPipeline(pipeline, renderpass, false); +void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, + const Framebuffer* framebuffer) { + ConvertPipeline(pipeline, framebuffer, false); } -void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { - ConvertPipeline(pipeline, renderpass, true); +void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, + const Framebuffer* framebuffer) { + ConvertPipeline(pipeline, framebuffer, true); } -void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, +void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer, vk::ShaderModule& module, bool single_texture, bool is_target_depth) { if (pipeline) { return; } + const VkRenderPass renderpass = framebuffer->RenderPass(); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*full_screen_vert, *module); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1502,28 +1552,32 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren }); } -void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, +void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, + const Framebuffer* framebuffer, vk::ShaderModule& module) { - ConvertPipelineEx(pipeline, renderpass, module, false, false); + ConvertPipelineEx(pipeline, framebuffer, module, false, false); } -void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, +void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, + const Framebuffer* framebuffer, vk::ShaderModule& module) { - ConvertPipelineEx(pipeline, renderpass, module, true, true); + ConvertPipelineEx(pipeline, framebuffer, module, true, true); } -void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, +void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer, bool is_target_depth) { if (pipeline) { return; } + const VkRenderPass renderpass = framebuffer->RenderPass(); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); VkShaderModule frag_shader = is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag; const std::array stages = MakeStages(*full_screen_vert, frag_shader); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 67294d594b..ea8f37a48f 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -33,6 +33,8 @@ struct BlitImagePipelineKey { VkRenderPass renderpass; Tegra::Engines::Fermi2D::Operation operation; + std::array color_formats; + VkFormat depth_format; }; struct BlitDepthStencilPipelineKey { @@ -43,6 +45,8 @@ struct BlitDepthStencilPipelineKey { u8 stencil_mask; u32 stencil_compare_mask; u32 stencil_ref; + std::array color_formats; + VkFormat depth_format; }; struct MSAACopyPipelineKey { @@ -123,31 +127,35 @@ private: void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, ImageView& src_image_view); - [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key); + [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key, + const Framebuffer* framebuffer); - [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); + [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key, + const Framebuffer* framebuffer); - [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key); + [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key, + const Framebuffer* framebuffer); [[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline( - const BlitDepthStencilPipelineKey& key); + const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer); [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil); - void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); + void ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer, + bool is_target_depth); - void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); + void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer); - void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); + void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer); - void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + void ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer, vk::ShaderModule& module, bool single_texture, bool is_target_depth); - void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer, vk::ShaderModule& module); - void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer, vk::ShaderModule& module); const Device& device; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 757e0f3ccf..9815157b97 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -290,7 +290,10 @@ GraphicsPipeline::GraphicsPipeline( descriptor_update_template = builder.CreateTemplate(set_layout, *pipeline_layout, uses_push_descriptor); - const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state, device))}; + VkRenderPass render_pass{}; + if (!device.IsKhrDynamicRenderingSupported()) { + render_pass = render_pass_cache.Get(MakeRenderPassKey(key.state, device)); + } Validate(); try { MakePipeline(render_pass); @@ -996,9 +999,47 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } + const RenderPassKey renderpass_key{MakeRenderPassKey(key.state, device)}; + std::array color_attachment_formats{}; + for (size_t index = 0; index < renderpass_key.color_formats.size(); ++index) { + const PixelFormat pixel_format{renderpass_key.color_formats[index]}; + if (pixel_format == PixelFormat::Invalid) { + color_attachment_formats[index] = VK_FORMAT_UNDEFINED; + continue; + } + color_attachment_formats[index] = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, pixel_format).format; + } + VkFormat depth_attachment_format{VK_FORMAT_UNDEFINED}; + VkFormat stencil_attachment_format{VK_FORMAT_UNDEFINED}; + if (renderpass_key.depth_format != PixelFormat::Invalid) { + const VkFormat format{ + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, + renderpass_key.depth_format) + .format}; + const auto surface_type{VideoCore::Surface::GetFormatType(renderpass_key.depth_format)}; + if (surface_type == VideoCore::Surface::SurfaceType::Depth || + surface_type == VideoCore::Surface::SurfaceType::DepthStencil) { + depth_attachment_format = format; + } + if (surface_type == VideoCore::Surface::SurfaceType::Stencil || + surface_type == VideoCore::Surface::SurfaceType::DepthStencil) { + stencil_attachment_format = format; + } + } + const VkPipelineRenderingCreateInfo rendering_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .pNext = nullptr, + .viewMask = 0, + .colorAttachmentCount = static_cast(NumAttachments(key.state)), + .pColorAttachmentFormats = color_attachment_formats.data(), + .depthAttachmentFormat = depth_attachment_format, + .stencilAttachmentFormat = stencil_attachment_format, + }; + pipeline = device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = device.IsKhrDynamicRenderingSupported() ? &rendering_ci : nullptr, .flags = flags, .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 720f3b868c..a937f8e0c1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -423,6 +423,7 @@ void RasterizerVulkan::Clear(u32 layer_count) { const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor && regs.clear_surface.layer == 0 && !scheduler.IsRenderPassActive() && + !device.IsKhrDynamicRenderingSupported() && (!use_color || color_full_channels) && ds_deferrable; if (!can_defer_clear) { scheduler.RequestRenderpass(framebuffer); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 6234b41978..3c70e5c786 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -95,11 +95,40 @@ void Scheduler::DispatchWork() { void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass, const VkClearValue* clear_values, u32 clear_value_count) { - const VkFramebuffer framebuffer_handle = framebuffer->Handle(); const VkExtent2D render_area = framebuffer->RenderArea(); + if (device.IsKhrDynamicRenderingSupported()) { + std::array attachment_views{}; + const auto& color_views = framebuffer->ColorAttachments(); + for (size_t index = 0; index < color_views.size(); ++index) { + attachment_views[index] = color_views[index]; + } + attachment_views[8] = framebuffer->DepthAttachment(); + state.renderpass = VkRenderPass{}; + state.framebuffer = VkFramebuffer{}; + state.attachment_views = attachment_views; + state.render_area = render_area; + state.rendering = true; + + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { + const std::string render_pass_info = + fmt::format("renderArea={}x{}, numImages={}", render_area.width, render_area.height, + framebuffer->NumImages()); + GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); + } + + Record([framebuffer](vk::CommandBuffer cmdbuf) { + framebuffer->BeginRendering(cmdbuf); + }); + num_renderpass_images = framebuffer->NumImages(); + renderpass_images = framebuffer->Images(); + renderpass_image_ranges = framebuffer->ImageRanges(); + return; + } + const VkFramebuffer framebuffer_handle = framebuffer->Handle(); state.renderpass = renderpass; state.framebuffer = framebuffer_handle; state.render_area = render_area; + state.rendering = true; if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string render_pass_info = @@ -195,9 +224,25 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { RealizeDeferredClear(); return; } + const VkExtent2D render_area = framebuffer->RenderArea(); + if (device.IsKhrDynamicRenderingSupported()) { + std::array attachment_views{}; + const auto& color_views = framebuffer->ColorAttachments(); + for (size_t index = 0; index < color_views.size(); ++index) { + attachment_views[index] = color_views[index]; + } + attachment_views[8] = framebuffer->DepthAttachment(); + if (state.rendering && attachment_views == state.attachment_views && + render_area.width == state.render_area.width && + render_area.height == state.render_area.height) { + return; + } + EndRenderPass(); + BeginRenderPassImpl(framebuffer, VkRenderPass{}, nullptr, 0); + return; + } const VkRenderPass renderpass = framebuffer->RenderPass(); const VkFramebuffer framebuffer_handle = framebuffer->Handle(); - const VkExtent2D render_area = framebuffer->RenderArea(); if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer && render_area.width == state.render_area.width && render_area.height == state.render_area.height) { @@ -380,7 +425,7 @@ void Scheduler::EndPendingOperations() { void Scheduler::EndRenderPass() { RealizeDeferredClear(); - if (!state.renderpass) { + if (!state.rendering) { return; } @@ -398,7 +443,8 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, - has_transform_feedback = device.IsExtTransformFeedbackSupported()]( + has_transform_feedback = device.IsExtTransformFeedbackSupported(), + dynamic_rendering = device.IsKhrDynamicRenderingSupported()]( vk::CommandBuffer cmdbuf) { std::array barriers; for (size_t i = 0; i < num_images; ++i) { @@ -435,7 +481,11 @@ void Scheduler::EndRenderPass() .subresourceRange = range, }; } - cmdbuf.EndRenderPass(); + if (dynamic_rendering) { + cmdbuf.EndRendering(); + } else { + cmdbuf.EndRenderPass(); + } cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); @@ -453,6 +503,9 @@ void Scheduler::EndRenderPass() }); state.renderpass = VkRenderPass{}; + state.framebuffer = VkFramebuffer{}; + state.attachment_views = {}; + state.rendering = false; num_renderpass_images = 0; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 5c4af8c146..7f44fb22ec 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -71,7 +71,7 @@ public: /// Returns true when a render pass is currently active in the scheduler state. bool IsRenderPassActive() const { - return state.renderpass != VK_NULL_HANDLE; + return state.rendering; } /// Update the pipeline to the current execution context. @@ -250,8 +250,10 @@ private: struct State { VkRenderPass renderpass{}; VkFramebuffer framebuffer{}; + std::array attachment_views{}; VkExtent2D render_area = {0, 0}; GraphicsPipeline* graphics_pipeline = nullptr; + bool rendering = false; bool is_rescaling = false; bool rescaling_defined = false; bool needs_state_enable_refresh = false; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 44a4227a19..5e6b702e3b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1353,7 +1353,7 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst } void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) { - if (!dst->RenderPass()) { + if (!dst->RenderPass() && !device.IsKhrDynamicRenderingSupported()) { return; } @@ -2683,7 +2683,7 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::spanSamples(); + color_attachments[index] = color_buffer->RenderTarget(); + color_attachment_formats[index] = + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, + color_buffer->format) + .format; + num_color_attachments = static_cast(index + 1); ++num_images; } const size_t num_colors = attachments.size(); @@ -2741,6 +2747,11 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer); image_ranges[num_images] = subresource_range; samples = depth_buffer->Samples(); + depth_attachment = depth_buffer->RenderTarget(); + depth_attachment_format = + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, + depth_buffer->format) + .format; ++num_images; has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; @@ -2755,7 +2766,6 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, discard_msaa_color = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; - renderpass = runtime.render_pass_cache.Get(renderpass_key); render_pass_key = renderpass_key; render_pass_cache = &runtime.render_pass_cache; render_area.width = (std::min)(render_area.width, width); @@ -2819,6 +2829,11 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } num_color_buffers = static_cast(num_colors); + layer_count = static_cast((std::max)(num_layers, 1)); + if (runtime.device.IsKhrDynamicRenderingSupported()) { + return; + } + renderpass = runtime.render_pass_cache.Get(renderpass_key); framebuffer = runtime.device.GetLogical().CreateFramebuffer({ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .pNext = nullptr, @@ -2828,7 +2843,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, .pAttachments = attachments.data(), .width = render_area.width, .height = render_area.height, - .layers = static_cast((std::max)(num_layers, 1)), + .layers = layer_count, }); } @@ -2844,6 +2859,53 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste return render_pass_cache->Get(key); } +void Framebuffer::BeginRendering(vk::CommandBuffer cmdbuf) const { + std::array color_attachment_infos{}; + for (size_t index = 0; index < num_color_attachments; ++index) { + color_attachment_infos[index] = VkRenderingAttachmentInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = color_attachments[index], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; + } + const VkRenderingAttachmentInfo depth_attachment_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = depth_attachment, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; + const VkRenderingInfo rendering_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = 0, + .renderArea = + { + .offset = {.x = 0, .y = 0}, + .extent = render_area, + }, + .layerCount = layer_count, + .viewMask = 0, + .colorAttachmentCount = num_color_attachments, + .pColorAttachments = color_attachment_infos.data(), + .pDepthAttachment = has_depth ? &depth_attachment_info : nullptr, + .pStencilAttachment = has_stencil ? &depth_attachment_info : nullptr, + }; + cmdbuf.BeginRendering(rendering_info); +} + void TextureCacheRuntime::AccelerateImageUpload( Image& image, const StagingBufferRef& map, std::span swizzles, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 99f2978cca..003d7995cc 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -178,6 +178,8 @@ public: std::span color_buffers, ImageView* depth_buffer, bool is_rescaled = false); + void BeginRendering(vk::CommandBuffer cmdbuf) const; + [[nodiscard]] VkFramebuffer Handle() const noexcept { return *framebuffer; } @@ -193,6 +195,30 @@ public: [[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, u32 color_discard_mask) const; + [[nodiscard]] u32 NumColorAttachments() const noexcept { + return num_color_attachments; + } + + [[nodiscard]] const std::array& ColorAttachments() const noexcept { + return color_attachments; + } + + [[nodiscard]] const std::array& ColorAttachmentFormats() const noexcept { + return color_attachment_formats; + } + + [[nodiscard]] VkImageView DepthAttachment() const noexcept { + return depth_attachment; + } + + [[nodiscard]] VkFormat DepthAttachmentFormat() const noexcept { + return depth_attachment_format; + } + + [[nodiscard]] u32 NumLayers() const noexcept { + return layer_count; + } + [[nodiscard]] VkExtent2D RenderArea() const noexcept { return render_area; } @@ -255,6 +281,12 @@ private: std::array images{}; std::array image_ranges{}; std::array rt_map{}; + std::array color_attachments{}; + std::array color_attachment_formats{}; + VkImageView depth_attachment{}; + VkFormat depth_attachment_format = VK_FORMAT_UNDEFINED; + u32 num_color_attachments = 0; + u32 layer_count = 1; bool has_depth{}; bool has_stencil{}; bool is_rescaled{}; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8fd9b6e69f..52e99a0842 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1373,6 +1373,11 @@ void Device::RemoveUnsuitableExtensions() { extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME); + // VK_KHR_dynamic_rendering + extensions.dynamic_rendering = features.dynamic_rendering.dynamicRendering; + RemoveExtensionFeatureIfUnsuitable(extensions.dynamic_rendering, features.dynamic_rendering, + VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + // VK_KHR_maintenance4 extensions.maintenance4 = features.maintenance4.maintenance4; RemoveExtensionFeatureIfUnsuitable(extensions.maintenance4, features.maintenance4, diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index c449f60324..10268c3e25 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -43,6 +43,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ shader_demote_to_helper_invocation) \ FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \ + FEATURE(KHR, DynamicRendering, DYNAMIC_RENDERING, dynamic_rendering) \ FEATURE(KHR, Maintenance4, MAINTENANCE_4, maintenance4) \ FEATURE(KHR, Synchronization2, SYNCHRONIZATION_2, synchronization2) @@ -941,6 +942,11 @@ FN_MAX_LIMIT_LIST return extensions.maintenance3; } + /// Returns true if the device supports VK_KHR_dynamic_rendering. + bool IsKhrDynamicRenderingSupported() const { + return extensions.dynamic_rendering; + } + /// Returns true if the device supports VK_KHR_maintenance4. bool IsKhrMaintenance4Supported() const { return extensions.maintenance4; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 4943705859..91344227c5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -92,6 +92,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdBeginConditionalRenderingEXT); X(vkCmdBeginQuery); X(vkCmdBeginRenderPass); + X(vkCmdBeginRendering); X(vkCmdBeginTransformFeedbackEXT); X(vkCmdBeginDebugUtilsLabelEXT); X(vkCmdBindDescriptorSets); @@ -119,6 +120,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdEndConditionalRenderingEXT); X(vkCmdEndQuery); X(vkCmdEndRenderPass); + X(vkCmdEndRendering); X(vkCmdEndTransformFeedbackEXT); X(vkCmdEndDebugUtilsLabelEXT); X(vkCmdFillBuffer); @@ -257,6 +259,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device); } + // Support for dynamic rendering is optional until Vulkan 1.3 + if (!dld.vkCmdBeginRendering) { + Proc(dld.vkCmdBeginRendering, dld, "vkCmdBeginRenderingKHR", device); + Proc(dld.vkCmdEndRendering, dld, "vkCmdEndRenderingKHR", device); + } + // Synchronization2 is core in Vulkan 1.3, otherwise requires VK_KHR_synchronization2 if (!dld.vkCmdPipelineBarrier2) { Proc(dld.vkCmdPipelineBarrier2, dld, "vkCmdPipelineBarrier2KHR", device); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index ef975e2bda..022e111c5d 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -208,6 +208,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT{}; PFN_vkCmdBeginQuery vkCmdBeginQuery{}; PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass{}; + PFN_vkCmdBeginRendering vkCmdBeginRendering{}; PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT{}; PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets{}; PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer{}; @@ -236,6 +237,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; PFN_vkCmdEndQuery vkCmdEndQuery{}; PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; + PFN_vkCmdEndRendering vkCmdEndRendering{}; PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; PFN_vkCmdFillBuffer vkCmdFillBuffer{}; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; @@ -1186,6 +1188,14 @@ public: dld->vkCmdEndRenderPass(handle); } + void BeginRendering(const VkRenderingInfo& rendering_info) const noexcept { + dld->vkCmdBeginRendering(handle, &rendering_info); + } + + void EndRendering() const noexcept { + dld->vkCmdEndRendering(handle); + } + void BeginQuery(VkQueryPool query_pool, u32 query, VkQueryControlFlags flags) const noexcept { dld->vkCmdBeginQuery(handle, query_pool, query, flags); } From 20bf6bc282f421cd7d68ac09668d6bf54f8b2e06 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Jul 2026 12:16:52 -0400 Subject: [PATCH 02/23] [vulkan] Adjustment on the framebuffer use vs non used attachments --- .../renderer_vulkan/vk_scheduler.cpp | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3c70e5c786..2e5ff0761d 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -116,8 +116,57 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); } - Record([framebuffer](vk::CommandBuffer cmdbuf) { - framebuffer->BeginRendering(cmdbuf); + const u32 num_color = framebuffer->NumColorAttachments(); + const bool has_depth = framebuffer->HasAspectDepthBit(); + const bool has_stencil = framebuffer->HasAspectStencilBit(); + const VkImageView depth_view = framebuffer->DepthAttachment(); + const u32 layers = framebuffer->NumLayers(); + std::array color_infos{}; + for (u32 index = 0; index < num_color; ++index) { + color_infos[index] = VkRenderingAttachmentInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = color_views[index], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; + } + const VkRenderingAttachmentInfo depth_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = depth_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; + Record([color_infos, depth_info, num_color, has_depth, has_stencil, layers, + render_area](vk::CommandBuffer cmdbuf) { + const VkRenderingInfo rendering_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = 0, + .renderArea = + { + .offset = {.x = 0, .y = 0}, + .extent = render_area, + }, + .layerCount = layers, + .viewMask = 0, + .colorAttachmentCount = num_color, + .pColorAttachments = color_infos.data(), + .pDepthAttachment = has_depth ? &depth_info : nullptr, + .pStencilAttachment = has_stencil ? &depth_info : nullptr, + }; + cmdbuf.BeginRendering(rendering_info); }); num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); From f210f16e8c4af08e6b7ac79669117c9aa9ce0b47 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Jul 2026 23:44:27 -0400 Subject: [PATCH 03/23] [TEST] Debug on clears made by driver --- src/video_core/renderer_vulkan/vk_scheduler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 2e5ff0761d..5a40c28f42 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -131,9 +131,9 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass .resolveMode = VK_RESOLVE_MODE_NONE, .resolveImageView = VK_NULL_HANDLE, .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {}, + .clearValue = {.color = {.float32 = {1.0f, 0.0f, 1.0f, 1.0f}}}, }; } const VkRenderingAttachmentInfo depth_info{ From 4fbdc133dd864ac752c69810ac79a8d4a8986f48 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 10 Jul 2026 00:27:01 -0400 Subject: [PATCH 04/23] [vulkan] Add resume/supend bits on renderpass --- .../renderer_vulkan/vk_buffer_cache.cpp | 12 +- .../renderer_vulkan/vk_scheduler.cpp | 173 +++++++++++------- src/video_core/renderer_vulkan/vk_scheduler.h | 9 +- 3 files changed, 124 insertions(+), 70 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 4402550af9..8884535f1c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -197,7 +197,7 @@ public: } if (!host_visible) { - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestOutsideRenderPassOperationContext(true); scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset, dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) { const VkBufferCopy copy{ @@ -454,7 +454,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, return; } - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestOutsideRenderPassOperationContext(true); scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { if (barrier) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, @@ -475,7 +475,7 @@ void BufferCacheRuntime::PreCopyBarrier() { .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, }; - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestOutsideRenderPassOperationContext(true); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); @@ -489,7 +489,7 @@ void BufferCacheRuntime::PostCopyBarrier() { .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestOutsideRenderPassOperationContext(true); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, WRITE_BARRIER); @@ -513,7 +513,7 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestOutsideRenderPassOperationContext(true); scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); @@ -695,7 +695,7 @@ vk::Buffer BufferCacheRuntime::CreateNullBuffer() { ret.SetObjectNameEXT("Null buffer"); } - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestOutsideRenderPassOperationContext(true); scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) { cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0); }); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 5a40c28f42..af769faf35 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -107,7 +107,12 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass state.framebuffer = VkFramebuffer{}; state.attachment_views = attachment_views; state.render_area = render_area; + state.num_color = framebuffer->NumColorAttachments(); + state.has_depth = framebuffer->HasAspectDepthBit(); + state.has_stencil = framebuffer->HasAspectStencilBit(); + state.layer_count = framebuffer->NumLayers(); state.rendering = true; + state.suspended = false; if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string render_pass_info = @@ -116,58 +121,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); } - const u32 num_color = framebuffer->NumColorAttachments(); - const bool has_depth = framebuffer->HasAspectDepthBit(); - const bool has_stencil = framebuffer->HasAspectStencilBit(); - const VkImageView depth_view = framebuffer->DepthAttachment(); - const u32 layers = framebuffer->NumLayers(); - std::array color_infos{}; - for (u32 index = 0; index < num_color; ++index) { - color_infos[index] = VkRenderingAttachmentInfo{ - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = color_views[index], - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = VK_RESOLVE_MODE_NONE, - .resolveImageView = VK_NULL_HANDLE, - .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {.color = {.float32 = {1.0f, 0.0f, 1.0f, 1.0f}}}, - }; - } - const VkRenderingAttachmentInfo depth_info{ - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = depth_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = VK_RESOLVE_MODE_NONE, - .resolveImageView = VK_NULL_HANDLE, - .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {}, - }; - Record([color_infos, depth_info, num_color, has_depth, has_stencil, layers, - render_area](vk::CommandBuffer cmdbuf) { - const VkRenderingInfo rendering_info{ - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, - .pNext = nullptr, - .flags = 0, - .renderArea = - { - .offset = {.x = 0, .y = 0}, - .extent = render_area, - }, - .layerCount = layers, - .viewMask = 0, - .colorAttachmentCount = num_color, - .pColorAttachments = color_infos.data(), - .pDepthAttachment = has_depth ? &depth_info : nullptr, - .pStencilAttachment = has_stencil ? &depth_info : nullptr, - }; - cmdbuf.BeginRendering(rendering_info); - }); + RecordDynamicBegin(false, true); num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); @@ -281,9 +235,16 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { attachment_views[index] = color_views[index]; } attachment_views[8] = framebuffer->DepthAttachment(); - if (state.rendering && attachment_views == state.attachment_views && - render_area.width == state.render_area.width && - render_area.height == state.render_area.height) { + const bool same_target = state.rendering && + attachment_views == state.attachment_views && + render_area.width == state.render_area.width && + render_area.height == state.render_area.height; + if (same_target && !state.suspended) { + return; + } + if (same_target && state.suspended) { + RecordDynamicBegin(true, true); + state.suspended = false; return; } EndRenderPass(); @@ -302,7 +263,16 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { BeginRenderPassImpl(framebuffer, renderpass, nullptr, 0); } -void Scheduler::RequestOutsideRenderPassOperationContext() { +void Scheduler::RequestOutsideRenderPassOperationContext(bool allow_suspend) { + if (allow_suspend && device.IsKhrDynamicRenderingSupported() && state.rendering && + !state.suspended) { + query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); + query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); + query_cache->NotifySegment(false); + Record([](vk::CommandBuffer cmdbuf) { cmdbuf.EndRendering(); }); + state.suspended = true; + return; + } EndRenderPass(); } @@ -466,6 +436,69 @@ void Scheduler::InvalidateState() { state_tracker.InvalidateCommandBufferState(); } +void Scheduler::RecordDynamicBegin(bool resuming, bool suspending) { + const std::array views = state.attachment_views; + const u32 num_color = state.num_color; + const bool has_depth = state.has_depth; + const bool has_stencil = state.has_stencil; + const u32 layers = state.layer_count; + const VkExtent2D render_area = state.render_area; + VkRenderingFlags flags = 0; + if (resuming) { + flags |= VK_RENDERING_RESUMING_BIT; + } + if (suspending) { + flags |= VK_RENDERING_SUSPENDING_BIT; + } + Record([views, num_color, has_depth, has_stencil, layers, render_area, + flags](vk::CommandBuffer cmdbuf) { + std::array color_infos{}; + for (u32 index = 0; index < num_color; ++index) { + color_infos[index] = VkRenderingAttachmentInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = views[index], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; + } + const VkRenderingAttachmentInfo depth_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = views[8], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; + const VkRenderingInfo rendering_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = flags, + .renderArea = + { + .offset = {.x = 0, .y = 0}, + .extent = render_area, + }, + .layerCount = layers, + .viewMask = 0, + .colorAttachmentCount = num_color, + .pColorAttachments = color_infos.data(), + .pDepthAttachment = has_depth ? &depth_info : nullptr, + .pStencilAttachment = has_stencil ? &depth_info : nullptr, + }; + cmdbuf.BeginRendering(rendering_info); + }); +} + void Scheduler::EndPendingOperations() { query_cache->CounterReset(VideoCommon::QueryType::ZPassPixelCount64); EndRenderPass(); @@ -478,16 +511,29 @@ void Scheduler::EndRenderPass() return; } - query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); + const bool dynamic = device.IsKhrDynamicRenderingSupported(); - // Log render pass end - if (GPU::Logging::IsActive() && - Settings::values.gpu_log_vulkan_calls.GetValue()) { - GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); + if (!state.suspended) { + query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); + + // Log render pass end + if (GPU::Logging::IsActive() && + Settings::values.gpu_log_vulkan_calls.GetValue()) { + GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); + } + + query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); + query_cache->NotifySegment(false); + + if (dynamic) { + Record([](vk::CommandBuffer cmdbuf) { cmdbuf.EndRendering(); }); + state.suspended = true; + } } - query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); - query_cache->NotifySegment(false); + if (dynamic && state.suspended) { + RecordDynamicBegin(true, false); + } Record([num_images = num_renderpass_images, images = renderpass_images, @@ -555,6 +601,7 @@ void Scheduler::EndRenderPass() state.framebuffer = VkFramebuffer{}; state.attachment_views = {}; state.rendering = false; + state.suspended = false; num_renderpass_images = 0; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 7f44fb22ec..f6a26b5557 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -67,7 +67,7 @@ public: /// Requests the current execution context to be able to execute operations only allowed outside /// of a renderpass. - void RequestOutsideRenderPassOperationContext(); + void RequestOutsideRenderPassOperationContext(bool allow_suspend = false); /// Returns true when a render pass is currently active in the scheduler state. bool IsRenderPassActive() const { @@ -254,6 +254,11 @@ private: VkExtent2D render_area = {0, 0}; GraphicsPipeline* graphics_pipeline = nullptr; bool rendering = false; + bool suspended = false; + u32 num_color = 0; + bool has_depth = false; + bool has_stencil = false; + u32 layer_count = 1; bool is_rescaling = false; bool rescaling_defined = false; bool needs_state_enable_refresh = false; @@ -284,6 +289,8 @@ private: void EndPendingOperations(); + void RecordDynamicBegin(bool resuming, bool suspending); + void EndRenderPass(); void AcquireNewChunk(); From 9694216ad7dac509a4b8de1f6d2bce6590a2d9e0 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 16 Jul 2026 16:53:22 -0400 Subject: [PATCH 05/23] [TEST] 2nd stage on dynamic rendering implementation --- src/video_core/renderer_vulkan/blit_image.cpp | 28 ++-- src/video_core/renderer_vulkan/blit_image.h | 18 ++- .../renderer_vulkan/vk_buffer_cache.cpp | 12 +- .../renderer_vulkan/vk_rasterizer.cpp | 1 - .../renderer_vulkan/vk_scheduler.cpp | 151 ++++++++---------- src/video_core/renderer_vulkan/vk_scheduler.h | 8 +- .../renderer_vulkan/vk_texture_cache.cpp | 3 +- 7 files changed, 111 insertions(+), 110 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 079dee53a5..c92040691d 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -709,10 +709,12 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, const BlitMSAAPipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .samples = dst_framebuffer->Samples(), + .color_formats = dst_framebuffer->ColorAttachmentFormats(), + .depth_format = dst_framebuffer->DepthAttachmentFormat(), }; const VkPipelineLayout layout = *one_texture_pipeline_layout; const VkSampler sampler = *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key); + const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key, dst_framebuffer); const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D); RecordShaderReadBarrier(scheduler, src_image_view); @@ -736,7 +738,7 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer, const bool resolve_stencil = dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported(); const VkPipeline pipeline = - FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil); + FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer, resolve_stencil); const VkPipelineLayout layout = resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout; const VkSampler sampler = *nearest_sampler; @@ -1382,12 +1384,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( return *clear_stencil_pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key) { +VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key, + const Framebuffer* framebuffer) { const auto it = std::ranges::find(blit_msaa_color_keys, key); if (it != blit_msaa_color_keys.end()) { return *blit_msaa_color_pipelines[std::distance(blit_msaa_color_keys.begin(), it)]; } blit_msaa_color_keys.push_back(key); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*full_screen_vert, *blit_color_msaa_frag); const VkPipelineMultisampleStateCreateInfo multisample_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, @@ -1403,7 +1407,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); blit_msaa_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = key.renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1425,22 +1429,28 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip return *blit_msaa_color_pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, - bool resolve_stencil) { +VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline( + const Framebuffer* framebuffer, bool resolve_stencil) { + const VkRenderPass renderpass = framebuffer->RenderPass(); + const ResolveDepthStencilPipelineKey key{ + .renderpass = renderpass, + .depth_format = framebuffer->DepthAttachmentFormat(), + }; auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys; auto& pipelines = resolve_stencil ? resolve_depth_stencil_pipelines : resolve_depth_pipelines; - const auto it = std::ranges::find(keys, renderpass); + const auto it = std::ranges::find(keys, key); if (it != keys.end()) { return *pipelines[std::distance(keys.begin(), it)]; } - keys.push_back(renderpass); + keys.push_back(key); + const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*full_screen_vert, resolve_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = renderpass ? nullptr : &rendering_ci, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index ea8f37a48f..fd4db58a3b 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -62,6 +62,15 @@ struct BlitMSAAPipelineKey { VkRenderPass renderpass; VkSampleCountFlagBits samples; + std::array color_formats; + VkFormat depth_format; +}; + +struct ResolveDepthStencilPipelineKey { + constexpr auto operator<=>(const ResolveDepthStencilPipelineKey&) const noexcept = default; + + VkRenderPass renderpass; + VkFormat depth_format; }; class BlitImageHelper { @@ -138,8 +147,9 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline( const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer); [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key); - [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); - [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, + [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key, + const Framebuffer* framebuffer); + [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(const Framebuffer* framebuffer, bool resolve_stencil); void ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer, @@ -203,9 +213,9 @@ private: std::vector msaa_copy_pipelines; std::vector blit_msaa_color_keys; std::vector blit_msaa_color_pipelines; - std::vector resolve_depth_keys; + std::vector resolve_depth_keys; std::vector resolve_depth_pipelines; - std::vector resolve_depth_stencil_keys; + std::vector resolve_depth_stencil_keys; std::vector resolve_depth_stencil_pipelines; struct MSAACopyResources { u64 tick; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 8884535f1c..4402550af9 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -197,7 +197,7 @@ public: } if (!host_visible) { - scheduler.RequestOutsideRenderPassOperationContext(true); + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset, dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) { const VkBufferCopy copy{ @@ -454,7 +454,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, return; } - scheduler.RequestOutsideRenderPassOperationContext(true); + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { if (barrier) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, @@ -475,7 +475,7 @@ void BufferCacheRuntime::PreCopyBarrier() { .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, }; - scheduler.RequestOutsideRenderPassOperationContext(true); + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); @@ -489,7 +489,7 @@ void BufferCacheRuntime::PostCopyBarrier() { .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - scheduler.RequestOutsideRenderPassOperationContext(true); + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, WRITE_BARRIER); @@ -513,7 +513,7 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - scheduler.RequestOutsideRenderPassOperationContext(true); + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); @@ -695,7 +695,7 @@ vk::Buffer BufferCacheRuntime::CreateNullBuffer() { ret.SetObjectNameEXT("Null buffer"); } - scheduler.RequestOutsideRenderPassOperationContext(true); + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) { cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0); }); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a937f8e0c1..720f3b868c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -423,7 +423,6 @@ void RasterizerVulkan::Clear(u32 layer_count) { const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor && regs.clear_surface.layer == 0 && !scheduler.IsRenderPassActive() && - !device.IsKhrDynamicRenderingSupported() && (!use_color || color_full_channels) && ds_deferrable; if (!can_defer_clear) { scheduler.RequestRenderpass(framebuffer); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index af769faf35..5dba1ebdd9 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -93,40 +93,44 @@ void Scheduler::DispatchWork() { } } +void Scheduler::BeginDynamicRendering(const Framebuffer* framebuffer, const DeferredClear* clear) { + const VkExtent2D render_area = framebuffer->RenderArea(); + std::array attachment_views{}; + const auto& color_views = framebuffer->ColorAttachments(); + for (size_t index = 0; index < color_views.size(); ++index) { + attachment_views[index] = color_views[index]; + } + attachment_views[8] = framebuffer->DepthAttachment(); + state.renderpass = VkRenderPass{}; + state.framebuffer = VkFramebuffer{}; + state.attachment_views = attachment_views; + state.render_area = render_area; + state.num_color = framebuffer->NumColorAttachments(); + state.has_depth = framebuffer->HasAspectDepthBit(); + state.has_stencil = framebuffer->HasAspectStencilBit(); + state.layer_count = framebuffer->NumLayers(); + state.rendering = true; + + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { + const std::string render_pass_info = + fmt::format("renderArea={}x{}, numImages={}", render_area.width, render_area.height, + framebuffer->NumImages()); + GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); + } + + RecordDynamicBegin(clear); + num_renderpass_images = framebuffer->NumImages(); + renderpass_images = framebuffer->Images(); + renderpass_image_ranges = framebuffer->ImageRanges(); +} + void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass, const VkClearValue* clear_values, u32 clear_value_count) { - const VkExtent2D render_area = framebuffer->RenderArea(); if (device.IsKhrDynamicRenderingSupported()) { - std::array attachment_views{}; - const auto& color_views = framebuffer->ColorAttachments(); - for (size_t index = 0; index < color_views.size(); ++index) { - attachment_views[index] = color_views[index]; - } - attachment_views[8] = framebuffer->DepthAttachment(); - state.renderpass = VkRenderPass{}; - state.framebuffer = VkFramebuffer{}; - state.attachment_views = attachment_views; - state.render_area = render_area; - state.num_color = framebuffer->NumColorAttachments(); - state.has_depth = framebuffer->HasAspectDepthBit(); - state.has_stencil = framebuffer->HasAspectStencilBit(); - state.layer_count = framebuffer->NumLayers(); - state.rendering = true; - state.suspended = false; - - if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { - const std::string render_pass_info = - fmt::format("renderArea={}x{}, numImages={}", render_area.width, render_area.height, - framebuffer->NumImages()); - GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); - } - - RecordDynamicBegin(false, true); - num_renderpass_images = framebuffer->NumImages(); - renderpass_images = framebuffer->Images(); - renderpass_image_ranges = framebuffer->ImageRanges(); + BeginDynamicRendering(framebuffer, nullptr); return; } + const VkExtent2D render_area = framebuffer->RenderArea(); const VkFramebuffer framebuffer_handle = framebuffer->Handle(); state.renderpass = renderpass; state.framebuffer = framebuffer_handle; @@ -173,6 +177,12 @@ void Scheduler::RealizeDeferredClear() { const DeferredClear dc = deferred_clear; deferred_clear = {}; + if (device.IsKhrDynamicRenderingSupported()) { + EndRenderPass(); + BeginDynamicRendering(dc.framebuffer, &dc); + return; + } + std::array clear_values{}; u32 count = 0; const RenderPassKey& base = dc.framebuffer->RenderPassKeyBase(); @@ -235,20 +245,13 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { attachment_views[index] = color_views[index]; } attachment_views[8] = framebuffer->DepthAttachment(); - const bool same_target = state.rendering && - attachment_views == state.attachment_views && - render_area.width == state.render_area.width && - render_area.height == state.render_area.height; - if (same_target && !state.suspended) { - return; - } - if (same_target && state.suspended) { - RecordDynamicBegin(true, true); - state.suspended = false; + if (state.rendering && attachment_views == state.attachment_views && + render_area.width == state.render_area.width && + render_area.height == state.render_area.height) { return; } EndRenderPass(); - BeginRenderPassImpl(framebuffer, VkRenderPass{}, nullptr, 0); + BeginDynamicRendering(framebuffer, nullptr); return; } const VkRenderPass renderpass = framebuffer->RenderPass(); @@ -263,16 +266,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { BeginRenderPassImpl(framebuffer, renderpass, nullptr, 0); } -void Scheduler::RequestOutsideRenderPassOperationContext(bool allow_suspend) { - if (allow_suspend && device.IsKhrDynamicRenderingSupported() && state.rendering && - !state.suspended) { - query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); - query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); - query_cache->NotifySegment(false); - Record([](vk::CommandBuffer cmdbuf) { cmdbuf.EndRendering(); }); - state.suspended = true; - return; - } +void Scheduler::RequestOutsideRenderPassOperationContext() { EndRenderPass(); } @@ -436,24 +430,23 @@ void Scheduler::InvalidateState() { state_tracker.InvalidateCommandBufferState(); } -void Scheduler::RecordDynamicBegin(bool resuming, bool suspending) { +void Scheduler::RecordDynamicBegin(const DeferredClear* clear) { const std::array views = state.attachment_views; const u32 num_color = state.num_color; const bool has_depth = state.has_depth; const bool has_stencil = state.has_stencil; const u32 layers = state.layer_count; const VkExtent2D render_area = state.render_area; - VkRenderingFlags flags = 0; - if (resuming) { - flags |= VK_RENDERING_RESUMING_BIT; - } - if (suspending) { - flags |= VK_RENDERING_SUSPENDING_BIT; - } - Record([views, num_color, has_depth, has_stencil, layers, render_area, - flags](vk::CommandBuffer cmdbuf) { + const u32 color_clear_mask = clear ? clear->color_clear_mask : 0u; + const std::array color_clear_values = + clear ? clear->color_values : std::array{}; + const bool ds_clear = clear != nullptr && clear->depth_stencil; + const VkClearValue ds_clear_value = clear ? clear->depth_stencil_value : VkClearValue{}; + Record([views, num_color, has_depth, has_stencil, layers, render_area, color_clear_mask, + color_clear_values, ds_clear, ds_clear_value](vk::CommandBuffer cmdbuf) { std::array color_infos{}; for (u32 index = 0; index < num_color; ++index) { + const bool clear_slot = ((color_clear_mask >> index) & 1u) != 0; color_infos[index] = VkRenderingAttachmentInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .pNext = nullptr, @@ -462,9 +455,9 @@ void Scheduler::RecordDynamicBegin(bool resuming, bool suspending) { .resolveMode = VK_RESOLVE_MODE_NONE, .resolveImageView = VK_NULL_HANDLE, .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = clear_slot ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {}, + .clearValue = clear_slot ? color_clear_values[index] : VkClearValue{}, }; } const VkRenderingAttachmentInfo depth_info{ @@ -475,14 +468,14 @@ void Scheduler::RecordDynamicBegin(bool resuming, bool suspending) { .resolveMode = VK_RESOLVE_MODE_NONE, .resolveImageView = VK_NULL_HANDLE, .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = ds_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {}, + .clearValue = ds_clear ? ds_clear_value : VkClearValue{}, }; const VkRenderingInfo rendering_info{ .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, .pNext = nullptr, - .flags = flags, + .flags = 0, .renderArea = { .offset = {.x = 0, .y = 0}, @@ -511,29 +504,16 @@ void Scheduler::EndRenderPass() return; } - const bool dynamic = device.IsKhrDynamicRenderingSupported(); + query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); - if (!state.suspended) { - query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); - - // Log render pass end - if (GPU::Logging::IsActive() && - Settings::values.gpu_log_vulkan_calls.GetValue()) { - GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); - } - - query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); - query_cache->NotifySegment(false); - - if (dynamic) { - Record([](vk::CommandBuffer cmdbuf) { cmdbuf.EndRendering(); }); - state.suspended = true; - } + // Log render pass end + if (GPU::Logging::IsActive() && + Settings::values.gpu_log_vulkan_calls.GetValue()) { + GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); } - if (dynamic && state.suspended) { - RecordDynamicBegin(true, false); - } + query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); + query_cache->NotifySegment(false); Record([num_images = num_renderpass_images, images = renderpass_images, @@ -601,7 +581,6 @@ void Scheduler::EndRenderPass() state.framebuffer = VkFramebuffer{}; state.attachment_views = {}; state.rendering = false; - state.suspended = false; num_renderpass_images = 0; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index f6a26b5557..1ebd80db2b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -67,7 +67,7 @@ public: /// Requests the current execution context to be able to execute operations only allowed outside /// of a renderpass. - void RequestOutsideRenderPassOperationContext(bool allow_suspend = false); + void RequestOutsideRenderPassOperationContext(); /// Returns true when a render pass is currently active in the scheduler state. bool IsRenderPassActive() const { @@ -254,7 +254,6 @@ private: VkExtent2D render_area = {0, 0}; GraphicsPipeline* graphics_pipeline = nullptr; bool rendering = false; - bool suspended = false; u32 num_color = 0; bool has_depth = false; bool has_stencil = false; @@ -276,6 +275,9 @@ private: void BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass, const VkClearValue* clear_values, u32 clear_value_count); + /// Begins a dynamic rendering pass, optionally realizing a deferred clear via load ops. + void BeginDynamicRendering(const Framebuffer* framebuffer, const DeferredClear* clear); + /// If a deferred clear is pending. void RealizeDeferredClear(); @@ -289,7 +291,7 @@ private: void EndPendingOperations(); - void RecordDynamicBegin(bool resuming, bool suspending); + void RecordDynamicBegin(const DeferredClear* clear); void EndRenderPass(); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 5e6b702e3b..6e7507ad3e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2760,7 +2760,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } renderpass_key.samples = samples; const bool do_resolve_color = - samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); + samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler() && + !runtime.device.IsKhrDynamicRenderingSupported(); renderpass_key.resolve_color = do_resolve_color; discard_msaa_color = From 5428dbbd1404a250135a60cc41a64a8b9c3622cb Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 16 Jul 2026 17:04:32 -0400 Subject: [PATCH 06/23] [TEST] Adjustments on blit/clears per blending object --- src/video_core/renderer_vulkan/blit_image.cpp | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index c92040691d..613bfeb9af 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1281,25 +1281,30 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel clear_color_keys.push_back(key); const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag); - const VkPipelineColorBlendAttachmentState color_blend_attachment_state{ - .blendEnable = VK_TRUE, - .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR, - .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; + const u32 num_color = framebuffer->NumColorAttachments(); + std::array blend_attachments{}; + for (u32 index = 0; index < num_color; ++index) { + blend_attachments[index] = VkPipelineColorBlendAttachmentState{ + .blendEnable = index == 0 ? VK_TRUE : VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = index == 0 ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT) + : 0, + }; + } const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_CLEAR, - .attachmentCount = 1, - .pAttachments = &color_blend_attachment_state, + .attachmentCount = num_color, + .pAttachments = blend_attachments.data(), .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); @@ -1336,6 +1341,18 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( clear_stencil_keys.push_back(key); const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag); + const u32 num_color = framebuffer->NumColorAttachments(); + std::array blend_attachments{}; + const VkPipelineColorBlendStateCreateInfo color_blend_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_CLEAR, + .attachmentCount = num_color, + .pAttachments = blend_attachments.data(), + .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, + }; const auto stencil = VkStencilOpState{ .failOp = VK_STENCIL_OP_KEEP, .passOp = VK_STENCIL_OP_REPLACE, @@ -1373,7 +1390,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = &depth_stencil_ci, - .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, + .pColorBlendState = &color_blend_ci, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *clear_color_pipeline_layout, .renderPass = key.renderpass, From 6313800aee900dd70ddfb3233d12bd1c7112b6fc Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 16 Jul 2026 20:02:59 -0400 Subject: [PATCH 07/23] [TEST] Check on color components for masks/blending --- src/video_core/renderer_vulkan/blit_image.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 613bfeb9af..1225df5718 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1282,6 +1282,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer); const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag); const u32 num_color = framebuffer->NumColorAttachments(); + constexpr VkColorComponentFlags full_write_mask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT; std::array blend_attachments{}; for (u32 index = 0; index < num_color; ++index) { blend_attachments[index] = VkPipelineColorBlendAttachmentState{ @@ -1292,9 +1295,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = index == 0 ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT) - : 0, + .colorWriteMask = index == 0 ? full_write_mask : VkColorComponentFlags{0}, }; } const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{ From e058a15074c8c1881c9b624b750a6bd0d002de27 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 16 Jul 2026 20:29:18 -0400 Subject: [PATCH 08/23] [TEST] Wire MSAA resolve to dynamic rendering native resolve --- .../renderer_vulkan/vk_scheduler.cpp | 25 ++++++-- src/video_core/renderer_vulkan/vk_scheduler.h | 3 + .../renderer_vulkan/vk_texture_cache.cpp | 64 +++++++++++++++++-- .../renderer_vulkan/vk_texture_cache.h | 11 ++++ 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 5dba1ebdd9..cbb3010b5f 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -104,6 +104,9 @@ void Scheduler::BeginDynamicRendering(const Framebuffer* framebuffer, const Defe state.renderpass = VkRenderPass{}; state.framebuffer = VkFramebuffer{}; state.attachment_views = attachment_views; + state.color_resolve_views = framebuffer->ColorResolveAttachments(); + state.color_resolve_modes = framebuffer->ColorResolveModes(); + state.discards_msaa_color = framebuffer->DiscardsMsaaColor(); state.render_area = render_area; state.num_color = framebuffer->NumColorAttachments(); state.has_depth = framebuffer->HasAspectDepthBit(); @@ -432,31 +435,41 @@ void Scheduler::InvalidateState() { void Scheduler::RecordDynamicBegin(const DeferredClear* clear) { const std::array views = state.attachment_views; + const std::array resolve_views = state.color_resolve_views; + const std::array resolve_modes = state.color_resolve_modes; const u32 num_color = state.num_color; const bool has_depth = state.has_depth; const bool has_stencil = state.has_stencil; const u32 layers = state.layer_count; const VkExtent2D render_area = state.render_area; const u32 color_clear_mask = clear ? clear->color_clear_mask : 0u; + const u32 color_discard_mask = + clear != nullptr && state.discards_msaa_color ? clear->color_clear_mask : 0u; const std::array color_clear_values = clear ? clear->color_values : std::array{}; const bool ds_clear = clear != nullptr && clear->depth_stencil; const VkClearValue ds_clear_value = clear ? clear->depth_stencil_value : VkClearValue{}; - Record([views, num_color, has_depth, has_stencil, layers, render_area, color_clear_mask, - color_clear_values, ds_clear, ds_clear_value](vk::CommandBuffer cmdbuf) { + Record([views, resolve_views, resolve_modes, num_color, has_depth, has_stencil, layers, + render_area, color_clear_mask, color_discard_mask, color_clear_values, ds_clear, + ds_clear_value](vk::CommandBuffer cmdbuf) { std::array color_infos{}; for (u32 index = 0; index < num_color; ++index) { const bool clear_slot = ((color_clear_mask >> index) & 1u) != 0; + const VkImageView resolve_view = resolve_views[index]; + const bool has_resolve = resolve_view != VK_NULL_HANDLE; + const bool discard_slot = has_resolve && ((color_discard_mask >> index) & 1u) != 0; color_infos[index] = VkRenderingAttachmentInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .pNext = nullptr, .imageView = views[index], .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = VK_RESOLVE_MODE_NONE, - .resolveImageView = VK_NULL_HANDLE, - .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .resolveMode = has_resolve ? resolve_modes[index] : VK_RESOLVE_MODE_NONE, + .resolveImageView = resolve_view, + .resolveImageLayout = + has_resolve ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, .loadOp = clear_slot ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .storeOp = discard_slot ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE, .clearValue = clear_slot ? color_clear_values[index] : VkClearValue{}, }; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 1ebd80db2b..4af0054ea2 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -251,9 +251,12 @@ private: VkRenderPass renderpass{}; VkFramebuffer framebuffer{}; std::array attachment_views{}; + std::array color_resolve_views{}; + std::array color_resolve_modes{}; VkExtent2D render_area = {0, 0}; GraphicsPipeline* graphics_pipeline = nullptr; bool rendering = false; + bool discards_msaa_color = false; u32 num_color = 0; bool has_depth = false; bool has_stencil = false; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 6e7507ad3e..8aeafde03b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1041,6 +1041,31 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk shadow.extent = extent; shadow.layers = layers; shadow.up_to_date = true; + if (device.IsKhrDynamicRenderingSupported()) { + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([image = *shadow.image, layers](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = layers, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, barrier); + }); + } return *shadow.view; } @@ -2760,8 +2785,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } renderpass_key.samples = samples; const bool do_resolve_color = - samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler() && - !runtime.device.IsKhrDynamicRenderingSupported(); + samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); renderpass_key.resolve_color = do_resolve_color; discard_msaa_color = @@ -2781,10 +2805,15 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } const VkFormat vk_format = MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format; + color_resolve_modes[index] = VideoCore::Surface::IsPixelFormatInteger(format) + ? VK_RESOLVE_MODE_SAMPLE_ZERO_BIT + : VK_RESOLVE_MODE_AVERAGE_BIT; if (ENABLE_MSAA_RESOLVE_CONSUME) { const VkImage msaa_image = images[rt_map[index]]; - attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format, - render_area, layers)); + const VkImageView shadow_view = runtime.GetOrCreateResolveShadow( + msaa_image, vk_format, render_area, layers); + color_resolve_attachments[index] = shadow_view; + attachments.push_back(shadow_view); continue; } VkImageCreateInfo resolve_ci{ @@ -2823,6 +2852,33 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, .layerCount = layers, }, }); + if (runtime.device.IsKhrDynamicRenderingSupported()) { + runtime.scheduler.RequestOutsideRenderPassOperationContext(); + runtime.scheduler.Record([image = *resolve_image, layers](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = layers, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, + barrier); + }); + } + color_resolve_attachments[index] = *resolve_view; attachments.push_back(*resolve_view); resolve_images.push_back(std::move(resolve_image)); resolve_image_views.push_back(std::move(resolve_view)); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 003d7995cc..d3cd36b049 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -207,6 +207,15 @@ public: return color_attachment_formats; } + [[nodiscard]] const std::array& ColorResolveAttachments() const noexcept { + return color_resolve_attachments; + } + + [[nodiscard]] const std::array& ColorResolveModes() + const noexcept { + return color_resolve_modes; + } + [[nodiscard]] VkImageView DepthAttachment() const noexcept { return depth_attachment; } @@ -283,6 +292,8 @@ private: std::array rt_map{}; std::array color_attachments{}; std::array color_attachment_formats{}; + std::array color_resolve_attachments{}; + std::array color_resolve_modes{}; VkImageView depth_attachment{}; VkFormat depth_attachment_format = VK_FORMAT_UNDEFINED; u32 num_color_attachments = 0; From 791880f9bf6f0bed5bf40e1e90f0410b2205d25c Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 16 Jul 2026 22:50:08 -0400 Subject: [PATCH 09/23] [TEST] Adjust records/commandBuffer --- .../renderer_vulkan/vk_texture_cache.cpp | 95 ++++++++++--------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8aeafde03b..1007d6b375 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1042,29 +1042,30 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk shadow.layers = layers; shadow.up_to_date = true; if (device.IsKhrDynamicRenderingSupported()) { - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([image = *shadow.image, layers](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = layers, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, barrier); - }); + scheduler.RecordWithUploadBuffer( + [image = *shadow.image, layers](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = layers, + }, + }; + upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, + barrier); + }); } return *shadow.view; } @@ -2853,30 +2854,30 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, }, }); if (runtime.device.IsKhrDynamicRenderingSupported()) { - runtime.scheduler.RequestOutsideRenderPassOperationContext(); - runtime.scheduler.Record([image = *resolve_image, layers](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = layers, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, - barrier); - }); + runtime.scheduler.RecordWithUploadBuffer( + [image = *resolve_image, layers](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = layers, + }, + }; + upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, barrier); + }); } color_resolve_attachments[index] = *resolve_view; attachments.push_back(*resolve_view); From ab92e5fa52b04a3edb77a304d43bd7dd887e70c3 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 01:13:15 -0400 Subject: [PATCH 10/23] [TEST] Hunting down recursive mutex 1 --- .../renderer_vulkan/vk_query_cache.cpp | 5 ++--- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 16 +++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 709f65dc25..8d352f3b73 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -236,8 +236,7 @@ public: } PauseCounter(); const auto driver_id = device.GetDriverID(); - if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || - driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { pending_sync.clear(); sync_values_stash.clear(); return; @@ -1521,7 +1520,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { EndHostConditionalRendering(); return true; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 720f3b868c..77a4e3d9a7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -241,11 +241,17 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { if (!pipeline) { return; } - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - // update engine as channel may be different. - pipeline->SetEngine(maxwell3d, gpu_memory); - if (!pipeline->Configure(is_indexed)) - return; + { + // Only resource binding/upload needs the cache locks held. The query-cache calls and the + // deferred draw recording below acquire their own locks, so keeping them outside this scope + // avoids re-entrant locking of buffer_cache.mutex and shrinks the serialized region. + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + // update engine as channel may be different. + pipeline->SetEngine(maxwell3d, gpu_memory); + if (!pipeline->Configure(is_indexed)) { + return; + } + } UpdateDynamicStates(); From f53235779358969c81a5d106eae470552ee8541e Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 01:55:28 -0400 Subject: [PATCH 11/23] [TEST] Hunting down recursive mutex 2 --- src/video_core/renderer_vulkan/vk_query_cache.cpp | 5 +++-- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 4 ---- src/video_core/texture_cache/texture_cache_base.h | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 8d352f3b73..709f65dc25 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -236,7 +236,8 @@ public: } PauseCounter(); const auto driver_id = device.GetDriverID(); - if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { pending_sync.clear(); sync_values_stash.clear(); return; @@ -1520,7 +1521,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { EndHostConditionalRendering(); return true; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 77a4e3d9a7..2093617c69 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -242,11 +242,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { return; } { - // Only resource binding/upload needs the cache locks held. The query-cache calls and the - // deferred draw recording below acquire their own locks, so keeping them outside this scope - // avoids re-entrant locking of buffer_cache.mutex and shrinks the serialized region. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); if (!pipeline->Configure(is_indexed)) { return; diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index fbc2bb4cf7..abad222eba 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -257,7 +257,7 @@ public: /// Prepare an image to be used void PrepareImage(ImageId image_id, bool is_modification, bool invalidate); - std::recursive_mutex mutex; + std::mutex mutex; private: /// Iterate over all page indices in a range From 933f79af95a99213b1954bf215fc10f487f48549 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 02:15:10 -0400 Subject: [PATCH 12/23] [TEST] Hunting down recursive mutex 3 --- src/video_core/buffer_cache/buffer_cache_base.h | 2 +- src/video_core/renderer_opengl/gl_rasterizer.cpp | 2 +- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 14ab3e6ebc..3cddde6cf5 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -321,7 +321,7 @@ public: } while (channel_state->has_deleted_buffers); } - std::recursive_mutex mutex; + std::mutex mutex; Runtime& runtime; bool any_buffer_uploaded = false; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1d77d28c46..c0bb388930 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -716,7 +716,7 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::unique_lock lock{buffer_cache.mutex}; + std::scoped_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 2093617c69..ec9e4208b5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -925,7 +925,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::unique_lock lock{buffer_cache.mutex}; + std::scoped_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } From 84490a7d6ff6db85530a59382ee43bb341ce4029 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 02:32:14 -0400 Subject: [PATCH 13/23] [TEST] Hunting down recursive mutex 4 --- .../yuzu_emu/features/settings/model/view/SettingsItem.kt | 2 +- src/android/app/src/main/jni/android_settings.h | 2 +- src/video_core/buffer_cache/buffer_cache_base.h | 2 +- src/video_core/renderer_opengl/gl_rasterizer.cpp | 2 +- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 8 +++----- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 2 +- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 7c6063988f..644c2f751f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -594,7 +594,7 @@ abstract class SettingsItem( IntSetting.ANDROID_PIPELINE_WORKERS, titleId = R.string.pipeline_worker_cores, descriptionId = R.string.pipeline_worker_cores_description, - min = 4, + min = 1, max = 8, units = "cores" ) diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index 304ff62f07..3aba766a1b 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -147,7 +147,7 @@ namespace AndroidSettings { &show_performance_overlay}; - Settings::Setting pipeline_worker_count{linkage, 4, "pipeline_worker_count", + Settings::Setting pipeline_worker_count{linkage, 0, "pipeline_worker_count", Settings::Category::Android, Settings::Specialization::Default, true, diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 3cddde6cf5..14ab3e6ebc 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -321,7 +321,7 @@ public: } while (channel_state->has_deleted_buffers); } - std::mutex mutex; + std::recursive_mutex mutex; Runtime& runtime; bool any_buffer_uploaded = false; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c0bb388930..1d77d28c46 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -716,7 +716,7 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::scoped_lock lock{buffer_cache.mutex}; + std::unique_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 0ff4e52c54..92d16c4d67 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -305,12 +305,10 @@ size_t GetTotalPipelineWorkers() { std::max(static_cast(std::thread::hardware_concurrency()), 2ULL) - 1ULL; #ifdef __ANDROID__ const int configured = AndroidSettings::values.pipeline_worker_count.GetValue(); - const int clamped = std::clamp(configured, 4, 8); - const size_t desired = static_cast(clamped); - if (desired == 0) { - return 1ULL; + if (configured <= 0) { + return max_core_threads; } - return std::min(max_core_threads, desired); + return std::min(max_core_threads, static_cast(configured)); #else return max_core_threads; #endif diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ec9e4208b5..2093617c69 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -925,7 +925,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::scoped_lock lock{buffer_cache.mutex}; + std::unique_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } From a484e6c34b9d22c042a490310ec6de27b0c44655 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 03:00:53 -0400 Subject: [PATCH 14/23] [TEST] Hunting down recursive mutex 5 --- src/android/app/src/main/jni/android_settings.h | 2 +- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index 3aba766a1b..3eaa377b20 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -147,7 +147,7 @@ namespace AndroidSettings { &show_performance_overlay}; - Settings::Setting pipeline_worker_count{linkage, 0, "pipeline_worker_count", + Settings::Setting pipeline_worker_count{linkage, 2, "pipeline_worker_count", Settings::Category::Android, Settings::Specialization::Default, true, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 92d16c4d67..8e60368406 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -305,10 +305,8 @@ size_t GetTotalPipelineWorkers() { std::max(static_cast(std::thread::hardware_concurrency()), 2ULL) - 1ULL; #ifdef __ANDROID__ const int configured = AndroidSettings::values.pipeline_worker_count.GetValue(); - if (configured <= 0) { - return max_core_threads; - } - return std::min(max_core_threads, static_cast(configured)); + const size_t desired = static_cast(std::max(configured, 1)); + return std::min(max_core_threads, desired); #else return max_core_threads; #endif From eb32b8766a3777e75e3afc53568240f970475002 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 03:13:55 -0400 Subject: [PATCH 15/23] [TEST] Hunting down recursive mutex 6 --- src/video_core/buffer_cache/buffer_cache.h | 66 ++++++++++++++++++- .../renderer_opengl/gl_rasterizer.cpp | 1 - .../renderer_vulkan/vk_rasterizer.cpp | 1 - 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e40aad1fb5..cf5b29b2b1 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -175,9 +175,71 @@ std::optional BufferCache

::GetFlushArea(DA template void BufferCache

::DownloadMemory(DAddr device_addr, u64 size) { - ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) { - DownloadBufferMemory(buffer, device_addr, size); + if constexpr (!USE_MEMORY_MAPS) { + std::scoped_lock lock{mutex}; + ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) { + DownloadBufferMemory(buffer, device_addr, size); + }); + return; + } + + boost::container::small_vector, 8> downloads; + u64 total_size_bytes = 0; + u64 largest_copy = 0; + + std::unique_lock lock{mutex}; + ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { + memory_tracker.ForEachDownloadRangeAndClear( + device_addr, size, [&](u64 device_addr_out, u64 range_size) { + const DAddr buffer_addr = buffer.CpuAddr(); + const auto add_download = [&](DAddr start, DAddr end) { + const u64 new_offset = start - buffer_addr; + const u64 new_size = end - start; + downloads.push_back({ + BufferCopy{ + .src_offset = new_offset, + .dst_offset = total_size_bytes, + .size = new_size, + }, + buffer_id, + }); + constexpr u64 align = 64ULL; + constexpr u64 mask = ~(align - 1ULL); + total_size_bytes += (new_size + align - 1) & mask; + largest_copy = (std::max)(largest_copy, new_size); + }; + gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download); + ClearDownload(device_addr_out, range_size); + gpu_modified_ranges.Subtract(device_addr_out, range_size); + }); }); + if (total_size_bytes == 0) { + return; + } + + auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes); + boost::container::small_vector writebacks; + runtime.PreCopyBarrier(); + for (auto& [copy, buffer_id] : downloads) { + copy.dst_offset += download_staging.offset; + Buffer& buffer = slot_buffers[buffer_id]; + buffer.MarkUsage(copy.src_offset, copy.size); + const std::array copies{copy}; + runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); + BufferCopy writeback{copy}; + writeback.src_offset = static_cast(buffer.CpuAddr()) + copy.src_offset; + writebacks.push_back(writeback); + } + runtime.PostCopyBarrier(); + lock.unlock(); + + runtime.Finish(); + const u8* const base = download_staging.mapped_span.data(); + for (const BufferCopy& writeback : writebacks) { + const u64 staging_offset = writeback.dst_offset - download_staging.offset; + device_memory.WriteBlockUnsafe(static_cast(writeback.src_offset), + base + staging_offset, writeback.size); + } } template diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1d77d28c46..e660f75dae 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -485,7 +485,6 @@ void RasterizerOpenGL::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType texture_cache.DownloadMemory(addr, size); } if ((True(which & VideoCommon::CacheType::BufferCache))) { - std::scoped_lock lock{buffer_cache.mutex}; buffer_cache.DownloadMemory(addr, size); } if ((True(which & VideoCommon::CacheType::QueryCache))) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 2093617c69..6d9eed9d2e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -675,7 +675,6 @@ void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType texture_cache.DownloadMemory(addr, size); } if ((True(which & VideoCommon::CacheType::BufferCache))) { - std::scoped_lock lock{buffer_cache.mutex}; buffer_cache.DownloadMemory(addr, size); } if ((True(which & VideoCommon::CacheType::QueryCache))) { From 7ed7e5e31dad9392722dc1818f76169d51c63b69 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 03:56:02 -0400 Subject: [PATCH 16/23] [TEST] Remove unnecessary submit --- src/video_core/fence_manager.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 16f19bf009..a4242994a4 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -91,9 +91,6 @@ public: func(); } fences.push(std::move(new_fence)); - if (should_flush) { - rasterizer.FlushCommands(); - } if constexpr (can_async_check) { guard.unlock(); cv.notify_all(); From e81d170458eb2bb78e4524961391f12da66c61ff Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 07:28:30 -0400 Subject: [PATCH 17/23] [TEST] Remove unnecessary memory upload --- src/video_core/buffer_cache/buffer_cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index cf5b29b2b1..a3dd157fa6 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -276,7 +276,7 @@ bool BufferCache

::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am auto& src_buffer = slot_buffers[buffer_a]; auto& dest_buffer = slot_buffers[buffer_b]; SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast(amount)); - SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast(amount)); + memory_tracker.UnmarkRegionAsCpuModified(*cpu_dest_address, static_cast(amount)); std::array copies{BufferCopy{ .src_offset = src_buffer.Offset(*cpu_src_address), .dst_offset = dest_buffer.Offset(*cpu_dest_address), From e8b1dc7c0bb55bd1cbc9c99db601a25f2b5c220b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 07:52:45 -0400 Subject: [PATCH 18/23] [TEST] Adjustments on CommandPools + ResetQueryPool --- .../renderer_vulkan/vk_command_pool.cpp | 44 ++++++++++++++----- .../renderer_vulkan/vk_command_pool.h | 17 ++++--- .../vulkan_common/vulkan_wrapper.cpp | 1 + src/video_core/vulkan_common/vulkan_wrapper.h | 5 +++ 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp index d0dbf7ca54..7ba35e0e20 100644 --- a/src/video_core/renderer_vulkan/vk_command_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include "video_core/renderer_vulkan/vk_command_pool.h" +#include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -14,32 +18,52 @@ constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4; struct CommandPool::Pool { vk::CommandPool handle; vk::CommandBuffers cmdbufs; + u64 tick; }; CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_) - : ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {} + : master_semaphore{master_semaphore_}, device{device_} {} CommandPool::~CommandPool() = default; -void CommandPool::Allocate(size_t begin, size_t end) { - // Command buffers are going to be committed, recorded, executed every single usage cycle. - // They are also going to be reset when committed. +void CommandPool::AllocatePool() { Pool& pool = pools.emplace_back(); pool.handle = device.GetLogical().CreateCommandPool({ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .pNext = nullptr, - .flags = - VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, .queueFamilyIndex = device.GetGraphicsFamily(), }); pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE); + pool.tick = 0; +} + +void CommandPool::AcquirePool() { + if (!pools.empty()) { + master_semaphore.Refresh(); + const u64 gpu_tick = master_semaphore.KnownGpuTick(); + for (size_t i = 0; i < pools.size(); ++i) { + const size_t candidate = (current_pool + 1 + i) % pools.size(); + if (gpu_tick >= pools[candidate].tick) { + current_pool = candidate; + current_index = 0; + pools[current_pool].handle.Reset(); + return; + } + } + } + AllocatePool(); + current_pool = pools.size() - 1; + current_index = 0; } VkCommandBuffer CommandPool::Commit() { - const size_t index = CommitResource(); - const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE; - const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE; - return pools[pool_index].cmdbufs[sub_index]; + if (pools.empty() || current_index >= COMMAND_BUFFER_POOL_SIZE) { + AcquirePool(); + } + Pool& pool = pools[current_pool]; + pool.tick = master_semaphore.CurrentTick(); + return pool.cmdbufs[current_index++]; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_command_pool.h b/src/video_core/renderer_vulkan/vk_command_pool.h index ec1647f012..362ec0610d 100644 --- a/src/video_core/renderer_vulkan/vk_command_pool.h +++ b/src/video_core/renderer_vulkan/vk_command_pool.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +9,7 @@ #include #include -#include "video_core/renderer_vulkan/vk_resource_pool.h" +#include "common/common_types.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { @@ -14,20 +17,24 @@ namespace Vulkan { class Device; class MasterSemaphore; -class CommandPool final : public ResourcePool { +class CommandPool final { public: explicit CommandPool(MasterSemaphore& master_semaphore_, const Device& device_); - ~CommandPool() override; - - void Allocate(size_t begin, size_t end) override; + ~CommandPool(); VkCommandBuffer Commit(); private: struct Pool; + void AllocatePool(); + void AcquirePool(); + + MasterSemaphore& master_semaphore; const Device& device; std::vector pools; + size_t current_pool = 0; + size_t current_index = 0; }; } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 91344227c5..ffb549da06 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -232,6 +232,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkMapMemory); X(vkQueueSubmit); X(vkQueueSubmit2); + X(vkResetCommandPool); X(vkResetFences); X(vkResetQueryPool); X(vkSetDebugUtilsObjectNameEXT); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 022e111c5d..fb94d04c74 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -348,6 +348,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkMapMemory vkMapMemory{}; PFN_vkQueueSubmit vkQueueSubmit{}; PFN_vkQueueSubmit2 vkQueueSubmit2{}; + PFN_vkResetCommandPool vkResetCommandPool{}; PFN_vkResetFences vkResetFences{}; PFN_vkResetQueryPool vkResetQueryPool{}; PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{}; @@ -927,6 +928,10 @@ public: CommandBuffers Allocate(std::size_t num_buffers, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) const; + void Reset(VkCommandPoolResetFlags flags = 0) const { + Check(dld->vkResetCommandPool(owner, handle, flags)); + } + /// Set object name. void SetObjectNameEXT(const char* name) const; }; From 4956bc86c379a08de0bf3c433c15858e868bfbdb Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 17:44:24 -0400 Subject: [PATCH 19/23] [TEST] Hunting down recursive mutex 7 --- src/video_core/buffer_cache/buffer_cache.h | 58 +++++++++++++--------- src/video_core/fence_manager.h | 4 +- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index a3dd157fa6..b6d0852b4e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -735,32 +735,44 @@ void BufferCache

::PopAsyncFlushes() { template void BufferCache

::PopAsyncBuffers() { - if (async_buffers.empty()) { - return; - } - if (!async_buffers.front().has_value()) { + struct Writeback { + DAddr addr; + const u8* src; + u64 size; + }; + boost::container::small_vector writebacks; + { + std::scoped_lock lock{mutex}; + if (async_buffers.empty()) { + return; + } + if (!async_buffers.front().has_value()) { + async_buffers.pop_front(); + return; + } + auto& downloads = pending_downloads.front(); + auto& async_buffer = async_buffers.front(); + const u8* base = async_buffer->mapped_span.data(); + const size_t base_offset = async_buffer->offset; + for (const auto& copy : downloads) { + const DAddr device_addr = static_cast(copy.src_offset); + const u64 dst_offset = copy.dst_offset - base_offset; + const u8* read_mapped_memory = base + dst_offset; + async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) { + writebacks.push_back( + {start, &read_mapped_memory[start - device_addr], end - start}); + }); + async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) { + gpu_modified_ranges.Subtract(start, end - start); + }); + } + async_buffers_death_ring.emplace_back(*async_buffer); async_buffers.pop_front(); - return; + pending_downloads.pop_front(); } - auto& downloads = pending_downloads.front(); - auto& async_buffer = async_buffers.front(); - u8* base = async_buffer->mapped_span.data(); - const size_t base_offset = async_buffer->offset; - for (const auto& copy : downloads) { - const DAddr device_addr = static_cast(copy.src_offset); - const u64 dst_offset = copy.dst_offset - base_offset; - const u8* read_mapped_memory = base + dst_offset; - async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) { - device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr], - end - start); - }); - async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) { - gpu_modified_ranges.Subtract(start, end - start); - }); + for (const auto& wb : writebacks) { + device_memory.WriteBlockUnsafe(wb.addr, wb.src, wb.size); } - async_buffers_death_ring.emplace_back(*async_buffer); - async_buffers.pop_front(); - pending_downloads.pop_front(); } template diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index a4242994a4..7980786662 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -235,10 +235,10 @@ private: void PopAsyncFlushes() { { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + std::scoped_lock lock{texture_cache.mutex}; texture_cache.PopAsyncFlushes(); - buffer_cache.PopAsyncFlushes(); } + buffer_cache.PopAsyncFlushes(); query_cache.PopAsyncFlushes(); } From a57041d62f3df869651b1b48b64b8f7cdb11027c Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 17:57:55 -0400 Subject: [PATCH 20/23] [TEST] Discards on MSAA depth/stencil --- src/video_core/renderer_vulkan/vk_scheduler.cpp | 7 +++++-- src/video_core/renderer_vulkan/vk_scheduler.h | 1 + src/video_core/renderer_vulkan/vk_texture_cache.cpp | 3 +++ src/video_core/renderer_vulkan/vk_texture_cache.h | 5 +++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index cbb3010b5f..f513316df4 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -107,6 +107,7 @@ void Scheduler::BeginDynamicRendering(const Framebuffer* framebuffer, const Defe state.color_resolve_views = framebuffer->ColorResolveAttachments(); state.color_resolve_modes = framebuffer->ColorResolveModes(); state.discards_msaa_color = framebuffer->DiscardsMsaaColor(); + state.discards_msaa_depth = framebuffer->DiscardsMsaaDepth(); state.render_area = render_area; state.num_color = framebuffer->NumColorAttachments(); state.has_depth = framebuffer->HasAspectDepthBit(); @@ -449,9 +450,10 @@ void Scheduler::RecordDynamicBegin(const DeferredClear* clear) { clear ? clear->color_values : std::array{}; const bool ds_clear = clear != nullptr && clear->depth_stencil; const VkClearValue ds_clear_value = clear ? clear->depth_stencil_value : VkClearValue{}; + const bool ds_discard = state.discards_msaa_depth; Record([views, resolve_views, resolve_modes, num_color, has_depth, has_stencil, layers, render_area, color_clear_mask, color_discard_mask, color_clear_values, ds_clear, - ds_clear_value](vk::CommandBuffer cmdbuf) { + ds_clear_value, ds_discard](vk::CommandBuffer cmdbuf) { std::array color_infos{}; for (u32 index = 0; index < num_color; ++index) { const bool clear_slot = ((color_clear_mask >> index) & 1u) != 0; @@ -482,7 +484,8 @@ void Scheduler::RecordDynamicBegin(const DeferredClear* clear) { .resolveImageView = VK_NULL_HANDLE, .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, .loadOp = ds_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .storeOp = ds_discard ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE, .clearValue = ds_clear ? ds_clear_value : VkClearValue{}, }; const VkRenderingInfo rendering_info{ diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 4af0054ea2..bc85aadf19 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -257,6 +257,7 @@ private: GraphicsPipeline* graphics_pipeline = nullptr; bool rendering = false; bool discards_msaa_color = false; + bool discards_msaa_depth = false; u32 num_color = 0; bool has_depth = false; bool has_stencil = false; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 1007d6b375..1475df1507 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -54,6 +54,7 @@ using VideoCore::Surface::SurfaceType; namespace { constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; +constexpr bool ENABLE_MSAA_DEPTH_DISCARD = true; constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (color == std::array{0, 0, 0, 0}) { @@ -2791,6 +2792,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, discard_msaa_color = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; + discard_msaa_depth = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_DEPTH_DISCARD && + samples != VK_SAMPLE_COUNT_1_BIT && has_depth && runtime.device.IsTiler(); render_pass_key = renderpass_key; render_pass_cache = &runtime.render_pass_cache; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index d3cd36b049..aa29b6302d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -280,6 +280,10 @@ public: return discard_msaa_color; } + [[nodiscard]] bool DiscardsMsaaDepth() const noexcept { + return discard_msaa_depth; + } + private: vk::Framebuffer framebuffer; VkRenderPass renderpass{}; @@ -306,6 +310,7 @@ private: RenderPassKey render_pass_key{}; RenderPassCache* render_pass_cache{nullptr}; bool discard_msaa_color{}; + bool discard_msaa_depth{}; }; class Image : public VideoCommon::ImageBase { From eec29b83f3179871f8eab8d936180bab2e55f554 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 19:56:16 -0400 Subject: [PATCH 21/23] [TEST] Coalesce NCE fault write. --- src/core/arm/nce/arm_nce.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 63d5638fce..b181839db8 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -157,6 +157,8 @@ bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, return HandleFailedGuestFault(guest_ctx, raw_info, raw_context); } +constexpr size_t NCE_WRITE_FAULT_CLUSTER_PAGES = 4; + bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { auto* info = static_cast(raw_info); @@ -165,7 +167,7 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi const Common::ProcessAddress addr = (reinterpret_cast(info->si_addr) & ~Memory::YUZU_PAGEMASK); auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory(); - if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) { + if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE * NCE_WRITE_FAULT_CLUSTER_PAGES)) { // We handled the access successfully and are returning to guest code. return true; } From 9c313fb787a93d59f351879ec44a80db854ce330 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 20:15:19 -0400 Subject: [PATCH 22/23] [TEST] Caching for texture + pages on NCE --- src/core/device_memory_manager.h | 5 ++++ src/core/device_memory_manager.inc | 23 +++++++++++++++++++ .../renderer_vulkan/vk_rasterizer.cpp | 5 +++- src/video_core/texture_cache/texture_cache.h | 5 ++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 3d97fdcc5c..a66ca4aaae 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -126,6 +126,10 @@ public: // New batch API to update multiple ranges with a single lock acquisition. void UpdatePagesCachedBatch(std::span> ranges, s32 delta); + void UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta); + + [[nodiscard]] bool IsRegionTextureCached(DAddr addr, size_t size) const noexcept; + private: struct TranslationEntry { DAddr guest_page{}; @@ -234,6 +238,7 @@ private: (1ULL << (device_virtual_bits - page_bits)) / subentries; using CachedPages = std::array; std::unique_ptr cached_pages; + std::unique_ptr texture_cached_pages; Common::RangeMutex counter_guard; std::mutex mapping_guard; diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 43a480cfef..15e52b1511 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -177,6 +177,7 @@ DeviceMemoryManager::DeviceMemoryManager(const DeviceMemory& device_memo { impl = std::make_unique>(); cached_pages = std::make_unique(); + texture_cached_pages = std::make_unique(); const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS; for (size_t i = 0; i < total_virtual; i++) { @@ -625,6 +626,28 @@ void DeviceMemoryManager::UpdatePagesCachedCount(DAddr addr, size_t size UpdatePagesCachedCountNoLock(addr, size, delta); } +template +void DeviceMemoryManager::UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta) { + Common::ScopedRangeLock lk(counter_guard, addr, size); + const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE); + for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) { + CounterAtomicType& count = texture_cached_pages->at(page >> subentries_shift).Count(page); + count.fetch_add(static_cast(delta), std::memory_order_release); + } +} + +template +bool DeviceMemoryManager::IsRegionTextureCached(DAddr addr, size_t size) const noexcept { + const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE); + for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) { + if (texture_cached_pages->at(page >> subentries_shift).Count(page).load( + std::memory_order_acquire) != 0) { + return true; + } + } + return false; +} + template void DeviceMemoryManager::UpdatePagesCachedBatch(std::span> ranges, s32 delta) { if (ranges.empty()) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6d9eed9d2e..c5ceebf6cb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -772,12 +772,15 @@ bool RasterizerVulkan::OnCPUWrite(DAddr addr, u64 size) { return false; } +static constexpr bool ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP = true; + void RasterizerVulkan::OnCacheInvalidation(DAddr addr, u64 size) { if (addr == 0 || size == 0) { return; } - { + if (!ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP || + device_memory.IsRegionTextureCached(addr, size)) { std::scoped_lock lock{texture_cache.mutex}; texture_cache.WriteMemory(addr, size); } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 2cfdb94359..1989511dd4 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -2308,6 +2308,7 @@ void TextureCache

::TrackImage(ImageBase& image, ImageId image_id) { if (False(image.flags & ImageFlagBits::Sparse)) { if (image.cpu_addr < ~(1ULL << 40)) { device_memory.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); + device_memory.UpdateTexturePagesCount(image.cpu_addr, image.guest_size_bytes, 1); } return; } @@ -2320,12 +2321,14 @@ void TextureCache

::TrackImage(ImageBase& image, ImageId image_id) { const DAddr cpu_addr = map.cpu_addr; const std::size_t size = map.size; device_memory.UpdatePagesCachedCount(cpu_addr, size, 1); + device_memory.UpdateTexturePagesCount(cpu_addr, size, 1); } return; } ForEachSparseSegment(image, [this]([[maybe_unused]] GPUVAddr gpu_addr, DAddr cpu_addr, size_t size) { device_memory.UpdatePagesCachedCount(cpu_addr, size, 1); + device_memory.UpdateTexturePagesCount(cpu_addr, size, 1); }); } @@ -2336,6 +2339,7 @@ void TextureCache

::UntrackImage(ImageBase& image, ImageId image_id) { if (False(image.flags & ImageFlagBits::Sparse)) { if (image.cpu_addr < ~(1ULL << 40)) { device_memory.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1); + device_memory.UpdateTexturePagesCount(image.cpu_addr, image.guest_size_bytes, -1); } return; } @@ -2348,6 +2352,7 @@ void TextureCache

::UntrackImage(ImageBase& image, ImageId image_id) { const DAddr cpu_addr = map.cpu_addr; const std::size_t size = map.size; device_memory.UpdatePagesCachedCount(cpu_addr, size, -1); + device_memory.UpdateTexturePagesCount(cpu_addr, size, -1); } } From f199597158abff1257fa4693384ce833681c7973 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 17 Jul 2026 21:03:17 -0400 Subject: [PATCH 23/23] [TEST] DR Off --- src/video_core/vulkan_common/vulkan_device.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 10268c3e25..daabb971b3 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -942,9 +942,11 @@ FN_MAX_LIMIT_LIST return extensions.maintenance3; } + static constexpr bool ENABLE_DYNAMIC_RENDERING = false; + /// Returns true if the device supports VK_KHR_dynamic_rendering. bool IsKhrDynamicRenderingSupported() const { - return extensions.dynamic_rendering; + return ENABLE_DYNAMIC_RENDERING && extensions.dynamic_rendering; } /// Returns true if the device supports VK_KHR_maintenance4.