From 8a606458d6c36ab03e265903223d36249f50b631 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Wed, 21 Jan 2026 18:53:01 +1000 Subject: [PATCH] fix(vulkan): coordinate query cache with render pass boundaries - Close queries before ending render pass in EndRenderPass - Close queries before starting new render pass in RequestRenderpass - Simplify EndPendingOperations to delegate to EndRenderPass - Fixes validation errors about queries started in wrong render pass context --- .../renderer_vulkan/vk_scheduler.cpp | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 461cdb809..e93c0e54a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -98,6 +98,17 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { render_area.height == state.render_area.height) { return; } + + // CRITICAL: Before transitioning to a new render pass, we must close any queries + // that were started outside the current render pass context. Vulkan requires that + // if vkCmdBeginQuery was called outside a render pass, vkCmdEndQuery must also + // be called outside that render pass. + if (query_cache && !state.renderpass) { + // We're transitioning from outside render pass to inside + // Close any queries that were started outside + query_cache->NotifySegment(false); + } + EndRenderPass(); state.renderpass = renderpass; state.framebuffer = framebuffer_handle; @@ -122,6 +133,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); + // Note: Queries will be started by the next draw call via NotifySegment(true) in PrepareDraw } void Scheduler::RequestOutsideRenderPassOperationContext() { @@ -279,18 +291,8 @@ void Scheduler::InvalidateState() { } void Scheduler::EndPendingOperations() { -#if ANDROID - if (Settings::IsGPULevelHigh()) { - // This is problematic on Android, disable on GPU Normal and Low. - // query_cache->DisableStreams(); - } -#else - // query_cache->DisableStreams(); -#endif - if (Settings::IsGPULevelNormal()) { - // Skip query cache operations for Low accuracy - query_cache->NotifySegment(false); - } + // EndRenderPass now handles closing queries before ending the render pass + // This ensures queries started inside a render pass are properly ended EndRenderPass(); } @@ -298,6 +300,12 @@ void Scheduler::EndRenderPass() { if (!state.renderpass) { return; } + // CRITICAL: End any active queries before ending the render pass + // Queries started inside a render pass must be ended before vkCmdEndRenderPass + // This prevents validation errors about queries started in subpass but not ended + if (query_cache) { + query_cache->NotifySegment(false); + } Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) { std::array barriers;