From d7bf139e8513e8839fff28b11e949a9e299f8203 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Wed, 1 Feb 2023 22:31:37 +0200 Subject: [PATCH] renderer_vulkan: Add pipeline barriers for attachments --- .../renderer_vulkan/renderer_vulkan.cpp | 102 +++++----- .../renderer_vulkan/vk_blit_helper.cpp | 14 +- .../vk_format_reinterpreter.cpp | 10 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 3 +- .../renderer_vulkan/vk_rasterizer.cpp | 28 +-- .../renderer_vulkan/vk_renderpass_cache.cpp | 174 ++++++++++++------ .../renderer_vulkan/vk_renderpass_cache.h | 35 ++-- .../renderer_vulkan/vk_texture_mailbox.cpp | 43 ++++- .../renderer_vulkan/vk_texture_runtime.cpp | 2 +- .../renderer_vulkan/vk_texture_runtime.h | 32 ++-- 10 files changed, 269 insertions(+), 174 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index a2b18c98d..2c02794dd 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -28,7 +28,6 @@ #include MICROPROFILE_DEFINE(Vulkan_RenderFrame, "Vulkan", "Render Frame", MP_RGB(128, 128, 64)); -MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128)); MICROPROFILE_DEFINE(Vulkan_SwapchainCopy, "Vulkan", "Swapchain Copy", MP_RGB(64, 64, 0)); namespace Vulkan { @@ -195,72 +194,57 @@ void RendererVulkan::PrepareRendertarget() { void RendererVulkan::RenderToMailbox(const Layout::FramebufferLayout& layout, std::unique_ptr& mailbox, bool flipped) { - const vk::Device device = instance.GetDevice(); - Frontend::Frame* frame; - { - MICROPROFILE_SCOPE(Vulkan_WaitPresent); - frame = mailbox->GetRenderFrame(); + Frontend::Frame* frame = mailbox->GetRenderFrame(); + MICROPROFILE_SCOPE(Vulkan_RenderFrame); - std::scoped_lock lock{frame->fence_mutex}; - [[maybe_unused]] vk::Result result = - device.waitForFences(frame->present_done, false, std::numeric_limits::max()); - device.resetFences(frame->present_done); + const auto [width, height] = swapchain.GetExtent(); + if (width != frame->width || height != frame->height) { + mailbox->ReloadRenderFrame(frame, width, height); } - { - MICROPROFILE_SCOPE(Vulkan_RenderFrame); + scheduler.Record([layout](vk::CommandBuffer cmdbuf) { + const vk::Viewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = static_cast(layout.width), + .height = static_cast(layout.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; - const auto [width, height] = swapchain.GetExtent(); - if (width != frame->width || height != frame->height) { - mailbox->ReloadRenderFrame(frame, width, height); - } + const vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = {layout.width, layout.height}, + }; - scheduler.Record([layout](vk::CommandBuffer cmdbuf) { - const vk::Viewport viewport = { - .x = 0.0f, - .y = 0.0f, - .width = static_cast(layout.width), - .height = static_cast(layout.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; + cmdbuf.setViewport(0, viewport); + cmdbuf.setScissor(0, scissor); + }); - const vk::Rect2D scissor = { - .offset = {0, 0}, - .extent = {layout.width, layout.height}, - }; + renderpass_cache.ExitRenderpass(); + scheduler.Record([this, framebuffer = frame->framebuffer, width = frame->width, + height = frame->height](vk::CommandBuffer cmdbuf) { + const vk::ClearValue clear{.color = clear_color}; + const vk::RenderPassBeginInfo renderpass_begin_info = { + .renderPass = renderpass_cache.GetPresentRenderpass(), + .framebuffer = framebuffer, + .renderArea = + vk::Rect2D{ + .offset = {0, 0}, + .extent = {width, height}, + }, + .clearValueCount = 1, + .pClearValues = &clear, + }; - cmdbuf.setViewport(0, viewport); - cmdbuf.setScissor(0, scissor); - }); + cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); + }); - renderpass_cache.ExitRenderpass(); + DrawScreens(layout, flipped); - scheduler.Record([this, framebuffer = frame->framebuffer, width = frame->width, - height = frame->height](vk::CommandBuffer cmdbuf) { - const vk::ClearValue clear{.color = clear_color}; - const vk::RenderPassBeginInfo renderpass_begin_info = { - .renderPass = renderpass_cache.GetPresentRenderpass(), - .framebuffer = framebuffer, - .renderArea = - vk::Rect2D{ - .offset = {0, 0}, - .extent = {width, height}, - }, - .clearValueCount = 1, - .pClearValues = &clear, - }; - - cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); - }); - - DrawScreens(layout, flipped); - - scheduler.Flush(frame->render_ready); - scheduler.Record( - [&mailbox, frame](vk::CommandBuffer) { mailbox->ReleaseRenderFrame(frame); }); - scheduler.DispatchWork(); - } + scheduler.Flush(frame->render_ready); + scheduler.Record([&mailbox, frame](vk::CommandBuffer) { mailbox->ReleaseRenderFrame(frame); }); + scheduler.DispatchWork(); } void RendererVulkan::BeginRendering() { @@ -1100,7 +1084,7 @@ void RendererVulkan::TryPresent(int timeout_ms, bool is_secondary) { cmdbuf.end(); static constexpr std::array wait_stage_masks = { - vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, }; diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.cpp b/src/video_core/renderer_vulkan/vk_blit_helper.cpp index abd8401d8..43e0178ed 100644 --- a/src/video_core/renderer_vulkan/vk_blit_helper.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_helper.cpp @@ -318,12 +318,12 @@ bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest, const std::array textures = { vk::DescriptorImageInfo{ .sampler = nearest_sampler, - .imageView = source.GetDepthView(), + .imageView = source.DepthView(), .imageLayout = vk::ImageLayout::eGeneral, }, vk::DescriptorImageInfo{ .sampler = nearest_sampler, - .imageView = source.GetStencilView(), + .imageView = source.StencilView(), .imageLayout = vk::ImageLayout::eGeneral, }, }; @@ -348,15 +348,15 @@ void BlitHelper::BlitD24S8ToR32(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) { const std::array textures = { vk::DescriptorImageInfo{ - .imageView = source.GetDepthView(), + .imageView = source.DepthView(), .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal, }, vk::DescriptorImageInfo{ - .imageView = source.GetStencilView(), + .imageView = source.StencilView(), .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal, }, vk::DescriptorImageInfo{ - .imageView = dest.GetImageView(), + .imageView = dest.ImageView(), .imageLayout = vk::ImageLayout::eGeneral, }, }; @@ -365,8 +365,8 @@ void BlitHelper::BlitD24S8ToR32(Surface& source, Surface& dest, device.updateDescriptorSetWithTemplate(set, compute_update_template, textures[0]); renderpass_cache.ExitRenderpass(); - scheduler.Record([this, set, blit, src_image = source.alloc.image, - dst_image = dest.alloc.image](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, set, blit, src_image = source.Image(), + dst_image = dest.Image()](vk::CommandBuffer cmdbuf) { const std::array pre_barriers = { vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite, diff --git a/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp b/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp index e99ee48c1..49d66f829 100644 --- a/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp +++ b/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp @@ -147,15 +147,15 @@ void D24S8toRGBA8::Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surf VideoCore::Rect2D dst_rect) { const std::array textures = { vk::DescriptorImageInfo{ - .imageView = source.GetDepthView(), + .imageView = source.DepthView(), .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal, }, vk::DescriptorImageInfo{ - .imageView = source.GetStencilView(), + .imageView = source.StencilView(), .imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal, }, vk::DescriptorImageInfo{ - .imageView = dest.GetImageView(), + .imageView = dest.ImageView(), .imageLayout = vk::ImageLayout::eGeneral, }, }; @@ -164,8 +164,8 @@ void D24S8toRGBA8::Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surf device.updateDescriptorSetWithTemplate(set, update_template, textures[0]); runtime.GetRenderpassCache().ExitRenderpass(); - scheduler.Record([this, set, src_rect, src_image = source.alloc.image, - dst_image = dest.alloc.image](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, set, src_rect, src_image = source.Image(), + dst_image = dest.Image()](vk::CommandBuffer cmdbuf) { const std::array pre_barriers = { vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 6b80aaee8..6c21cbc50 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -607,7 +607,8 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) { const vk::Device device = instance.GetDevice(); // When using SPIR-V emit the fragment shader on the main thread - // since it's quite fast. This also heavily reduces flicker + // since it's quite fast. This also heavily reduces flicker when + // using asychronous shader compilation if (emit_spirv) { const std::vector code = GenerateFragmentShaderSPV(config); shader.module = CompileSPV(code, device); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5073ba1e7..cbdd5c493 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -123,12 +123,12 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan pipeline_cache.BindTexelBuffer(4, texture_rgba_view); for (u32 i = 0; i < 4; i++) { - pipeline_cache.BindTexture(i, null_surface.GetImageView()); + pipeline_cache.BindTexture(i, null_surface.ImageView()); pipeline_cache.BindSampler(i, default_sampler); } for (u32 i = 0; i < 7; i++) { - pipeline_cache.BindStorageImage(i, null_storage_surface.GetImageView()); + pipeline_cache.BindStorageImage(i, null_storage_surface.ImageView()); } // Explicitly call the derived version to avoid warnings about calling virtual @@ -542,9 +542,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { const u32 binding = static_cast(face); if (surface) { - pipeline_cache.BindStorageImage(binding, surface->GetImageView()); + pipeline_cache.BindStorageImage(binding, surface->ImageView()); } else { - pipeline_cache.BindStorageImage(binding, null_storage_surface.GetImageView()); + pipeline_cache.BindStorageImage(binding, null_storage_surface.ImageView()); } }; @@ -585,9 +585,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { case TextureType::Shadow2D: { auto surface = res_cache.GetTextureSurface(texture); if (surface) { - pipeline_cache.BindStorageImage(0, surface->GetStorageView()); + pipeline_cache.BindStorageImage(0, surface->StorageView()); } else { - pipeline_cache.BindStorageImage(0, null_storage_surface.GetImageView()); + pipeline_cache.BindStorageImage(0, null_storage_surface.ImageView()); } continue; } @@ -617,9 +617,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { auto surface = res_cache.GetTextureCube(config); if (surface) { - pipeline_cache.BindTexture(3, surface->GetImageView()); + pipeline_cache.BindTexture(3, surface->ImageView()); } else { - pipeline_cache.BindTexture(3, null_surface.GetImageView()); + pipeline_cache.BindTexture(3, null_surface.ImageView()); } BindSampler(3, texture_cube_sampler, texture.config); @@ -635,7 +635,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { auto surface = res_cache.GetTextureSurface(texture); if (surface) { - if (color_surface && color_surface->GetImageView() == surface->GetImageView()) { + if (color_surface && color_surface->ImageView() == surface->ImageView()) { Surface temp{*color_surface, runtime}; const VideoCore::TextureCopy copy = { .src_level = 0, @@ -647,9 +647,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { .extent = VideoCore::Extent{temp.GetScaledWidth(), temp.GetScaledHeight()}}; runtime.CopyTextures(*color_surface, temp, copy); - pipeline_cache.BindTexture(texture_index, temp.GetImageView()); + pipeline_cache.BindTexture(texture_index, temp.ImageView()); } else { - pipeline_cache.BindTexture(texture_index, surface->GetImageView()); + pipeline_cache.BindTexture(texture_index, surface->ImageView()); } } else { @@ -660,10 +660,10 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { // the geometry in question. // For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn // on the male character's face, which in the OpenGL default appear black. - pipeline_cache.BindTexture(texture_index, null_surface.GetImageView()); + pipeline_cache.BindTexture(texture_index, null_surface.ImageView()); } } else { - pipeline_cache.BindTexture(texture_index, null_surface.GetImageView()); + pipeline_cache.BindTexture(texture_index, null_surface.ImageView()); pipeline_cache.BindSampler(texture_index, default_sampler); } } @@ -1016,7 +1016,7 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width, (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width); - screen_info.image_view = src_surface->GetImageView(); + screen_info.image_view = src_surface->ImageView(); return true; } diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp index 35983ae36..e3f5f8436 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -48,6 +48,7 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth u32 height = UINT32_MAX; u32 cursor = 0; std::array formats{}; + std::array images{}; std::array views{}; const auto Prepare = [&](Surface* const surface) { @@ -59,12 +60,37 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth width = std::min(width, surface->GetScaledWidth()); height = std::min(height, surface->GetScaledHeight()); formats[cursor] = surface->pixel_format; - views[cursor++] = surface->GetFramebufferView(); + images[cursor] = surface->Image(); + views[cursor++] = surface->FramebufferView(); }; Prepare(color); Prepare(depth_stencil); + const RenderingInfo new_info = { + .color = + RenderTarget{ + .aspect = vk::ImageAspectFlagBits::eColor, + .image = images[0], + .image_view = views[0], + }, + .depth = + RenderTarget{ + .aspect = depth_stencil ? depth_stencil->Aspect() : vk::ImageAspectFlagBits::eDepth, + .image = images[1], + .image_view = views[1], + }, + .render_area = render_area, + .clear = clear, + .do_clear = do_clear, + }; + + const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); + if (info == new_info && rendering && !is_dirty) { + cmd_count++; + return; + } + const vk::RenderPass renderpass = GetRenderpass(formats[0], formats[1], do_clear); const FramebufferInfo framebuffer_info = { @@ -79,38 +105,24 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth it->second = CreateFramebuffer(framebuffer_info, renderpass); } - const RenderpassState new_state = { - .renderpass = renderpass, - .framebuffer = it->second, - .render_area = render_area, - .clear = clear, - }; - - const u64 new_state_hash = Common::ComputeStructHash64(new_state); - const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); - if (state_hash == new_state_hash && rendering && !is_dirty) { - cmd_count++; - return; - } - if (rendering) { ExitRenderpass(); } + scheduler.Record( + [render_area, clear, renderpass, framebuffer = it->second](vk::CommandBuffer cmdbuf) { + const vk::RenderPassBeginInfo renderpass_begin_info = { + .renderPass = renderpass, + .framebuffer = framebuffer, + .renderArea = render_area, + .clearValueCount = 1, + .pClearValues = &clear, + }; - scheduler.Record([new_state](vk::CommandBuffer cmdbuf) { - const vk::RenderPassBeginInfo renderpass_begin_info = { - .renderPass = new_state.renderpass, - .framebuffer = new_state.framebuffer, - .renderArea = new_state.render_area, - .clearValueCount = 1, - .pClearValues = &new_state.clear, - }; - - cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); - }); + cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); + }); scheduler.MarkStateNonDirty(StateFlags::Renderpass); - state_hash = new_state_hash; + info = new_info; rendering = true; } @@ -120,13 +132,65 @@ void RenderpassCache::ExitRenderpass() { } rendering = false; - scheduler.Record([dynamic_rendering = dynamic_rendering](vk::CommandBuffer cmdbuf) { - if (dynamic_rendering) { - cmdbuf.endRenderingKHR(); - } else { - cmdbuf.endRenderPass(); - } - }); + scheduler.Record( + [info = info, dynamic_rendering = dynamic_rendering](vk::CommandBuffer cmdbuf) { + u32 num_barriers = 0; + std::array barriers; + vk::PipelineStageFlags src_stage{}; + vk::PipelineStageFlags dst_stage{}; + + if (info.color) { + barriers[num_barriers++] = vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = info.color.image, + .subresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + + src_stage |= vk::PipelineStageFlagBits::eColorAttachmentOutput; + dst_stage |= vk::PipelineStageFlagBits::eFragmentShader; + } + if (info.depth) { + barriers[num_barriers++] = vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite, + .dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | + vk::AccessFlagBits::eDepthStencilAttachmentWrite, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = info.depth.image, + .subresourceRange{ + .aspectMask = info.depth.aspect, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + + src_stage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | + vk::PipelineStageFlagBits::eLateFragmentTests; + dst_stage |= vk::PipelineStageFlagBits::eLateFragmentTests; + } + if (dynamic_rendering) { + cmdbuf.endRenderingKHR(); + } else { + cmdbuf.endRenderPass(); + } + cmdbuf.pipelineBarrier(src_stage, dst_stage, vk::DependencyFlagBits::eByRegion, 0, + nullptr, 0, nullptr, num_barriers, barriers.data()); + }); // The Mali guide recommends flushing at the end of each major renderpass // Testing has shown this has a significant effect on rendering performance @@ -138,33 +202,40 @@ void RenderpassCache::ExitRenderpass() { void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_stencil, vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) { - RenderingState new_state = { + RenderingInfo new_info = { .render_area = render_area, .clear = clear, .do_clear = do_clear, }; if (color) { - new_state.color_view = color->GetFramebufferView(); + new_info.color = RenderTarget{ + .aspect = vk::ImageAspectFlagBits::eColor, + .image = color->Image(), + .image_view = color->FramebufferView(), + }; } if (depth_stencil) { - new_state.depth_view = depth_stencil->GetFramebufferView(); + new_info.depth = RenderTarget{ + .aspect = depth_stencil->Aspect(), + .image = depth_stencil->Image(), + .image_view = depth_stencil->FramebufferView(), + }; } - const u64 new_state_hash = Common::ComputeStructHash64(new_state); const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); - if (state_hash == new_state_hash && rendering && !is_dirty) { + if (info == new_info && rendering && !is_dirty) { cmd_count++; return; } + const bool has_stencil = + depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil; + if (rendering) { ExitRenderpass(); } - - const bool has_stencil = - depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil; - scheduler.Record([new_state, has_stencil](vk::CommandBuffer cmdbuf) { + scheduler.Record([new_info, has_stencil](vk::CommandBuffer cmdbuf) { u32 cursor = 0; std::array infos{}; @@ -178,21 +249,20 @@ void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_ .imageView = image_view, .imageLayout = vk::ImageLayout::eGeneral, .loadOp = - new_state.do_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + new_info.do_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, .storeOp = vk::AttachmentStoreOp::eStore, - .clearValue = new_state.clear, + .clearValue = new_info.clear, }; }; - Prepare(new_state.color_view); - Prepare(new_state.depth_view); + Prepare(new_info.color.image_view); + Prepare(new_info.depth.image_view); - const u32 color_attachment_count = new_state.color_view ? 1u : 0u; - const vk::RenderingAttachmentInfoKHR* depth_info = - new_state.depth_view ? &infos[1] : nullptr; + const u32 color_attachment_count = new_info.color ? 1u : 0u; + const vk::RenderingAttachmentInfoKHR* depth_info = new_info.depth ? &infos[1] : nullptr; const vk::RenderingAttachmentInfoKHR* stencil_info = has_stencil ? &infos[1] : nullptr; const vk::RenderingInfoKHR rendering_info = { - .renderArea = new_state.render_area, + .renderArea = new_info.render_area, .layerCount = 1, .colorAttachmentCount = color_attachment_count, .pColorAttachments = &infos[0], @@ -204,7 +274,7 @@ void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_ }); scheduler.MarkStateNonDirty(StateFlags::Renderpass); - state_hash = new_state_hash; + info = new_info; rendering = true; } diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.h b/src/video_core/renderer_vulkan/vk_renderpass_cache.h index 0648afb3e..215991c44 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.h +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.h @@ -40,8 +40,8 @@ struct hash { namespace Vulkan { class RenderpassCache { - static constexpr u32 MAX_COLOR_FORMATS = 5; - static constexpr u32 MAX_DEPTH_FORMATS = 4; + static constexpr std::size_t MAX_COLOR_FORMATS = 5; + static constexpr std::size_t MAX_DEPTH_FORMATS = 4; public: RenderpassCache(const Instance& instance, Scheduler& scheduler); @@ -80,26 +80,31 @@ private: vk::Framebuffer CreateFramebuffer(const FramebufferInfo& info, vk::RenderPass renderpass); private: - struct RenderpassState { - vk::RenderPass renderpass; - vk::Framebuffer framebuffer; - vk::Rect2D render_area; - vk::ClearValue clear; + struct RenderTarget { + vk::ImageAspectFlags aspect; + vk::Image image; + vk::ImageView image_view; - [[nodiscard]] bool operator==(const RenderpassState& other) const { - return std::memcmp(this, &other, sizeof(RenderpassState)) == 0; + operator bool() const noexcept { + return image; + } + + [[nodiscard]] bool operator==(const RenderTarget& other) const { + return image_view == other.image_view; } }; - struct RenderingState { - vk::ImageView color_view; - vk::ImageView depth_view; + struct RenderingInfo { + RenderTarget color; + RenderTarget depth; vk::Rect2D render_area; vk::ClearValue clear; bool do_clear; - [[nodiscard]] bool operator==(const RenderpassState& other) const { - return std::memcmp(this, &other, sizeof(RenderpassState)) == 0; + [[nodiscard]] bool operator==(const RenderingInfo& other) const { + return color == other.color && depth == other.depth && + render_area == other.render_area && do_clear == other.do_clear && + std::memcmp(&clear, &other.clear, sizeof(vk::ClearValue)) == 0; } }; @@ -108,10 +113,10 @@ private: vk::RenderPass present_renderpass{}; vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2]; std::unordered_map framebuffers; + RenderingInfo info{}; bool rendering = false; bool dynamic_rendering = false; u32 cmd_count{}; - u64 state_hash{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_mailbox.cpp b/src/video_core/renderer_vulkan/vk_texture_mailbox.cpp index acfe59fae..35aed768f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_mailbox.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_mailbox.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/microprofile.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_renderpass_cache.h" #include "video_core/renderer_vulkan/vk_swapchain.h" @@ -9,6 +10,8 @@ #include +MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128)); + namespace Vulkan { TextureMailbox::TextureMailbox(const Instance& instance_, const Swapchain& swapchain_, @@ -129,14 +132,44 @@ void TextureMailbox::ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 he } Frontend::Frame* TextureMailbox::GetRenderFrame() { - std::unique_lock lock{free_mutex}; + MICROPROFILE_SCOPE(Vulkan_WaitPresent); - if (free_queue.empty()) { - free_cv.wait(lock, [&] { return !free_queue.empty(); }); + Frontend::Frame* frame{}; + { + std::unique_lock lock{free_mutex}; + if (free_queue.empty()) { + free_cv.wait(lock, [&] { return !free_queue.empty(); }); + } + + frame = free_queue.front(); + free_queue.pop(); } - Frontend::Frame* frame = free_queue.front(); - free_queue.pop(); + std::scoped_lock lock{frame->fence_mutex}; + + vk::Device device = instance.GetDevice(); + vk::Result result{}; + + const auto Wait = [&]() { + result = device.waitForFences(frame->present_done, false, std::numeric_limits::max()); + return result; + }; + + while (Wait() != vk::Result::eSuccess) { + // Retry if the waiting time out + if (result == vk::Result::eTimeout) { + continue; + } + + // eErrorInitializationFailed occurs on Mali GPU drivers due to them + // using the ppoll() syscall which isn't correctly restarted after a signal, + // we need to manually retry waiting in that case + if (result == vk::Result::eErrorInitializationFailed) { + continue; + } + } + + device.resetFences(frame->present_done); return frame; } diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 6f0e42262..000f0c90f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -935,7 +935,7 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi // to avoid having the interleave the data later. These should(?) be // uncommon anyways and the perf hit is very small if (type == VideoCore::SurfaceType::DepthStencil) { - return DepthStencilDownload(download, staging); + return /*DepthStencilDownload(download, staging)*/; } const bool is_scaled = res_scale != 1; diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index cf9dda18e..a37a842e9 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -162,7 +162,6 @@ private: class Surface : public VideoCore::SurfaceBase { friend class TextureRuntime; - friend class RasterizerVulkan; public: Surface(TextureRuntime& runtime); @@ -186,29 +185,39 @@ public: /// Returns the pipeline stage flags indicative of the surface vk::PipelineStageFlags PipelineStageFlags() const noexcept; + /// Returns the surface aspect + vk::ImageAspectFlags Aspect() const noexcept { + return alloc.aspect; + } + + /// Returns the surface image handle + vk::Image Image() const noexcept { + return alloc.image; + } + /// Returns an image view used to sample the surface from a shader - vk::ImageView GetImageView() const noexcept { + vk::ImageView ImageView() const noexcept { return alloc.image_view; } /// Returns an image view used to create a framebuffer - vk::ImageView GetFramebufferView() noexcept { + vk::ImageView FramebufferView() noexcept { is_framebuffer = true; return alloc.base_view; } /// Returns the depth only image view of the surface, null otherwise - vk::ImageView GetDepthView() const noexcept { + vk::ImageView DepthView() const noexcept { return alloc.depth_view; } /// Returns the stencil only image view of the surface, null otherwise - vk::ImageView GetStencilView() const noexcept { + vk::ImageView StencilView() const noexcept { return alloc.stencil_view; } /// Returns the R32 image view used for atomic load/store - vk::ImageView GetStorageView() noexcept { + vk::ImageView StorageView() noexcept { if (!alloc.storage_view) { LOG_CRITICAL(Render_Vulkan, "Surface with pixel format {} and internal format {} " @@ -220,11 +229,6 @@ public: return alloc.storage_view; } - /// Returns the internal format of the allocated texture - vk::Format GetInternalFormat() const noexcept { - return alloc.format; - } - private: /// Uploads pixel data to scaled texture void ScaledUpload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging); @@ -240,12 +244,10 @@ private: TextureRuntime& runtime; const Instance& instance; Scheduler& scheduler; - -public: - bool is_framebuffer{}; - bool is_storage{}; ImageAlloc alloc; FormatTraits traits; + bool is_framebuffer{}; + bool is_storage{}; }; struct Traits {