diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5c214e5207..87dbc33b7a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -438,10 +438,17 @@ void RasterizerVulkan::Clear(u32 layer_count) { const bool ds_deferrable = !ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) && (!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial); - const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor && - regs.clear_surface.layer == 0 && - !scheduler.IsRenderPassActive() && - (!use_color || color_full_channels) && ds_deferrable; + const bool clear_shape_deferrable = ENABLE_DEFERRED_CLEAR && + !regs.clear_control.use_scissor && + regs.clear_surface.layer == 0 && + (!use_color || color_full_channels) && ds_deferrable; + // An open pass normally blocks deferral, which drops the clear to ClearAttachments inside the + // pass and also loses the MSAA store discard, since that only applies when a clear is folded + // into the begin. A pass whose BeginRendering has not been recorded yet can be retracted for + // free, so the clear becomes a load op after all. Only retract when it will actually be used. + const bool can_defer_clear = + clear_shape_deferrable && + (!scheduler.IsRenderPassActive() || scheduler.RetractUnrecordedRenderPass()); 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 741f424401..b4ca9b4049 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -126,12 +126,43 @@ void Scheduler::BeginDynamicRendering(const Framebuffer* framebuffer, const Defe GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); } - RecordDynamicBegin(clear); + pending_begin = true; + has_pending_begin_clear = clear != nullptr; + pending_begin_clear = clear != nullptr ? *clear : DeferredClear{}; num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); } +void Scheduler::FlushPendingRenderPass() { + if (!pending_begin) { + return; + } + + pending_begin = false; + const bool had_clear = has_pending_begin_clear; + has_pending_begin_clear = false; + RecordDynamicBegin(had_clear ? &pending_begin_clear : nullptr); +} + +bool Scheduler::RetractUnrecordedRenderPass() { + if (!pending_begin) { + return false; + } + pending_begin = false; + if (has_pending_begin_clear) { + deferred_clear = pending_begin_clear; + has_pending_begin_clear = false; + } + state.renderpass = VkRenderPass{}; + state.framebuffer = VkFramebuffer{}; + state.attachment_views = {}; + state.rendering = false; + state.uses_transform_feedback = false; + num_renderpass_images = 0; + return true; +} + void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass, const VkClearValue* clear_values, u32 clear_value_count) { if (device.IsKhrDynamicRenderingSupported()) { @@ -439,101 +470,152 @@ void Scheduler::InvalidateState() { state_tracker.InvalidateCommandBufferState(); } -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{}; - const bool ds_discard = state.discards_msaa_depth; - const VkImageView ds_resolve_view = state.depth_resolve_view; - const VkResolveModeFlagBits depth_resolve_mode = state.depth_resolve_mode; - const VkResolveModeFlagBits stencil_resolve_mode = state.stencil_resolve_mode; - 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, ds_discard, ds_resolve_view, depth_resolve_mode, - stencil_resolve_mode](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 = 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 = discard_slot ? VK_ATTACHMENT_STORE_OP_DONT_CARE - : VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = clear_slot ? color_clear_values[index] : VkClearValue{}, - }; - } - const bool has_ds_resolve = ds_resolve_view != VK_NULL_HANDLE; - const VkAttachmentLoadOp ds_load_op = ds_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR - : ds_discard ? VK_ATTACHMENT_LOAD_OP_DONT_CARE - : VK_ATTACHMENT_LOAD_OP_LOAD; - const VkAttachmentStoreOp ds_store_op = - ds_discard ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; - const VkRenderingAttachmentInfo depth_info{ +namespace { +/// The part of a dynamic rendering begin that every pass needs. +struct DynamicRenderingBase { + std::array views; + VkExtent2D render_area; + u32 num_color; + u32 layers; + VkImageView ds_resolve_view; + VkResolveModeFlagBits depth_resolve_mode; + VkResolveModeFlagBits stencil_resolve_mode; + bool has_depth; + bool has_stencil; + bool ds_discard; +}; + +/// The part only a pass that actually clears something needs. +struct DynamicRenderingClears { + std::array color_values; + VkClearValue ds_value; + u32 color_clear_mask; + u32 color_discard_mask; + bool ds_clear; +}; + +/// Builds the rendering info and begins the instance. `resolve_views`/`resolve_modes` and `clears` +/// are null for passes with no colour resolve targets and no clears, so those tables never have to +/// be copied into the recorded command. +void IssueBeginRendering(vk::CommandBuffer cmdbuf, const DynamicRenderingBase& base, + const std::array* resolve_views, + const std::array* resolve_modes, + const DynamicRenderingClears* clears) { + std::array color_infos{}; + for (u32 index = 0; index < base.num_color; ++index) { + const bool clear_slot = + clears != nullptr && ((clears->color_clear_mask >> index) & 1u) != 0; + const VkImageView resolve_view = + resolve_views != nullptr ? (*resolve_views)[index] : VK_NULL_HANDLE; + const bool has_resolve = resolve_view != VK_NULL_HANDLE; + const bool discard_slot = + has_resolve && clears != nullptr && ((clears->color_discard_mask >> index) & 1u) != 0; + color_infos[index] = VkRenderingAttachmentInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .pNext = nullptr, - .imageView = views[8], + .imageView = base.views[index], .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = has_ds_resolve ? depth_resolve_mode : VK_RESOLVE_MODE_NONE, - .resolveImageView = has_ds_resolve ? ds_resolve_view : VK_NULL_HANDLE, + .resolveMode = has_resolve ? (*resolve_modes)[index] : VK_RESOLVE_MODE_NONE, + .resolveImageView = resolve_view, .resolveImageLayout = - has_ds_resolve ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = ds_load_op, - .storeOp = ds_store_op, - .clearValue = ds_clear ? ds_clear_value : VkClearValue{}, + has_resolve ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = clear_slot ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = discard_slot ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = clear_slot ? clears->color_values[index] : VkClearValue{}, }; - // Stencil gets its own struct because its resolve mode may differ from depth's. - const VkRenderingAttachmentInfo stencil_info{ - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = views[8], - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = has_ds_resolve ? stencil_resolve_mode : VK_RESOLVE_MODE_NONE, - .resolveImageView = has_ds_resolve ? ds_resolve_view : VK_NULL_HANDLE, - .resolveImageLayout = - has_ds_resolve ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = ds_load_op, - .storeOp = ds_store_op, - .clearValue = ds_clear ? ds_clear_value : VkClearValue{}, - }; - 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 ? &stencil_info : nullptr, - }; - cmdbuf.BeginRendering(rendering_info); + } + const bool ds_clear = clears != nullptr && clears->ds_clear; + const VkClearValue ds_clear_value = clears != nullptr ? clears->ds_value : VkClearValue{}; + const bool has_ds_resolve = base.ds_resolve_view != VK_NULL_HANDLE; + const VkAttachmentLoadOp ds_load_op = ds_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR + : base.ds_discard ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD; + const VkAttachmentStoreOp ds_store_op = + base.ds_discard ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; + const VkRenderingAttachmentInfo depth_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = base.views[8], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = has_ds_resolve ? base.depth_resolve_mode : VK_RESOLVE_MODE_NONE, + .resolveImageView = has_ds_resolve ? base.ds_resolve_view : VK_NULL_HANDLE, + .resolveImageLayout = has_ds_resolve ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = ds_load_op, + .storeOp = ds_store_op, + .clearValue = ds_clear ? ds_clear_value : VkClearValue{}, + }; + // Stencil gets its own struct because its resolve mode may differ from depth's. + const VkRenderingAttachmentInfo stencil_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = base.views[8], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = has_ds_resolve ? base.stencil_resolve_mode : VK_RESOLVE_MODE_NONE, + .resolveImageView = has_ds_resolve ? base.ds_resolve_view : VK_NULL_HANDLE, + .resolveImageLayout = has_ds_resolve ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = ds_load_op, + .storeOp = ds_store_op, + .clearValue = ds_clear ? ds_clear_value : VkClearValue{}, + }; + const VkRenderingInfo rendering_info{ + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = 0, + .renderArea = + { + .offset = {.x = 0, .y = 0}, + .extent = base.render_area, + }, + .layerCount = base.layers, + .viewMask = 0, + .colorAttachmentCount = base.num_color, + .pColorAttachments = color_infos.data(), + .pDepthAttachment = base.has_depth ? &depth_info : nullptr, + .pStencilAttachment = base.has_stencil ? &stencil_info : nullptr, + }; + cmdbuf.BeginRendering(rendering_info); +} +} // Anonymous namespace + +void Scheduler::RecordDynamicBegin(const DeferredClear* clear) { + const DynamicRenderingBase base{ + .views = state.attachment_views, + .render_area = state.render_area, + .num_color = state.num_color, + .layers = state.layer_count, + .ds_resolve_view = state.depth_resolve_view, + .depth_resolve_mode = state.depth_resolve_mode, + .stencil_resolve_mode = state.stencil_resolve_mode, + .has_depth = state.has_depth, + .has_stencil = state.has_stencil, + .ds_discard = state.discards_msaa_depth, + }; + bool has_color_resolve = false; + for (const VkImageView resolve_view : state.color_resolve_views) { + has_color_resolve = has_color_resolve || resolve_view != VK_NULL_HANDLE; + } + if (clear == nullptr && !has_color_resolve) { + // Common case. Every byte captured here is copied into the command chunk on every single + // pass begin, and the clear values plus the two resolve tables are two thirds of them. + Record([base](vk::CommandBuffer cmdbuf) { + IssueBeginRendering(cmdbuf, base, nullptr, nullptr, nullptr); + }); + return; + } + const DynamicRenderingClears clears{ + .color_values = clear != nullptr ? clear->color_values : std::array{}, + .ds_value = clear != nullptr ? clear->depth_stencil_value : VkClearValue{}, + .color_clear_mask = clear != nullptr ? clear->color_clear_mask : 0u, + .color_discard_mask = + clear != nullptr && state.discards_msaa_color ? clear->color_clear_mask : 0u, + .ds_clear = clear != nullptr && clear->depth_stencil, + }; + Record([base, resolve_views = state.color_resolve_views, + resolve_modes = state.color_resolve_modes, clears](vk::CommandBuffer cmdbuf) { + IssueBeginRendering(cmdbuf, base, &resolve_views, &resolve_modes, &clears); }); } @@ -560,6 +642,14 @@ void Scheduler::EndRenderPass() query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); query_cache->NotifySegment(false); + if (pending_begin) { + if (!has_pending_begin_clear) { + RetractUnrecordedRenderPass(); + return; + } + FlushPendingRenderPass(); + } + Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 3732fc6171..b14463c477 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -112,12 +112,17 @@ public: template requires std::is_invocable_v void Record(T&& c) { + if (pending_begin) { + FlushPendingRenderPass(); + } this->RecordWithUploadBuffer( [command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) { command(cmdbuf); }); } + bool RetractUnrecordedRenderPass(); + /// Returns the current command buffer tick. [[nodiscard]] u64 CurrentTick() const noexcept { return master_semaphore->CurrentTick(); @@ -318,6 +323,8 @@ private: void RecordDynamicBegin(const DeferredClear* clear); + void FlushPendingRenderPass(); + void EndRenderPass(); void AcquireNewChunk(); @@ -335,6 +342,10 @@ private: DeferredClear deferred_clear; + bool pending_begin = false; + bool has_pending_begin_clear = false; + DeferredClear pending_begin_clear; + std::unique_ptr chunk; std::function on_submit; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 223c341543..7b1b2ef2fa 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -56,6 +56,7 @@ namespace { constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; constexpr bool ENABLE_MSAA_DEPTH_DISCARD = true; +constexpr bool ENABLE_MSAA_DEPTH_RESOLVE = false; constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (color == std::array{0, 0, 0, 0}) { @@ -2882,14 +2883,15 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, (!has_stencil || (stencil_mode != VK_RESOLVE_MODE_NONE && (runtime.device.IsIndependentResolveSupported() || depth_mode == stencil_mode))); - const bool do_resolve_depth = - samples != VK_SAMPLE_COUNT_1_BIT && has_depth && runtime.device.IsTiler() && - runtime.device.IsKhrDynamicRenderingSupported() && resolve_modes_compatible; + const bool msaa_depth = + samples != VK_SAMPLE_COUNT_1_BIT && has_depth && runtime.device.IsTiler(); + const bool do_resolve_depth = ENABLE_MSAA_DEPTH_RESOLVE && msaa_depth && + runtime.device.IsKhrDynamicRenderingSupported() && + resolve_modes_compatible; 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 && do_resolve_depth; + discard_msaa_depth = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_DEPTH_DISCARD && msaa_depth; render_pass_key = renderpass_key; render_pass_cache = &runtime.render_pass_cache;