diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index f95f41c47a..b540983fed 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -50,9 +50,6 @@ using VideoCore::Surface::SurfaceType; const SurfaceType surface_type = GetSurfaceType(format); const bool has_stencil = surface_type == SurfaceType::DepthStencil || surface_type == SurfaceType::Stencil; - const VkImageLayout attachment_layout = - surface_type == SurfaceType::ColorTexture ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; return { .flags = {}, .format = SurfaceFormat(device, FormatType::Optimal, true, format).format, @@ -63,8 +60,14 @@ using VideoCore::Surface::SurfaceType; : VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = has_stencil ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = attachment_layout, - .finalLayout = attachment_layout, + // Render passes operate in optimal attachment layouts; transitions are managed by the + // scheduler based on the tracked image state. + .initialLayout = surface_type == SurfaceType::ColorTexture + ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + .finalLayout = surface_type == SurfaceType::ColorTexture + ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, }; } } // Anonymous namespace diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 05c7678fe5..9eae6981ad 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -23,6 +23,50 @@ namespace Vulkan { +namespace { +struct StageAccessInfo { + VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags access = 0; +}; + +[[nodiscard]] StageAccessInfo StageAccessForLayout(VkImageLayout layout, + [[maybe_unused]] VkImageAspectFlags aspect_mask) noexcept { + switch (layout) { + case VK_IMAGE_LAYOUT_UNDEFINED: + return {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0}; + case VK_IMAGE_LAYOUT_GENERAL: + return {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT}; + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + return {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT}; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: { + constexpr VkPipelineStageFlags depth_stages = + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + return {depth_stages, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT}; + } + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + return {VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT}; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + return {VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT}; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + return {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_READ_BIT}; + default: + // Fallback to a conservative barrier when encountering uncommon layouts. + return {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT}; + } +} + +[[nodiscard]] VkImageLayout OptimalLayoutForRange(const VkImageSubresourceRange& range) noexcept { + if ((range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; +} +} // Anonymous namespace + void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { @@ -118,7 +162,6 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { } else { previous_layouts[i] = VK_IMAGE_LAYOUT_GENERAL; } - image_layout_cache[key] = framebuffer_layouts[i]; } Record([renderpass, framebuffer_handle, render_area, framebuffer_image_count, @@ -129,43 +172,24 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { VkPipelineStageFlags dst_stage_mask = 0; size_t barrier_count = 0; for (size_t i = 0; i < framebuffer_image_count; ++i) { - const VkImageLayout target_layout = framebuffer_layouts[i]; - if (target_layout == VK_IMAGE_LAYOUT_GENERAL || target_layout == VK_IMAGE_LAYOUT_UNDEFINED) { - continue; - } - const VkImageSubresourceRange& range = framebuffer_ranges[i]; + VkImageLayout target_layout = framebuffer_layouts[i]; + if (target_layout == VK_IMAGE_LAYOUT_UNDEFINED) { + target_layout = OptimalLayoutForRange(range); + } const VkImageLayout old_layout = previous_layouts[i]; if (old_layout == target_layout) { continue; } - VkAccessFlags dst_access = 0; - VkPipelineStageFlags dst_stage = 0; - - if (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - dst_access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dst_stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } - if (range.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - dst_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dst_stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - } - - VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - VkAccessFlags src_access = 0; - if (old_layout != VK_IMAGE_LAYOUT_UNDEFINED) { - src_stage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - src_access = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; - } + const StageAccessInfo src_info = StageAccessForLayout(old_layout, range.aspectMask); + const StageAccessInfo dst_info = StageAccessForLayout(target_layout, range.aspectMask); barriers[barrier_count++] = VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = src_access, - .dstAccessMask = dst_access, + .srcAccessMask = src_info.access, + .dstAccessMask = dst_info.access, .oldLayout = old_layout, .newLayout = target_layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -173,8 +197,8 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { .image = framebuffer_images[i], .subresourceRange = range, }; - src_stage_mask |= src_stage; - dst_stage_mask |= dst_stage; + src_stage_mask |= src_info.stage; + dst_stage_mask |= dst_info.stage; } if (barrier_count > 0) { @@ -205,6 +229,15 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { renderpass_images = framebuffer_images; renderpass_image_ranges = framebuffer_ranges; renderpass_image_layouts = framebuffer_layouts; + + for (size_t i = 0; i < framebuffer_image_count; ++i) { + VkImageLayout target_layout = framebuffer_layouts[i]; + if (target_layout == VK_IMAGE_LAYOUT_UNDEFINED) { + target_layout = OptimalLayoutForRange(framebuffer_ranges[i]); + } + image_layout_cache[ImageKey(framebuffer_images[i])] = target_layout; + renderpass_image_layouts[i] = target_layout; + } } void Scheduler::RequestOutsideRenderPassOperationContext() { @@ -367,62 +400,51 @@ void Scheduler::EndRenderPass(bool force_general) images = renderpass_images, ranges = renderpass_image_ranges, layouts = renderpass_image_layouts](vk::CommandBuffer cmdbuf) { - std::array barriers; + cmdbuf.EndRenderPass(); + + if (num_images == 0) { + return; + } + + std::array barriers{}; VkPipelineStageFlags src_stages = 0; + VkPipelineStageFlags dst_stages = 0; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; - const bool is_color = range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT; - const bool is_depth_stencil = range.aspectMask - & (VK_IMAGE_ASPECT_DEPTH_BIT - | VK_IMAGE_ASPECT_STENCIL_BIT); - - VkAccessFlags src_access = 0; - VkPipelineStageFlags this_stage = 0; - - if (is_color) { - src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - this_stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } - - if (is_depth_stencil) { - src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - this_stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT - | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + VkImageLayout old_layout = layouts[i]; + if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED) { + old_layout = OptimalLayoutForRange(range); } + constexpr VkImageLayout new_layout = VK_IMAGE_LAYOUT_GENERAL; - src_stages |= this_stage; - - const VkImageLayout render_layout = - layouts[i] != VK_IMAGE_LAYOUT_UNDEFINED ? layouts[i] : VK_IMAGE_LAYOUT_GENERAL; + const StageAccessInfo src_info = StageAccessForLayout(old_layout, range.aspectMask); + const StageAccessInfo dst_info = StageAccessForLayout(new_layout, range.aspectMask); barriers[i] = VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = src_access, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT - | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT - | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .oldLayout = render_layout, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = images[i], - .subresourceRange = range, + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_info.access, + .dstAccessMask = dst_info.access, + .oldLayout = old_layout, + .newLayout = new_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = images[i], + .subresourceRange = range, }; + src_stages |= src_info.stage; + dst_stages |= dst_info.stage; } - cmdbuf.EndRenderPass(); - - cmdbuf.PipelineBarrier(src_stages, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(src_stages != 0 ? src_stages + : static_cast(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT), + dst_stages != 0 ? dst_stages + : static_cast(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT), 0, {}, {}, - {barriers.data(), num_images} // Batched image barriers - ); + {barriers.data(), num_images}); }); for (size_t i = 0; i < num_renderpass_images; ++i) { @@ -433,11 +455,11 @@ void Scheduler::EndRenderPass(bool force_general) cmdbuf.EndRenderPass(); }); for (size_t i = 0; i < num_renderpass_images; ++i) { - const VkImageLayout render_layout = - renderpass_image_layouts[i] != VK_IMAGE_LAYOUT_UNDEFINED - ? renderpass_image_layouts[i] - : VK_IMAGE_LAYOUT_GENERAL; - image_layout_cache[ImageKey(renderpass_images[i])] = render_layout; + VkImageLayout layout = renderpass_image_layouts[i]; + if (layout == VK_IMAGE_LAYOUT_UNDEFINED) { + layout = OptimalLayoutForRange(renderpass_image_ranges[i]); + } + image_layout_cache[ImageKey(renderpass_images[i])] = layout; } } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index e8ded78cad..1e02f35c69 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -131,6 +131,10 @@ public: image_layout_cache[ImageKey(image)] = layout; } + void ClearImageLayoutTracking(VkImage image) noexcept { + image_layout_cache.erase(ImageKey(image)); + } + std::mutex submit_mutex; private: diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index b31eab5d72..2378e5d87e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -840,6 +840,8 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, nullptr, write_barriers); }); + scheduler.TrackImageLayout(src_image, VK_IMAGE_LAYOUT_GENERAL); + scheduler.TrackImageLayout(dst_image, VK_IMAGE_LAYOUT_GENERAL); } } // Anonymous namespace @@ -1072,6 +1074,8 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, {}, {}, post_barriers); }); + scheduler.TrackImageLayout(src_image, VK_IMAGE_LAYOUT_GENERAL); + scheduler.TrackImageLayout(dst_image, VK_IMAGE_LAYOUT_GENERAL); } void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, @@ -1480,6 +1484,8 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr, post_barriers); }); + scheduler.TrackImageLayout(src_image, VK_IMAGE_LAYOUT_GENERAL); + scheduler.TrackImageLayout(dst_image, VK_IMAGE_LAYOUT_GENERAL); } void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, @@ -1551,7 +1557,21 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {} -Image::~Image() = default; +Image::~Image() { + if (!scheduler) { + return; + } + + const auto drop_image = [this](const vk::Image& wrapper) { + if (!wrapper) { + return; + } + scheduler->ClearImageLayoutTracking(*wrapper); + }; + + drop_image(original_image); + drop_image(scaled_image); +} void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, std::span copies) { @@ -1604,6 +1624,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, keep = temp_wrapper](vk::CommandBuffer cmdbuf) { CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, VideoCommon::FixSmallVectorADL(vk_copies)); }); + scheduler->TrackImageLayout(temp_vk_image, VK_IMAGE_LAYOUT_GENERAL); // Use MSAACopyPass to convert from non-MSAA to MSAA std::vector image_copies; @@ -1643,6 +1664,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, vk_copies](vk::CommandBuffer cmdbuf) { CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, VideoCommon::FixSmallVectorADL(vk_copies)); }); + scheduler->TrackImageLayout(vk_image, VK_IMAGE_LAYOUT_GENERAL); if (is_rescaled) { ScaleUp(); @@ -1766,6 +1788,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, memory_write_barrier, nullptr, image_write_barrier); }); + scheduler->TrackImageLayout(*temp_wrapper.original_image, VK_IMAGE_LAYOUT_GENERAL); return; } } else { @@ -1833,6 +1856,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, memory_write_barrier, nullptr, image_write_barrier); }); + scheduler->TrackImageLayout(*original_image, VK_IMAGE_LAYOUT_GENERAL); } if (is_rescaled) {