diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 171315f39..74dd0398c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -492,6 +492,93 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, }); } +void TextureRuntime::ResolveTexture(Surface& surface) { + + scheduler.Record([width = surface.GetScaledWidth(), height = surface.GetScaledHeight(), + aspect = surface.Aspect(), access_flags = surface.AccessFlags(), + pipeline_state_flags = surface.PipelineStageFlags(), + msaa_image = surface.Image(Type::MultiSampled), + dest_image = surface.Image()](vk::CommandBuffer cmdbuf) { + const vk::ImageResolve resolve_area = { + .srcSubresource{ + .aspectMask = aspect, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffset = {}, + .dstSubresource{ + .aspectMask = aspect, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffset = {}, + .extent{ + .width = width, + .height = height, + .depth = 1, + }, + }; + + const vk::ImageSubresourceRange subresource_range = MakeSubresourceRange(aspect, 0); + + const std::array read_barriers = { + vk::ImageMemoryBarrier{ + .srcAccessMask = access_flags, + .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = msaa_image, + .subresourceRange = subresource_range, + }, + vk::ImageMemoryBarrier{ + .srcAccessMask = access_flags, + .dstAccessMask = vk::AccessFlagBits::eTransferWrite, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dest_image, + .subresourceRange = subresource_range, + }, + }; + const std::array write_barriers = { + vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eTransferRead, + .dstAccessMask = access_flags, + .oldLayout = vk::ImageLayout::eTransferSrcOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = msaa_image, + .subresourceRange = subresource_range, + }, + vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = access_flags, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dest_image, + .subresourceRange = subresource_range, + }, + }; + + cmdbuf.pipelineBarrier(pipeline_state_flags, vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers); + + cmdbuf.resolveImage(msaa_image, vk::ImageLayout::eTransferSrcOptimal, dest_image, + vk::ImageLayout::eTransferDstOptimal, resolve_area); + + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, pipeline_state_flags, + vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers); + }); +} + bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, std::span copies) { renderpass_cache.EndRendering(); @@ -604,6 +691,15 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, renderpass_cache.EndRendering(); + // Must resolve images first + // Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves + if (source.sample_count > 1) { + ResolveTexture(source); + } + if (dest.sample_count > 1) { + ResolveTexture(dest); + } + const RecordParams params = { .aspect = source.Aspect(), .filter = MakeFilter(source.pixel_format), @@ -614,96 +710,6 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, .dst_image = dest.Image(), }; - // Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves - const auto resolve_image = [&](const Surface& msaa_surface) { - scheduler.Record([width = msaa_surface.GetScaledWidth(), - height = msaa_surface.GetScaledHeight(), aspect = msaa_surface.Aspect(), - access_flags = msaa_surface.AccessFlags(), - pipeline_state_flags = msaa_surface.PipelineStageFlags(), - msaa_image = msaa_surface.Image(Type::MultiSampled), - dest_image = msaa_surface.Image()](vk::CommandBuffer cmdbuf) { - const vk::ImageResolve resolve_area = { - .srcSubresource{ - .aspectMask = aspect, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .srcOffset = {}, - .dstSubresource{ - .aspectMask = aspect, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .dstOffset = {}, - .extent{width, height, 1}, - }; - - const std::array read_barriers = { - vk::ImageMemoryBarrier{ - .srcAccessMask = access_flags, - .dstAccessMask = vk::AccessFlagBits::eTransferRead, - .oldLayout = vk::ImageLayout::eGeneral, - .newLayout = vk::ImageLayout::eTransferSrcOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = msaa_image, - .subresourceRange = MakeSubresourceRange(aspect, 0), - }, - vk::ImageMemoryBarrier{ - .srcAccessMask = access_flags, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, - .oldLayout = vk::ImageLayout::eGeneral, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dest_image, - .subresourceRange = MakeSubresourceRange(aspect, 0), - }, - }; - const std::array write_barriers = { - vk::ImageMemoryBarrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferRead, - .dstAccessMask = access_flags, - .oldLayout = vk::ImageLayout::eTransferSrcOptimal, - .newLayout = vk::ImageLayout::eGeneral, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = msaa_image, - .subresourceRange = MakeSubresourceRange(aspect, 0), - }, - vk::ImageMemoryBarrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = access_flags, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eGeneral, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dest_image, - .subresourceRange = MakeSubresourceRange(aspect, 0), - }, - }; - - cmdbuf.pipelineBarrier(pipeline_state_flags, vk::PipelineStageFlagBits::eTransfer, - vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers); - - cmdbuf.resolveImage(msaa_image, vk::ImageLayout::eTransferSrcOptimal, dest_image, - vk::ImageLayout::eTransferDstOptimal, resolve_area); - - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, pipeline_state_flags, - vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers); - }); - }; - - // Must resolve images first - if (source.sample_count > 1) { - resolve_image(source); - } - if (dest.sample_count > 1) { - resolve_image(dest); - } - scheduler.Record([params, blit](vk::CommandBuffer cmdbuf) { const std::array source_offsets = { vk::Offset3D{static_cast(blit.src_rect.left), @@ -833,7 +839,6 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param scheduler{runtime_.GetScheduler()}, traits{instance.GetTraits(pixel_format)}, handles{Handle(instance), Handle(instance), Handle(instance), Handle(instance), Handle(instance)} { - if (pixel_format == VideoCore::PixelFormat::Invalid || !traits.transfer_support) { return; } diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index 18bd7cafa..7bffdd373 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -163,6 +163,9 @@ private: /// Clears a partial texture rect using a clear rectangle void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear); + /// Resolves the multi-sampled texture of a surface, if available, into the current texture + void ResolveTexture(Surface& surface); + private: const Instance& instance; Scheduler& scheduler;