aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp430
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h57
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp134
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp124
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp52
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp106
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h23
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp468
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h48
14 files changed, 1196 insertions, 320 deletions
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index d99e528bc0..89083d29fb 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -8,14 +8,20 @@
#include "video_core/renderer_vulkan/vk_texture_cache.h"
+#include "common/div_ceil.h"
#include "common/settings.h"
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
+#include "video_core/host_shaders/blit_color_msaa_frag_spv.h"
+#include "video_core/host_shaders/blit_depth_msaa_frag_spv.h"
+#include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/convert_abgr8_to_d32f_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_d32f_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
+#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
+#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -24,11 +30,13 @@
#include "video_core/host_shaders/vulkan_depthstencil_clear_frag_spv.h"
#include "video_core/renderer_vulkan/blit_image.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
+#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/surface.h"
+#include "video_core/texture_cache/samples_helper.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -74,6 +82,12 @@ struct PushConstants {
std::array<float, 2> tex_offset;
};
+struct MSAACopyPushConstants {
+ std::array<s32, 2> dst_offset;
+ std::array<s32, 2> src_offset;
+ std::array<s32, 2> scale;
+};
+
template <u32 binding>
inline constexpr VkDescriptorSetLayoutBinding TEXTURE_DESCRIPTOR_SET_LAYOUT_BINDING{
.binding = binding,
@@ -241,6 +255,21 @@ constexpr VkPipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_STENCIL_STATE_CRE
.maxDepthBounds = 0.0f,
};
+constexpr VkPipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .depthTestEnable = VK_TRUE,
+ .depthWriteEnable = VK_TRUE,
+ .depthCompareOp = VK_COMPARE_OP_ALWAYS,
+ .depthBoundsTestEnable = VK_FALSE,
+ .stencilTestEnable = VK_FALSE,
+ .front = VkStencilOpState{},
+ .back = VkStencilOpState{},
+ .minDepthBounds = 0.0f,
+ .maxDepthBounds = 0.0f,
+};
+
template <VkFilter filter>
inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
@@ -474,6 +503,46 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
});
}
+[[nodiscard]] VkSampleCountFlagBits SampleCountFlag(u32 num_samples) {
+ switch (num_samples) {
+ case 2:
+ return VK_SAMPLE_COUNT_2_BIT;
+ case 4:
+ return VK_SAMPLE_COUNT_4_BIT;
+ case 8:
+ return VK_SAMPLE_COUNT_8_BIT;
+ case 16:
+ return VK_SAMPLE_COUNT_16_BIT;
+ default:
+ return VK_SAMPLE_COUNT_1_BIT;
+ }
+}
+
+[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
+ VkFormat format, u32 base_level) {
+ return device.CreateImageView(VkImageViewCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .image = image,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = format,
+ .components{
+ .r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ },
+ .subresourceRange{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = base_level,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ });
+}
+
void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
const VkRenderPass render_pass = framebuffer->RenderPass();
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
@@ -514,11 +583,19 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
clear_color_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))),
+ msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
+ one_texture_set_layout.address(),
+ PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
+ blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
: vk::ShaderModule{}),
+ blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)),
+ blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported()
+ ? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV)
+ : vk::ShaderModule{}),
clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
clear_stencil_frag(BuildShader(device, VULKAN_DEPTHSTENCIL_CLEAR_FRAG_SPV)),
@@ -531,6 +608,8 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)),
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
+ convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
+ convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -591,6 +670,70 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
});
}
+void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
+ const ImageView& src_image_view, const Region2D& dst_region,
+ const Region2D& src_region) {
+ const BlitMSAAPipelineKey key{
+ .renderpass = dst_framebuffer->RenderPass(),
+ .samples = dst_framebuffer->Samples(),
+ };
+ const VkPipelineLayout layout = *one_texture_pipeline_layout;
+ const VkSampler sampler = *nearest_sampler;
+ const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
+ const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
+
+ RecordShaderReadBarrier(scheduler, src_image_view);
+ scheduler.RequestRenderpass(dst_framebuffer);
+ scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
+ src_view](vk::CommandBuffer cmdbuf) {
+ const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
+ UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ BindBlitState(cmdbuf, layout, dst_region, src_region);
+ cmdbuf.Draw(3, 1, 0, 0);
+ });
+ scheduler.InvalidateState();
+}
+
+void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
+ ImageView& src_image_view, const Region2D& dst_region,
+ const Region2D& src_region) {
+ const bool resolve_stencil =
+ dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
+ const VkPipeline pipeline =
+ FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
+ const VkPipelineLayout layout =
+ resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
+ const VkSampler sampler = *nearest_sampler;
+ const VkImageView src_depth_view = src_image_view.DepthView();
+ const VkImageView src_stencil_view =
+ resolve_stencil ? src_image_view.StencilView() : VK_NULL_HANDLE;
+
+ RecordShaderReadBarrier(scheduler, src_image_view);
+ scheduler.RequestRenderpass(dst_framebuffer);
+ scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_depth_view,
+ src_stencil_view, resolve_stencil](vk::CommandBuffer cmdbuf) {
+ if (resolve_stencil) {
+ const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
+ UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
+ src_stencil_view);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ } else {
+ const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
+ UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ }
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ BindBlitState(cmdbuf, layout, dst_region, src_region);
+ cmdbuf.Draw(3, 1, 0, 0);
+ });
+ scheduler.InvalidateState();
+}
+
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region,
@@ -739,6 +882,174 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
scheduler.InvalidateState();
}
+void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
+ VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
+ VideoCore::Surface::PixelFormat src_format, u32 num_samples,
+ std::span<const VideoCommon::ImageCopy> copies,
+ bool msaa_to_non_msaa) {
+ while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
+ msaa_copy_resources.pop_front();
+ }
+ const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
+ const s32 scale_x = 1 << samples_x;
+ const s32 scale_y = 1 << samples_y;
+ const VkSampleCountFlagBits samples =
+ msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
+ RenderPassKey renderpass_key{};
+ renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
+ renderpass_key.color_formats[0] = dst_format;
+ renderpass_key.depth_format = VideoCore::Surface::PixelFormat::Invalid;
+ renderpass_key.samples = samples;
+ const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
+ const MSAACopyPipelineKey key{
+ .renderpass = renderpass,
+ .samples = samples,
+ .msaa_to_non_msaa = msaa_to_non_msaa,
+ };
+ const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key);
+ const VkPipelineLayout layout = *msaa_copy_pipeline_layout;
+ const VkSampler sampler = *nearest_sampler;
+ const VkFormat src_vk_format =
+ MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
+ const VkFormat dst_vk_format =
+ MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
+ for (const VideoCommon::ImageCopy& copy : copies) {
+ ASSERT(copy.src_subresource.base_layer == 0);
+ ASSERT(copy.src_subresource.num_layers == 1);
+ ASSERT(copy.dst_subresource.base_layer == 0);
+ ASSERT(copy.dst_subresource.num_layers == 1);
+ vk::ImageView src_view =
+ MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
+ static_cast<u32>(copy.src_subresource.base_level));
+ vk::ImageView dst_view =
+ MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
+ static_cast<u32>(copy.dst_subresource.base_level));
+ const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
+ const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
+ const VkRect2D render_area{
+ .offset = dst_offset,
+ .extent = dst_extent,
+ };
+ vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .renderPass = renderpass,
+ .attachmentCount = 1,
+ .pAttachments = dst_view.address(),
+ .width = static_cast<u32>(dst_offset.x) + dst_extent.width,
+ .height = static_cast<u32>(dst_offset.y) + dst_extent.height,
+ .layers = 1,
+ });
+ const MSAACopyPushConstants push_constants{
+ .dst_offset = {dst_offset.x, dst_offset.y},
+ .src_offset = {copy.src_offset.x, copy.src_offset.y},
+ .scale = {scale_x, scale_y},
+ };
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([this, pipeline, layout, sampler, renderpass,
+ framebuffer_handle = *framebuffer, src_view_handle = *src_view,
+ src = src_image, dst = dst_image, render_area,
+ push_constants](vk::CommandBuffer cmdbuf) {
+ constexpr VkImageSubresourceRange color_range{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ };
+ const std::array pre_barriers{
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = src,
+ .subresourceRange = color_range,
+ },
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst,
+ .subresourceRange = color_range,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ 0, nullptr, nullptr, pre_barriers);
+ const VkRenderPassBeginInfo renderpass_bi{
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .pNext = nullptr,
+ .renderPass = renderpass,
+ .framebuffer = framebuffer_handle,
+ .renderArea = render_area,
+ .clearValueCount = 0,
+ .pClearValues = nullptr,
+ };
+ cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
+ const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
+ UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ const VkViewport viewport{
+ .x = static_cast<float>(render_area.offset.x),
+ .y = static_cast<float>(render_area.offset.y),
+ .width = static_cast<float>(render_area.extent.width),
+ .height = static_cast<float>(render_area.extent.height),
+ .minDepth = 0.0f,
+ .maxDepth = 1.0f,
+ };
+ cmdbuf.SetViewport(0, viewport);
+ cmdbuf.SetScissor(0, render_area);
+ cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
+ cmdbuf.Draw(3, 1, 0, 0);
+ cmdbuf.EndRenderPass();
+ const VkImageMemoryBarrier post_barrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst,
+ .subresourceRange = color_range,
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, post_barrier);
+ });
+ msaa_copy_resources.push_back(MSAACopyResources{
+ .tick = scheduler.CurrentTick(),
+ .src_view = std::move(src_view),
+ .dst_view = std::move(dst_view),
+ .framebuffer = std::move(framebuffer),
+ });
+ }
+ scheduler.InvalidateState();
+}
+
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -905,7 +1216,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
- .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
+ .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *two_textures_pipeline_layout,
.renderPass = key.renderpass,
@@ -1025,14 +1336,63 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
return *clear_stencil_pipelines.back();
}
-void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
- if (pipeline) {
- return;
+VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key) {
+ const auto it = std::ranges::find(blit_msaa_color_keys, key);
+ if (it != blit_msaa_color_keys.end()) {
+ return *blit_msaa_color_pipelines[std::distance(blit_msaa_color_keys.begin(), it)];
}
- VkShaderModule frag_shader = *convert_float_to_depth_frag;
- const std::array stages = MakeStages(*full_screen_vert, frag_shader);
+ blit_msaa_color_keys.push_back(key);
+ const std::array stages = MakeStages(*full_screen_vert, *blit_color_msaa_frag);
+ const VkPipelineMultisampleStateCreateInfo multisample_ci{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .rasterizationSamples = key.samples,
+ .sampleShadingEnable = VK_TRUE,
+ .minSampleShading = 1.0f,
+ .pSampleMask = nullptr,
+ .alphaToCoverageEnable = VK_FALSE,
+ .alphaToOneEnable = VK_FALSE,
+ };
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
- pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
+ blit_msaa_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast<u32>(stages.size()),
+ .pStages = stages.data(),
+ .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .pInputAssemblyState = &input_assembly_ci,
+ .pTessellationState = nullptr,
+ .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .pMultisampleState = &multisample_ci,
+ .pDepthStencilState = nullptr,
+ .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
+ .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .layout = *one_texture_pipeline_layout,
+ .renderPass = key.renderpass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = 0,
+ }));
+ return *blit_msaa_color_pipelines.back();
+}
+
+VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
+ bool resolve_stencil) {
+ auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
+ auto& pipelines = resolve_stencil ? resolve_depth_stencil_pipelines : resolve_depth_pipelines;
+ const auto it = std::ranges::find(keys, renderpass);
+ if (it != keys.end()) {
+ return *pipelines[std::distance(keys.begin(), it)];
+ }
+ keys.push_back(renderpass);
+ const std::array stages =
+ MakeStages(*full_screen_vert,
+ resolve_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
+ const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
+ pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -1044,25 +1404,41 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
- .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .pDepthStencilState = resolve_stencil ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
+ : &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
- .layout = *one_texture_pipeline_layout,
+ .layout = resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout,
.renderPass = renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- });
+ }));
+ return *pipelines.back();
}
-void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
- if (pipeline) {
- return;
+VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
+ const auto it = std::ranges::find(msaa_copy_keys, key);
+ if (it != msaa_copy_keys.end()) {
+ return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
}
- VkShaderModule frag_shader = *convert_depth_to_float_frag;
- const std::array stages = MakeStages(*full_screen_vert, frag_shader);
+ msaa_copy_keys.push_back(key);
+ const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa
+ ? *convert_msaa_to_non_msaa_frag
+ : *convert_non_msaa_to_msaa_frag);
+ const VkPipelineMultisampleStateCreateInfo multisample_ci{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .rasterizationSamples = key.samples,
+ .sampleShadingEnable = key.msaa_to_non_msaa ? VK_FALSE : VK_TRUE,
+ .minSampleShading = key.msaa_to_non_msaa ? 0.0f : 1.0f,
+ .pSampleMask = nullptr,
+ .alphaToCoverageEnable = VK_FALSE,
+ .alphaToOneEnable = VK_FALSE,
+ };
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
- pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
+ msaa_copy_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -1073,16 +1449,25 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
- .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
- .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .pMultisampleState = &multisample_ci,
+ .pDepthStencilState = nullptr,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
- .layout = *one_texture_pipeline_layout,
- .renderPass = renderpass,
+ .layout = *msaa_copy_pipeline_layout,
+ .renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- });
+ }));
+ return *msaa_copy_pipelines.back();
+}
+
+void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
+ ConvertPipeline(pipeline, renderpass, false);
+}
+
+void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
+ ConvertPipeline(pipeline, renderpass, true);
}
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
@@ -1106,7 +1491,8 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
- .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
+ .pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
+ : &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
.renderPass = renderpass,
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 34f1b25f0f..67294d594b 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -6,8 +6,12 @@
#pragma once
+#include <deque>
+#include <span>
+
#include "video_core/engines/fermi_2d.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
+#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -20,6 +24,7 @@ using VideoCommon::Region2D;
class Device;
class Framebuffer;
class ImageView;
+class RenderPassCache;
class StateTracker;
class Scheduler;
@@ -40,6 +45,21 @@ struct BlitDepthStencilPipelineKey {
u32 stencil_ref;
};
+struct MSAACopyPipelineKey {
+ constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default;
+
+ VkRenderPass renderpass;
+ VkSampleCountFlagBits samples;
+ bool msaa_to_non_msaa;
+};
+
+struct BlitMSAAPipelineKey {
+ constexpr auto operator<=>(const BlitMSAAPipelineKey&) const noexcept = default;
+
+ VkRenderPass renderpass;
+ VkSampleCountFlagBits samples;
+};
+
class BlitImageHelper {
public:
explicit BlitImageHelper(const Device& device, Scheduler& scheduler,
@@ -55,6 +75,12 @@ public:
VkImage src_image, VkSampler src_sampler, const Region2D& dst_region,
const Region2D& src_region, const Extent3D& src_size);
+ void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region);
+
+ void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region);
+
void BlitDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
@@ -84,6 +110,12 @@ public:
void ClearDepthStencil(const Framebuffer* dst_framebuffer, bool depth_clear, f32 clear_depth,
u8 stencil_mask, u32 stencil_ref, u32 stencil_compare_mask,
const Region2D& dst_region);
+
+ void CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
+ VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
+ VideoCore::Surface::PixelFormat src_format, u32 num_samples,
+ std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
+
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view);
@@ -98,6 +130,10 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key);
+ [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
+ [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
+ [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
+ bool resolve_stencil);
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
@@ -125,9 +161,13 @@ private:
vk::PipelineLayout one_texture_pipeline_layout;
vk::PipelineLayout two_textures_pipeline_layout;
vk::PipelineLayout clear_color_pipeline_layout;
+ vk::PipelineLayout msaa_copy_pipeline_layout;
vk::ShaderModule full_screen_vert;
vk::ShaderModule blit_color_to_color_frag;
+ vk::ShaderModule blit_color_msaa_frag;
vk::ShaderModule blit_depth_stencil_frag;
+ vk::ShaderModule blit_depth_msaa_frag;
+ vk::ShaderModule blit_depth_stencil_msaa_frag;
vk::ShaderModule clear_color_vert;
vk::ShaderModule clear_color_frag;
vk::ShaderModule clear_stencil_frag;
@@ -138,6 +178,8 @@ private:
vk::ShaderModule convert_d32f_to_abgr8_frag;
vk::ShaderModule convert_d24s8_to_abgr8_frag;
vk::ShaderModule convert_s8d24_to_abgr8_frag;
+ vk::ShaderModule convert_msaa_to_non_msaa_frag;
+ vk::ShaderModule convert_non_msaa_to_msaa_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -149,6 +191,21 @@ private:
std::vector<vk::Pipeline> clear_color_pipelines;
std::vector<BlitDepthStencilPipelineKey> clear_stencil_keys;
std::vector<vk::Pipeline> clear_stencil_pipelines;
+ std::vector<MSAACopyPipelineKey> msaa_copy_keys;
+ std::vector<vk::Pipeline> msaa_copy_pipelines;
+ std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
+ std::vector<vk::Pipeline> blit_msaa_color_pipelines;
+ std::vector<VkRenderPass> resolve_depth_keys;
+ std::vector<vk::Pipeline> resolve_depth_pipelines;
+ std::vector<VkRenderPass> resolve_depth_stencil_keys;
+ std::vector<vk::Pipeline> resolve_depth_stencil_pipelines;
+ struct MSAACopyResources {
+ u64 tick;
+ vk::ImageView src_view;
+ vk::ImageView dst_view;
+ vk::Framebuffer framebuffer;
+ };
+ std::deque<MSAACopyResources> msaa_copy_resources;
vk::Pipeline convert_d32_to_r32_pipeline;
vk::Pipeline convert_r32_to_d32_pipeline;
vk::Pipeline convert_d16_to_r16_pipeline;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index edc0283d9a..80c434dc8f 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -17,8 +17,6 @@
#include "common/div_ceil.h"
#include "common/vector_math.h"
#include "video_core/host_shaders/astc_decoder_comp_spv.h"
-#include "video_core/host_shaders/convert_msaa_to_non_msaa_comp_spv.h"
-#include "video_core/host_shaders/convert_non_msaa_to_msaa_comp_spv.h"
#include "video_core/host_shaders/queries_prefix_scan_sum_comp_spv.h"
#include "video_core/host_shaders/queries_prefix_scan_sum_nosubgroups_comp_spv.h"
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
@@ -140,33 +138,6 @@ constexpr DescriptorBankInfo ASTC_BANK_INFO{
.score = 2,
};
-constexpr std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> MSAA_DESCRIPTOR_SET_BINDINGS{{
- {
- .binding = 0,
- .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
- .descriptorCount = 1,
- .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
- .pImmutableSamplers = nullptr,
- },
- {
- .binding = 1,
- .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
- .descriptorCount = 1,
- .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
- .pImmutableSamplers = nullptr,
- },
-}};
-
-constexpr DescriptorBankInfo MSAA_BANK_INFO{
- .uniform_buffers = 0,
- .storage_buffers = 0,
- .texture_buffers = 0,
- .image_buffers = 0,
- .textures = 0,
- .images = 2,
- .score = 2,
-};
-
constexpr VkDescriptorUpdateTemplateEntry INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{
.dstBinding = 0,
.dstArrayElement = 0,
@@ -185,15 +156,6 @@ constexpr VkDescriptorUpdateTemplateEntry QUERIES_SCAN_DESCRIPTOR_UPDATE_TEMPLAT
.stride = sizeof(DescriptorUpdateEntry),
};
-constexpr VkDescriptorUpdateTemplateEntry MSAA_DESCRIPTOR_UPDATE_TEMPLATE{
- .dstBinding = 0,
- .dstArrayElement = 0,
- .descriptorCount = 2,
- .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
- .offset = 0,
- .stride = sizeof(DescriptorUpdateEntry),
-};
-
constexpr std::array<VkDescriptorUpdateTemplateEntry, ASTC_NUM_BINDINGS>
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{
{
@@ -910,100 +872,4 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
});
}
-MSAACopyPass::MSAACopyPass(const Device& device_, Scheduler& scheduler_,
- DescriptorPool& descriptor_pool_,
- StagingBufferPool& staging_buffer_pool_,
- ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
- : ComputePass(device_, scheduler_, descriptor_pool_, MSAA_DESCRIPTOR_SET_BINDINGS,
- MSAA_DESCRIPTOR_UPDATE_TEMPLATE, MSAA_BANK_INFO, {},
- CONVERT_NON_MSAA_TO_MSAA_COMP_SPV),
- scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
- compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {
- const auto make_msaa_pipeline = [this](size_t i, std::span<const u32> code) {
- modules[i] = device.GetLogical().CreateShaderModule({
- .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- .codeSize = static_cast<u32>(code.size_bytes()),
- .pCode = code.data(),
- });
- pipelines[i] = device.GetLogical().CreateComputePipeline(VkComputePipelineCreateInfo{
- .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- .stage{
- .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- .stage = VK_SHADER_STAGE_COMPUTE_BIT,
- .module = *modules[i],
- .pName = "main",
- .pSpecializationInfo = nullptr,
- },
- .layout = *layout,
- .basePipelineHandle = {},
- .basePipelineIndex = 0,
- });
- };
- make_msaa_pipeline(0, CONVERT_NON_MSAA_TO_MSAA_COMP_SPV);
- make_msaa_pipeline(1, CONVERT_MSAA_TO_NON_MSAA_COMP_SPV);
-}
-
-MSAACopyPass::~MSAACopyPass() = default;
-
-void MSAACopyPass::CopyImage(Image& dst_image, Image& src_image,
- std::span<const VideoCommon::ImageCopy> copies,
- bool msaa_to_non_msaa) {
- const VkPipeline msaa_pipeline = *pipelines[msaa_to_non_msaa ? 1 : 0];
- scheduler.RequestOutsideRenderPassOperationContext();
- for (const VideoCommon::ImageCopy& copy : copies) {
- ASSERT(copy.src_subresource.base_layer == 0);
- ASSERT(copy.src_subresource.num_layers == 1);
- ASSERT(copy.dst_subresource.base_layer == 0);
- ASSERT(copy.dst_subresource.num_layers == 1);
-
- compute_pass_descriptor_queue.Acquire(scheduler, 2);
- compute_pass_descriptor_queue.AddImage(
- src_image.StorageImageView(copy.src_subresource.base_level));
- compute_pass_descriptor_queue.AddImage(
- dst_image.StorageImageView(copy.dst_subresource.base_level));
- const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
-
- const Common::Vec3<u32> num_dispatches = {
- Common::DivCeil(copy.extent.width, 8U),
- Common::DivCeil(copy.extent.height, 8U),
- copy.extent.depth,
- };
-
- scheduler.Record([this, dst = dst_image.Handle(), msaa_pipeline, num_dispatches,
- descriptor_data](vk::CommandBuffer cmdbuf) {
- const VkDescriptorSet set = descriptor_allocator.Commit();
- device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, msaa_pipeline);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
- cmdbuf.Dispatch(num_dispatches.x, num_dispatches.y, num_dispatches.z);
- const VkImageMemoryBarrier write_barrier{
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
- .newLayout = VK_IMAGE_LAYOUT_GENERAL,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = dst,
- .subresourceRange{
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = VK_REMAINING_MIP_LEVELS,
- .baseArrayLayer = 0,
- .layerCount = VK_REMAINING_ARRAY_LAYERS,
- },
- };
- cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
- VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
- });
- }
-}
-
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index cb213dae7e..3d30aa6bdc 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -164,23 +164,4 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
-
-class MSAACopyPass final : public ComputePass {
-public:
- explicit MSAACopyPass(const Device& device_, Scheduler& scheduler_,
- DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_,
- ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
- ~MSAACopyPass();
-
- void CopyImage(Image& dst_image, Image& src_image,
- std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
-
-private:
- Scheduler& scheduler;
- StagingBufferPool& staging_buffer_pool;
- ComputePassDescriptorQueue& compute_pass_descriptor_queue;
- std::array<vk::ShaderModule, 2> modules;
- std::array<vk::Pipeline, 2> pipelines;
-};
-
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index c9704b7c6c..073e4b5079 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -48,6 +48,10 @@ public:
void Configure(Tegra::Engines::KeplerCompute& kepler_compute, Tegra::MemoryManager& gpu_memory,
Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache);
+ bool IsBound() const noexcept {
+ return static_cast<bool>(pipeline);
+ }
+
private:
const Device& device;
vk::PipelineCache& pipeline_cache;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 9c9a599f2e..757e0f3ccf 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -124,8 +124,8 @@ PixelFormat DecodeFormat(u8 encoded_format) {
return PixelFormatFromRenderTargetFormat(format);
}
-RenderPassKey MakeRenderPassKey(const FixedPipelineState& state) {
- RenderPassKey key;
+RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& device) {
+ RenderPassKey key{};
std::ranges::transform(state.color_formats, key.color_formats.begin(), DecodeFormat);
if (state.depth_enabled != 0) {
const auto depth_format{static_cast<Tegra::DepthFormat>(state.depth_format.Value())};
@@ -134,6 +134,11 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state) {
key.depth_format = PixelFormat::Invalid;
}
key.samples = MaxwellToVK::MsaaMode(state.msaa_mode);
+ const bool has_color = std::ranges::any_of(key.color_formats, [](PixelFormat format) {
+ return format != PixelFormat::Invalid;
+ });
+ key.resolve_color =
+ key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler();
return key;
}
@@ -285,9 +290,20 @@ GraphicsPipeline::GraphicsPipeline(
descriptor_update_template =
builder.CreateTemplate(set_layout, *pipeline_layout, uses_push_descriptor);
- const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
+ const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state, device))};
Validate();
- MakePipeline(render_pass);
+ try {
+ MakePipeline(render_pass);
+ } catch (const vk::Exception& exception) {
+ LOG_CRITICAL(Render_Vulkan, "Graphics pipeline build failed: {}", exception.what());
+ std::scoped_lock lock{build_mutex};
+ is_built = true;
+ build_condvar.notify_one();
+ if (shader_notify) {
+ shader_notify->MarkShaderComplete();
+ }
+ return;
+ }
if (pipeline_statistics) {
pipeline_statistics->Collect(device, *pipeline);
}
@@ -519,6 +535,9 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
texture_cache.UpdateRenderTargets(false);
texture_cache.CheckFeedbackLoop(std::span<const VideoCommon::ImageViewInOut>{views.data(),
views.size()});
+ if (IsBuilt() && !pipeline) {
+ return false;
+ }
ConfigureDraw(rescaling, render_area);
return true;
@@ -551,6 +570,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
uses_render_area = render_area.uses_render_area,
render_area_data = render_area.words](vk::CommandBuffer cmdbuf) {
if (bind_pipeline) {
+ if (!pipeline) {
+ return;
+ }
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
}
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 6ca9fe4c71..0ff4e52c54 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -374,9 +374,11 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_int8 = device.IsInt8Supported(),
.support_uniform_and_storage_buffer_8bit =
device.IsUniformAndStorageBuffer8BitAccessSupported(),
+ .support_storage_buffer_8bit = device.IsStorageBuffer8BitAccessSupported(),
.support_int16 = device.IsShaderInt16Supported(),
.support_uniform_and_storage_buffer_16bit =
device.IsUniformAndStorageBuffer16BitAccessSupported(),
+ .support_storage_buffer_16bit = device.IsStorageBuffer16BitAccessSupported(),
.support_int64 = device.IsShaderInt64Supported(),
.support_vertex_instance_id = false,
.support_float_controls = device.IsKhrShaderFloatControlsSupported(),
@@ -395,6 +397,10 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_fp64_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
+ .support_workgroup_layout_8bit_access =
+ device.IsWorkgroupMemoryExplicitLayout8BitAccessSupported(),
+ .support_workgroup_layout_16bit_access =
+ device.IsWorkgroupMemoryExplicitLayout16BitAccessSupported(),
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
.supported_subgroup_stages = supported_subgroup_stages,
.support_viewport_index_layer_non_geometry =
@@ -404,6 +410,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_demote_to_helper_invocation =
device.IsExtShaderDemoteToHelperInvocationSupported(),
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
+ .support_shared_int64_atomics = device.IsSharedInt64AtomicsSupported(),
.support_derivative_control = true,
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_native_ndc = device.IsExtDepthClipControlSupported(),
@@ -412,6 +419,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(),
.support_sampled_image_array_nonuniform_indexing =
device.IsSampledImageArrayNonUniformIndexingSupported(),
+ .support_storage_image_array_nonuniform_indexing =
+ device.IsStorageImageArrayNonUniformIndexingSupported(),
+ .support_uniform_texel_buffer_array_nonuniform_indexing =
+ device.IsUniformTexelBufferArrayNonUniformIndexingSupported(),
+ .support_storage_texel_buffer_array_nonuniform_indexing =
+ device.IsStorageTexelBufferArrayNonUniformIndexingSupported(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 26663d4b25..720f3b868c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -410,7 +410,23 @@ void RasterizerVulkan::Clear(u32 layer_count) {
texture_cache.UpdateRenderTargets(true);
const Framebuffer* const framebuffer = texture_cache.GetFramebuffer();
const VkExtent2D render_area = framebuffer->RenderArea();
- scheduler.RequestRenderpass(framebuffer);
+
+ constexpr bool ENABLE_DEFERRED_CLEAR = true;
+ const bool color_full_channels = regs.clear_surface.R && regs.clear_surface.G &&
+ regs.clear_surface.B && regs.clear_surface.A;
+ const bool stencil_partial = use_stencil && framebuffer->HasAspectStencilBit() &&
+ regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0;
+ const bool ds_used = use_depth || use_stencil;
+ 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;
+ if (!can_defer_clear) {
+ scheduler.RequestRenderpass(framebuffer);
+ }
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
@@ -494,15 +510,19 @@ void RasterizerVulkan::Clear(u32 layer_count) {
clear_value.color.int32[i] = s32(f32(s64(int_size - 1) << 1) * (regs.clear_color[i] - 0.5f));
}
- if (regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B && regs.clear_surface.A) {
- scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
- const VkClearAttachment attachment{
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .colorAttachment = color_attachment,
- .clearValue = clear_value,
- };
- cmdbuf.ClearAttachments(attachment, clear_rect);
- });
+ if (color_full_channels) {
+ if (can_defer_clear) {
+ scheduler.DeferColorClear(framebuffer, color_attachment, clear_value);
+ } else {
+ scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
+ const VkClearAttachment attachment{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .colorAttachment = color_attachment,
+ .clearValue = clear_value,
+ };
+ cmdbuf.ClearAttachments(attachment, clear_rect);
+ });
+ }
} else {
u8 color_mask = u8(regs.clear_surface.R | regs.clear_surface.G << 1 | regs.clear_surface.B << 2 | regs.clear_surface.A << 3);
Region2D dst_region = {
@@ -536,6 +556,11 @@ void RasterizerVulkan::Clear(u32 layer_count) {
blit_image.ClearDepthStencil(framebuffer, use_depth, regs.clear_depth,
u8(regs.stencil_front_mask), regs.clear_stencil,
regs.stencil_front_func_mask, dst_region);
+ } else if (can_defer_clear) {
+ VkClearValue ds_value{};
+ ds_value.depthStencil.depth = regs.clear_depth;
+ ds_value.depthStencil.stencil = regs.clear_stencil;
+ scheduler.DeferDepthStencilClear(framebuffer, ds_value);
} else {
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
@@ -569,13 +594,20 @@ void RasterizerVulkan::DispatchCompute() {
const auto [buffer, offset] =
buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op);
scheduler.RequestOutsideRenderPassOperationContext();
- scheduler.Record([indirect_buffer = buffer->Handle(),
+ scheduler.Record([pipeline, indirect_buffer = buffer->Handle(),
indirect_offset = offset](vk::CommandBuffer cmdbuf) {
+ if (!pipeline->IsBound()) {
+ return;
+ }
cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset);
});
return;
}
const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z};
+ const std::array<u32, 3> max_dim{device.GetMaxComputeWorkGroupCount()};
+ if (dim[0] > max_dim[0] || dim[1] > max_dim[1] || dim[2] > max_dim[2]) {
+ return;
+ }
scheduler.RequestOutsideRenderPassOperationContext();
static constexpr VkMemoryBarrier READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
@@ -585,7 +617,12 @@ void RasterizerVulkan::DispatchCompute() {
};
scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, READ_BARRIER); });
- scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); });
+ scheduler.Record([pipeline, dim](vk::CommandBuffer cmdbuf) {
+ if (!pipeline->IsBound()) {
+ return;
+ }
+ cmdbuf.Dispatch(dim[0], dim[1], dim[2]);
+ });
// Log compute dispatch
if (GPU::Logging::IsActive() &&
@@ -1309,15 +1346,22 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
}
if (update_references) {
[&]() {
+ bool changed;
if (regs.stencil_two_side_enable) {
- if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref) &&
- !state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref)) {
- return;
- }
+ const bool front_changed =
+ state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref);
+ const bool back_changed =
+ state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref);
+ changed = front_changed || back_changed;
} else {
- if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) {
- return;
- }
+ const bool front_changed =
+ state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref);
+ const bool back_changed =
+ state_tracker.CheckStencilReferenceBack(regs.stencil_front_ref);
+ changed = front_changed || back_changed;
+ }
+ if (!changed) {
+ return;
}
scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref,
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
@@ -1334,15 +1378,22 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
}
if (update_write_mask) {
[&]() {
+ bool changed;
if (regs.stencil_two_side_enable) {
- if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) &&
- !state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) {
- return;
- }
+ const bool front_changed =
+ state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask);
+ const bool back_changed =
+ state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask);
+ changed = front_changed || back_changed;
} else {
- if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) {
- return;
- }
+ const bool front_changed =
+ state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask);
+ const bool back_changed =
+ state_tracker.CheckStencilWriteMaskBack(regs.stencil_front_mask);
+ changed = front_changed || back_changed;
+ }
+ if (!changed) {
+ return;
}
scheduler.Record([front_write_mask = regs.stencil_front_mask,
back_write_mask = regs.stencil_back_mask,
@@ -1360,15 +1411,22 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
}
if (update_compare_masks) {
[&]() {
+ bool changed;
if (regs.stencil_two_side_enable) {
- if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) &&
- !state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) {
- return;
- }
+ const bool front_changed =
+ state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask);
+ const bool back_changed =
+ state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask);
+ changed = front_changed || back_changed;
} else {
- if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) {
- return;
- }
+ const bool front_changed =
+ state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask);
+ const bool back_changed =
+ state_tracker.CheckStencilCompareMaskBack(regs.stencil_front_func_mask);
+ changed = front_changed || back_changed;
+ }
+ if (!changed) {
+ return;
}
scheduler.Record([front_test_mask = regs.stencil_front_func_mask,
back_test_mask = regs.stencil_back_func_mask,
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 118b2a0832..66bade9a2e 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -44,7 +44,9 @@ using VideoCore::Surface::SurfaceType;
}
VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat format,
- VkSampleCountFlagBits samples) {
+ VkSampleCountFlagBits samples,
+ VkAttachmentLoadOp load_op,
+ VkAttachmentStoreOp store_op) {
using MaxwellToVK::SurfaceFormat;
const SurfaceType surface_type = GetSurfaceType(format);
@@ -55,12 +57,10 @@ using VideoCore::Surface::SurfaceType;
.flags = {},
.format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
.samples = samples,
- .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
- .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
- .stencilLoadOp = has_stencil ? VK_ATTACHMENT_LOAD_OP_LOAD
- : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
- .stencilStoreOp = has_stencil ? VK_ATTACHMENT_STORE_OP_STORE
- : VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .loadOp = load_op,
+ .storeOp = store_op,
+ .stencilLoadOp = has_stencil ? load_op : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = has_stencil ? store_op : VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_GENERAL,
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
};
@@ -87,7 +87,14 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
if (is_valid) {
- descriptions.push_back(AttachmentDescription(*device, format, key.samples));
+ const VkAttachmentLoadOp load_op = (key.color_clear_mask & (1u << index)) != 0
+ ? VK_ATTACHMENT_LOAD_OP_CLEAR
+ : VK_ATTACHMENT_LOAD_OP_LOAD;
+ const VkAttachmentStoreOp store_op = (key.color_discard_mask & (1u << index)) != 0
+ ? VK_ATTACHMENT_STORE_OP_DONT_CARE
+ : VK_ATTACHMENT_STORE_OP_STORE;
+ descriptions.push_back(
+ AttachmentDescription(*device, format, key.samples, load_op, store_op));
num_attachments = static_cast<u32>(index + 1);
++num_colors;
}
@@ -99,7 +106,32 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.attachment = num_colors,
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
- descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples));
+ const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear
+ ? VK_ATTACHMENT_LOAD_OP_CLEAR
+ : VK_ATTACHMENT_LOAD_OP_LOAD;
+ descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
+ depth_load_op, VK_ATTACHMENT_STORE_OP_STORE));
+ }
+ std::array<VkAttachmentReference, 8> resolve_references{};
+ const bool do_resolve_color =
+ key.resolve_color && key.samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0;
+ if (do_resolve_color) {
+ for (size_t index = 0; index < key.color_formats.size(); ++index) {
+ const PixelFormat format{key.color_formats[index]};
+ const bool is_valid{format != PixelFormat::Invalid};
+ resolve_references[index] = VkAttachmentReference{
+ .attachment = is_valid ? static_cast<u32>(descriptions.size()) : VK_ATTACHMENT_UNUSED,
+ .layout = VK_IMAGE_LAYOUT_GENERAL,
+ };
+ if (is_valid) {
+ VkAttachmentDescription resolve_desc =
+ AttachmentDescription(*device, format, VK_SAMPLE_COUNT_1_BIT,
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ VK_ATTACHMENT_STORE_OP_STORE);
+ resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ descriptions.push_back(resolve_desc);
+ }
+ }
}
const VkSubpassDescription subpass{
.flags = 0,
@@ -108,7 +140,7 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.pInputAttachments = nullptr,
.colorAttachmentCount = num_attachments,
.pColorAttachments = references.data(),
- .pResolveAttachments = nullptr,
+ .pResolveAttachments = do_resolve_color ? resolve_references.data() : nullptr,
.pDepthStencilAttachment = has_depth ? &depth_reference : nullptr,
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
index 5c7b3c2aed..75999a655e 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
@@ -20,6 +20,10 @@ struct RenderPassKey {
std::array<VideoCore::Surface::PixelFormat, 8> color_formats;
VideoCore::Surface::PixelFormat depth_format;
VkSampleCountFlagBits samples;
+ bool resolve_color;
+ u32 color_clear_mask;
+ bool depth_stencil_clear;
+ u32 color_discard_mask;
};
} // namespace Vulkan
@@ -30,6 +34,10 @@ struct hash<Vulkan::RenderPassKey> {
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
size_t value = static_cast<size_t>(key.depth_format) << 48;
value ^= static_cast<size_t>(key.samples) << 52;
+ value ^= static_cast<size_t>(key.resolve_color) << 63;
+ value ^= static_cast<size_t>(key.color_clear_mask) << 54;
+ value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
+ value ^= static_cast<size_t>(key.color_discard_mask) << 24;
for (size_t i = 0; i < key.color_formats.size(); ++i) {
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
}
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 334d82efff..6234b41978 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -93,30 +93,27 @@ void Scheduler::DispatchWork() {
}
}
-void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
- const VkRenderPass renderpass = framebuffer->RenderPass();
+void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass,
+ const VkClearValue* clear_values, u32 clear_value_count) {
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
const VkExtent2D render_area = framebuffer->RenderArea();
- if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer &&
- render_area.width == state.render_area.width &&
- render_area.height == state.render_area.height) {
- return;
- }
- EndRenderPass();
state.renderpass = renderpass;
state.framebuffer = framebuffer_handle;
state.render_area = render_area;
- // Log render pass begin
- if (GPU::Logging::IsActive() &&
- Settings::values.gpu_log_vulkan_calls.GetValue()) {
- const std::string render_pass_info = fmt::format(
- "renderArea={}x{}, numImages={}",
- render_area.width, render_area.height, framebuffer->NumImages());
+ if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) {
+ const std::string render_pass_info =
+ fmt::format("renderArea={}x{}, numImages={}", render_area.width, render_area.height,
+ framebuffer->NumImages());
GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info);
}
- Record([renderpass, framebuffer_handle, render_area](vk::CommandBuffer cmdbuf) {
+ std::array<VkClearValue, 9> values{};
+ for (u32 i = 0; i < clear_value_count && i < values.size(); ++i) {
+ values[i] = clear_values[i];
+ }
+ Record([renderpass, framebuffer_handle, render_area, values, clear_value_count](
+ vk::CommandBuffer cmdbuf) {
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
@@ -127,8 +124,8 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
.offset = {.x = 0, .y = 0},
.extent = render_area,
},
- .clearValueCount = 0,
- .pClearValues = nullptr,
+ .clearValueCount = clear_value_count,
+ .pClearValues = clear_value_count != 0 ? values.data() : nullptr,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
});
@@ -137,6 +134,80 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
renderpass_image_ranges = framebuffer->ImageRanges();
}
+void Scheduler::RealizeDeferredClear() {
+ if (deferred_clear.framebuffer == nullptr) {
+ return;
+ }
+ const DeferredClear dc = deferred_clear;
+ deferred_clear = {};
+
+ std::array<VkClearValue, 9> clear_values{};
+ u32 count = 0;
+ const RenderPassKey& base = dc.framebuffer->RenderPassKeyBase();
+ for (u32 slot = 0; slot < 8; ++slot) {
+ if (base.color_formats[slot] == VideoCore::Surface::PixelFormat::Invalid) {
+ continue;
+ }
+ clear_values[count++] = dc.color_values[slot];
+ }
+ if (base.depth_format != VideoCore::Surface::PixelFormat::Invalid) {
+ clear_values[count++] = dc.depth_stencil_value;
+ }
+ const u32 color_discard_mask =
+ dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u;
+ const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant(
+ dc.color_clear_mask, dc.depth_stencil, color_discard_mask);
+ EndRenderPass();
+ BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count);
+}
+
+bool Scheduler::DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot,
+ const VkClearValue& value) {
+ if (IsRenderPassActive()) {
+ return false;
+ }
+ if (deferred_clear.framebuffer != nullptr && deferred_clear.framebuffer != framebuffer) {
+ RealizeDeferredClear();
+ EndRenderPass();
+ }
+ deferred_clear.framebuffer = framebuffer;
+ deferred_clear.color_clear_mask |= 1u << rt_slot;
+ deferred_clear.color_values[rt_slot] = value;
+ return true;
+}
+
+bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value) {
+ if (IsRenderPassActive()) {
+ return false;
+ }
+ if (deferred_clear.framebuffer != nullptr && deferred_clear.framebuffer != framebuffer) {
+ RealizeDeferredClear();
+ EndRenderPass();
+ }
+ deferred_clear.framebuffer = framebuffer;
+ deferred_clear.depth_stencil = true;
+ deferred_clear.depth_stencil_value = value;
+ return true;
+}
+
+void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
+ if (deferred_clear.framebuffer == framebuffer) {
+ RealizeDeferredClear();
+ return;
+ }
+ const VkRenderPass renderpass = framebuffer->RenderPass();
+ const VkFramebuffer framebuffer_handle = framebuffer->Handle();
+ const VkExtent2D render_area = framebuffer->RenderArea();
+ if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer &&
+ render_area.width == state.render_area.width &&
+ render_area.height == state.render_area.height) {
+ return;
+ }
+ // Ends any active pass and realizes a deferred clear
+ EndRenderPass();
+ BeginRenderPassImpl(framebuffer, renderpass, nullptr, 0);
+}
+
void Scheduler::RequestOutsideRenderPassOperationContext() {
EndRenderPass();
}
@@ -308,6 +379,7 @@ void Scheduler::EndPendingOperations() {
void Scheduler::EndRenderPass()
{
+ RealizeDeferredClear();
if (!state.renderpass) {
return;
}
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 0709c3a370..5c4af8c146 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -59,6 +59,12 @@ public:
/// Requests to begin a renderpass.
void RequestRenderpass(const Framebuffer* framebuffer);
+ /// Defers a full-attachment color clear so it becomes the next render pass.
+ bool DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot, const VkClearValue& value);
+
+ /// Defers a full depth/stencil clear so it becomes the next render pass.
+ bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value);
+
/// Requests the current execution context to be able to execute operations only allowed outside
/// of a renderpass.
void RequestOutsideRenderPassOperationContext();
@@ -251,6 +257,21 @@ private:
bool needs_state_enable_refresh = false;
};
+ struct DeferredClear {
+ const Framebuffer* framebuffer = nullptr;
+ u32 color_clear_mask = 0;
+ std::array<VkClearValue, 8> color_values{};
+ bool depth_stencil = false;
+ VkClearValue depth_stencil_value{};
+ };
+
+ /// Begins a render pass for the given framebuffer, optionally with clear values.
+ void BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass,
+ const VkClearValue* clear_values, u32 clear_value_count);
+
+ /// If a deferred clear is pending.
+ void RealizeDeferredClear();
+
void WorkerThread(std::stop_token stop_token);
void AllocateWorkerCommandBuffer();
@@ -276,6 +297,8 @@ private:
vk::CommandBuffer current_cmdbuf;
vk::CommandBuffer current_upload_cmdbuf;
+ DeferredClear deferred_clear;
+
std::unique_ptr<CommandChunk> chunk;
std::function<void()> 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 960dafb99d..44a4227a19 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -52,6 +52,9 @@ using VideoCore::Surface::IsPixelFormatInteger;
using VideoCore::Surface::SurfaceType;
namespace {
+constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true;
+constexpr bool ENABLE_MSAA_COLOR_DISCARD = true;
+
constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
if (color == std::array<float, 4>{0, 0, 0, 0}) {
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
@@ -909,9 +912,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue, memory_allocator);
}
- if (device.IsStorageImageMultisampleSupported()) {
- msaa_copy_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue);
- }
if (!device.IsKhrImageFormatListSupported()) {
return;
}
@@ -995,12 +995,84 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
return *buffers[level];
}
+VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
+ VkExtent2D extent, u32 layers) {
+ ResolveShadow& shadow = resolve_shadows[msaa_image];
+ if (shadow.image && shadow.format == format && shadow.extent.width == extent.width &&
+ shadow.extent.height == extent.height && shadow.layers == layers) {
+ shadow.up_to_date = true;
+ return *shadow.view;
+ }
+ shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = format,
+ .extent = {extent.width, extent.height, 1},
+ .mipLevels = 1,
+ .arrayLayers = layers,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .queueFamilyIndexCount = 0,
+ .pQueueFamilyIndices = nullptr,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ });
+ shadow.view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .image = *shadow.image,
+ .viewType = layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D,
+ .format = format,
+ .components{},
+ .subresourceRange{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = layers,
+ },
+ });
+ shadow.format = format;
+ shadow.extent = extent;
+ shadow.layers = layers;
+ shadow.up_to_date = true;
+ return *shadow.view;
+}
+
+const TextureCacheRuntime::ResolveShadow* TextureCacheRuntime::GetValidResolveShadow(
+ VkImage msaa_image) const {
+ const auto it = resolve_shadows.find(msaa_image);
+ if (it == resolve_shadows.end() || !it->second.up_to_date || !it->second.image) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) {
+ const auto it = resolve_shadows.find(msaa_image);
+ if (it != resolve_shadows.end()) {
+ it->second.up_to_date = false;
+ }
+}
+
+void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) {
+ resolve_shadows.erase(msaa_image);
+}
+
void TextureCacheRuntime::BarrierFeedbackLoop() {
scheduler.RequestOutsideRenderPassOperationContext();
}
void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
+ if (ENABLE_MSAA_RESOLVE_CONSUME) {
+ InvalidateResolveShadow(dst.Handle());
+ }
boost::container::small_vector<VkBufferImageCopy, 16> vk_in_copies(copies.size());
boost::container::small_vector<VkBufferImageCopy, 16> vk_out_copies(copies.size());
const VkImageAspectFlags src_aspect_mask = src.AspectMask();
@@ -1147,6 +1219,15 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
return;
}
ASSERT(src.format == dst.format);
+ if (is_src_msaa && !is_dst_msaa &&
+ (aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
+ if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0) {
+ UNIMPLEMENTED_MSG("Stencil-only MSAA resolve is not supported");
+ return;
+ }
+ blit_image_helper.ResolveDepthStencil(dst_framebuffer, src, dst_region, src_region);
+ return;
+ }
if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
const auto format = src.format;
const auto can_blit_depth_stencil = [this, format] {
@@ -1172,19 +1253,20 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
ASSERT(!(is_dst_msaa && !is_src_msaa));
ASSERT(operation == Fermi2D::Operation::SrcCopy);
- const VkImage dst_image = dst.ImageHandle();
- const VkImage src_image = src.ImageHandle();
- const VkImageSubresourceLayers dst_layers = MakeSubresourceLayers(&dst);
- const VkImageSubresourceLayers src_layers = MakeSubresourceLayers(&src);
const bool is_msaa_to_msaa = is_src_msaa && is_dst_msaa;
-
- // NVIDIA 510+ and Intel crash on MSAA->MSAA blits (scaling operations)
- // Fall back to 3D helpers for MSAA scaling
+ if (is_msaa_to_msaa && aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
+ blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region);
+ return;
+ }
if (is_msaa_to_msaa && device.CantBlitMSAA()) {
- // This should be handled by NeedsScaleHelper() and use 3D helpers instead
- UNIMPLEMENTED_MSG("MSAA to MSAA blit not supported on this driver");
+ UNIMPLEMENTED_MSG("MSAA to MSAA depth-stencil blit is not supported on this driver");
return;
}
+
+ const VkImage dst_image = dst.ImageHandle();
+ const VkImage src_image = src.ImageHandle();
+ const VkImageSubresourceLayers dst_layers = MakeSubresourceLayers(&dst);
+ const VkImageSubresourceLayers src_layers = MakeSubresourceLayers(&src);
const bool is_resolve = is_src_msaa && !is_dst_msaa;
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([filter, dst_region, src_region, dst_image, src_image, dst_layers, src_layers,
@@ -1442,6 +1524,9 @@ bool TextureCacheRuntime::IsFormatScalable(PixelFormat format) {
void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
+ if (ENABLE_MSAA_RESOLVE_CONSUME) {
+ InvalidateResolveShadow(dst.Handle());
+ }
// As per the size-compatible formats section of vulkan, copy manually via ReinterpretImage
// these images that aren't size-compatible
if (BytesPerBlock(src.info.format) != BytesPerBlock(dst.info.format)) {
@@ -1555,10 +1640,117 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1;
- if (msaa_copy_pass) {
- return msaa_copy_pass->CopyImage(dst, src, copies, msaa_to_non_msaa);
+ const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples;
+ if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
+ VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
+ UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
+ return;
+ }
+ if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 &&
+ src.info.format == dst.info.format) {
+ const VideoCommon::ImageCopy& copy = copies.front();
+ const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle());
+ if (shadow != nullptr && copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
+ copy.src_subresource.base_level == 0 &&
+ static_cast<u32>(copy.extent.width) <= shadow->extent.width &&
+ static_cast<u32>(copy.extent.height) <= shadow->extent.height) {
+ const VkImage shadow_image = *shadow->image;
+ const VkImage dst_image = dst.Handle();
+ const VkImageCopy region{
+ .srcSubresource{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer),
+ .layerCount = static_cast<u32>(copy.src_subresource.num_layers),
+ },
+ .srcOffset = {0, 0, 0},
+ .dstSubresource{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
+ .baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
+ .layerCount = static_cast<u32>(copy.dst_subresource.num_layers),
+ },
+ .dstOffset = {copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z},
+ .extent = {copy.extent.width, copy.extent.height, 1},
+ };
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) {
+ const std::array pre_barriers{
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = shadow_image,
+ .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
+ VK_REMAINING_ARRAY_LAYERS},
+ },
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
+ VK_REMAINING_ARRAY_LAYERS},
+ },
+ };
+ const std::array post_barriers{
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = 0,
+ .dstAccessMask = 0,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = shadow_image,
+ .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
+ VK_REMAINING_ARRAY_LAYERS},
+ },
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
+ VK_REMAINING_ARRAY_LAYERS},
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr,
+ pre_barriers);
+ cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region);
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, nullptr,
+ post_barriers);
+ });
+ return;
+ }
}
- UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
+ blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(),
+ src.info.format, num_samples, copies, msaa_to_non_msaa);
}
u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
@@ -1577,7 +1769,11 @@ std::optional<size_t> TextureCacheRuntime::GetSamplerHeapBudget() const {
return device.GetSamplerHeapBudget();
}
-void TextureCacheRuntime::TickFrame() {}
+void TextureCacheRuntime::TickFrame() {
+ std::erase_if(pending_msaa_images, [this](const auto& pending) {
+ return scheduler.IsFree(pending.first);
+ });
+}
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
VAddr cpu_addr_)
@@ -1632,7 +1828,16 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
-Image::~Image() = default;
+Image::~Image() {
+ if (ENABLE_MSAA_RESOLVE_CONSUME && runtime != nullptr) {
+ if (original_image) {
+ runtime->EraseResolveShadow(*original_image);
+ }
+ if (scaled_image) {
+ runtime->EraseResolveShadow(*scaled_image);
+ }
+ }
+}
void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) {
using VideoCore::Surface::BytesPerBlock;
@@ -1679,82 +1884,75 @@ void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) {
void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
std::span<const VideoCommon::BufferImageCopy> copies) {
// TODO: Move this to another API
+ if (ENABLE_MSAA_RESOLVE_CONSUME && runtime != nullptr) {
+ runtime->InvalidateResolveShadow(Handle());
+ }
const bool is_rescaled = True(flags & ImageFlagBits::Rescaled);
if (is_rescaled) {
ScaleDown(true);
}
- // Handle MSAA upload if necessary
- /* WARNING, TODO: This code uses some hacks, besides being fundamentally ugly
- since tropic didn't want to touch it for a long time, so it needs a rewrite from someone
- better than me at vulkan. */
- // CHANGE: Gate the MSAA path more strictly and only use it for color, when the pass and device
- // support are available. Avoid running the MSAA path when prerequisites aren't met,
- // preventing validation and runtime issues.
const bool wants_msaa_upload = info.num_samples > 1
&& (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0
- && runtime->CanUploadMSAA() && runtime->msaa_copy_pass.has_value()
- && runtime->device.IsStorageImageMultisampleSupported();
+ && !VideoCore::Surface::IsPixelFormatInteger(info.format);
if (wants_msaa_upload) {
- // Create a temporary non-MSAA image to upload the data first
ImageInfo temp_info = info;
temp_info.num_samples = 1;
- // CHANGE: Build a fresh VkImageCreateInfo with robust usage flags for the temp image.
- // Using the target image's usage as-is could miss STORAGE/TRANSFER bits and trigger
- // validation errors.
VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info);
- image_ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
- VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
-
- // CHANGE: The previous stack-allocated wrapper was destroyed at function exit,
- // which could destroy VkImage before the GPU used it.
- auto temp_wrapper = std::make_shared<Image>(*runtime, temp_info, 0, 0);
- temp_wrapper->original_image = runtime->memory_allocator.CreateImage(image_ci);
- temp_wrapper->current_image = &Image::original_image;
- temp_wrapper->aspect_mask = aspect_mask;
- temp_wrapper->initialized = true;
-
- // Upload to the temporary non-MSAA image
+ image_ci.format =
+ MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format)
+ .format;
+ image_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci);
+
scheduler->RequestOutsideRenderPassOperationContext();
- auto vk_copies = TransformBufferImageCopies(copies, offset, temp_wrapper->aspect_mask);
+ auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
const VkBuffer src_buffer = buffer;
- const VkImage temp_vk_image = *temp_wrapper->original_image;
- const VkImageAspectFlags vk_aspect_mask = temp_wrapper->aspect_mask;
+ const VkImage temp_vk_image = *temp_image;
+ const VkImageAspectFlags vk_aspect_mask = aspect_mask;
- scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies,
- keep = temp_wrapper](vk::CommandBuffer cmdbuf) {
+ scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask,
+ vk_copies](vk::CommandBuffer cmdbuf) {
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, VideoCommon::FixSmallVectorADL(vk_copies));
});
- // Use MSAACopyPass to convert from non-MSAA to MSAA
+ const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
std::vector<VideoCommon::ImageCopy> image_copies;
image_copies.reserve(copies.size());
for (const auto& copy : copies) {
VideoCommon::ImageCopy image_copy{};
- image_copy.src_offset = {0, 0, 0}; // Use zero offset for source
- image_copy.dst_offset = copy.image_offset;
+ image_copy.src_offset = {0, 0, 0};
+ image_copy.dst_offset = {copy.image_offset.x >> samples_x,
+ copy.image_offset.y >> samples_y, copy.image_offset.z};
image_copy.src_subresource = copy.image_subresource;
image_copy.dst_subresource = copy.image_subresource;
- image_copy.extent = copy.image_extent;
+ image_copy.extent = {copy.image_extent.width >> samples_x,
+ copy.image_extent.height >> samples_y, copy.image_extent.depth};
image_copies.push_back(image_copy);
}
- runtime->msaa_copy_pass->CopyImage(*this, *temp_wrapper, image_copies,
- /*msaa_to_non_msaa=*/false);
- std::exchange(initialized, true);
+ runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format,
+ temp_vk_image, info.format, info.num_samples,
+ image_copies, false);
+ initialized = true;
+ runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image));
- const u64 tick = scheduler->Flush();
- scheduler->Wait(tick);
+ if (is_rescaled) {
+ ScaleUp();
+ }
+ return;
+ }
+ if (info.num_samples > 1) {
+ LOG_WARNING(Render_Vulkan, "MSAA upload not implemented for format {}", info.format);
if (is_rescaled) {
ScaleUp();
}
return;
}
- // Regular non-MSAA upload (original behavior preserved)
scheduler->RequestOutsideRenderPassOperationContext();
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
const VkBuffer src_buffer = buffer;
@@ -1794,22 +1992,45 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> o
ScaleDown();
}
- // RE-USE MSAA UPLOAD CODE BUT NOW FOR DOWNLOAD
- if (info.num_samples > 1 && runtime->msaa_copy_pass) {
- // TODO: Depth/stencil formats need special handling
- if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
+ if (info.num_samples > 1) {
+ if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT &&
+ !VideoCore::Surface::IsPixelFormatInteger(info.format)) {
ImageInfo temp_info = info;
temp_info.num_samples = 1;
VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info);
- image_ci.usage = original_image.UsageFlags();
+ image_ci.format =
+ MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format)
+ .format;
+ image_ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci);
+ const VkImage temp_vk_image = *temp_image;
- Image temp_wrapper(*runtime, temp_info, 0, 0);
- temp_wrapper.original_image = std::move(temp_image);
- temp_wrapper.current_image = &Image::original_image;
- temp_wrapper.aspect_mask = aspect_mask;
- temp_wrapper.initialized = true;
+ scheduler->RequestOutsideRenderPassOperationContext();
+ scheduler->Record([temp_vk_image](vk::CommandBuffer cmdbuf) {
+ const VkImageMemoryBarrier init_barrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = 0,
+ .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = temp_vk_image,
+ .subresourceRange{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
+ init_barrier);
+ });
std::vector<VideoCommon::ImageCopy> image_copies;
for (const auto& copy : copies) {
@@ -1822,7 +2043,9 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> o
image_copies.push_back(image_copy);
}
- runtime->msaa_copy_pass->CopyImage(temp_wrapper, *this, image_copies, true);
+ runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, temp_vk_image,
+ info.format, Handle(), info.format,
+ info.num_samples, image_copies, true);
boost::container::small_vector<VkBuffer, 8> buffers_vector{};
boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8>
@@ -1834,7 +2057,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> o
}
scheduler->RequestOutsideRenderPassOperationContext();
- scheduler->Record([buffers = std::move(buffers_vector), image = *temp_wrapper.original_image,
+ scheduler->Record([buffers = std::move(buffers_vector), image = temp_vk_image,
aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
@@ -1889,6 +2112,8 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> o
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, memory_write_barrier, nullptr, image_write_barrier);
});
+ runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(),
+ std::move(temp_image));
return;
}
} else {
@@ -2074,10 +2299,11 @@ bool Image::BlitScaleHelper(bool scale_up) {
blit_view = std::make_unique<ImageView>(*runtime, view_info, NULL_IMAGE_ID, *this);
}
- const u32 src_width = scale_up ? info.size.width : scaled_width;
- const u32 src_height = scale_up ? info.size.height : scaled_height;
- const u32 dst_width = scale_up ? scaled_width : info.size.width;
- const u32 dst_height = scale_up ? scaled_height : info.size.height;
+ const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
+ const u32 src_width = (scale_up ? info.size.width : scaled_width) >> samples_x;
+ const u32 src_height = (scale_up ? info.size.height : scaled_height) >> samples_y;
+ const u32 dst_width = (scale_up ? scaled_width : info.size.width) >> samples_x;
+ const u32 dst_height = (scale_up ? scaled_height : info.size.height) >> samples_y;
const Region2D src_region{
.start = {0, 0},
.end = {s32(src_width), s32(src_height)},
@@ -2087,17 +2313,23 @@ bool Image::BlitScaleHelper(bool scale_up) {
.end = {s32(dst_width), s32(dst_height)},
};
const VkExtent2D extent{
- .width = (std::max)(scaled_width, info.size.width),
- .height = (std::max)(scaled_height, info.size.height),
+ .width = (std::max)(scaled_width, info.size.width) >> samples_x,
+ .height = (std::max)(scaled_height, info.size.height) >> samples_y,
};
auto* view_ptr = blit_view.get();
if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
if (!blit_framebuffer)
blit_framebuffer.emplace(*runtime, view_ptr, nullptr, extent, scale_up);
- runtime->blit_image_helper.BlitColor(&*blit_framebuffer, *blit_view,
- dst_region, src_region, operation, BLIT_OPERATION);
- } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
+ if (info.num_samples > 1) {
+ runtime->blit_image_helper.BlitColorMSAA(&*blit_framebuffer, *blit_view,
+ dst_region, src_region);
+ } else {
+ runtime->blit_image_helper.BlitColor(&*blit_framebuffer, *blit_view,
+ dst_region, src_region, operation, BLIT_OPERATION);
+ }
+ } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) &&
+ info.num_samples == 1) {
if (!blit_framebuffer)
blit_framebuffer.emplace(*runtime, nullptr, view_ptr, extent, scale_up);
runtime->blit_image_helper.BlitDepthStencil(&*blit_framebuffer, *blit_view,
@@ -2113,7 +2345,8 @@ bool Image::BlitScaleHelper(bool scale_up) {
bool Image::NeedsScaleHelper() const {
const auto& device = runtime->device;
- const bool needs_msaa_helper = info.num_samples > 1 && device.CantBlitMSAA();
+ const bool needs_msaa_helper = info.num_samples > 1 &&
+ (device.CantBlitMSAA() || aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT);
if (needs_msaa_helper) {
return true;
}
@@ -2515,11 +2748,76 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
renderpass_key.depth_format = PixelFormat::Invalid;
}
renderpass_key.samples = samples;
+ const bool do_resolve_color =
+ samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler();
+ renderpass_key.resolve_color = do_resolve_color;
+
+ discard_msaa_color =
+ ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color;
renderpass = runtime.render_pass_cache.Get(renderpass_key);
+ render_pass_key = renderpass_key;
+ render_pass_cache = &runtime.render_pass_cache;
render_area.width = (std::min)(render_area.width, width);
render_area.height = (std::min)(render_area.height, height);
+ if (do_resolve_color) {
+ const u32 layers = static_cast<u32>((std::max)(num_layers, 1));
+ for (size_t index = 0; index < NUM_RT; ++index) {
+ const PixelFormat format = renderpass_key.color_formats[index];
+ if (format == PixelFormat::Invalid) {
+ continue;
+ }
+ const VkFormat vk_format =
+ MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format;
+ if (ENABLE_MSAA_RESOLVE_CONSUME) {
+ const VkImage msaa_image = images[rt_map[index]];
+ attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format,
+ render_area, layers));
+ continue;
+ }
+ VkImageCreateInfo resolve_ci{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = vk_format,
+ .extent = {render_area.width, render_area.height, 1},
+ .mipLevels = 1,
+ .arrayLayers = layers,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .queueFamilyIndexCount = 0,
+ .pQueueFamilyIndices = nullptr,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ };
+ vk::Image resolve_image = runtime.memory_allocator.CreateImage(resolve_ci);
+ vk::ImageView resolve_view =
+ runtime.device.GetLogical().CreateImageView(VkImageViewCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .image = *resolve_image,
+ .viewType = layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D,
+ .format = vk_format,
+ .components{},
+ .subresourceRange{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = layers,
+ },
+ });
+ attachments.push_back(*resolve_view);
+ resolve_images.push_back(std::move(resolve_image));
+ resolve_image_views.push_back(std::move(resolve_view));
+ }
+ }
+
num_color_buffers = static_cast<u32>(num_colors);
framebuffer = runtime.device.GetLogical().CreateFramebuffer({
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
@@ -2534,6 +2832,18 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
});
}
+VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
+ u32 color_discard_mask) const {
+ if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) {
+ return renderpass;
+ }
+ RenderPassKey key = render_pass_key;
+ key.color_clear_mask = color_clear_mask;
+ key.depth_stencil_clear = depth_stencil_clear;
+ key.color_discard_mask = color_discard_mask;
+ return render_pass_cache->Get(key);
+}
+
void TextureCacheRuntime::AccelerateImageUpload(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 13f51555a3..99f2978cca 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -12,6 +12,7 @@
#include "shader_recompiler/shader_info.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
+#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/texture_cache/image_view_base.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
@@ -87,7 +88,7 @@ public:
}
bool CanUploadMSAA() const noexcept {
- return msaa_copy_pass.operator bool();
+ return true;
}
void AccelerateImageUpload(Image&, const StagingBufferRef&,
@@ -110,6 +111,24 @@ public:
[[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size);
+ struct ResolveShadow {
+ vk::Image image;
+ vk::ImageView view;
+ VkFormat format = VK_FORMAT_UNDEFINED;
+ VkExtent2D extent{};
+ u32 layers = 0;
+ bool up_to_date = false;
+ };
+
+ [[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
+ VkExtent2D extent, u32 layers);
+
+ [[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
+
+ void InvalidateResolveShadow(VkImage msaa_image);
+
+ void EraseResolveShadow(VkImage msaa_image);
+
std::span<const VkFormat> ViewFormats(PixelFormat format) {
return view_formats[static_cast<std::size_t>(format)];
}
@@ -130,12 +149,13 @@ public:
std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
- std::optional<MSAACopyPass> msaa_copy_pass;
const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
std::array<vk::Buffer, indexing_slots> buffers{};
+ std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
+ ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
};
class Framebuffer {
@@ -166,6 +186,13 @@ public:
return renderpass;
}
+ [[nodiscard]] const RenderPassKey& RenderPassKeyBase() const noexcept {
+ return render_pass_key;
+ }
+
+ [[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
+ u32 color_discard_mask) const;
+
[[nodiscard]] VkExtent2D RenderArea() const noexcept {
return render_area;
}
@@ -206,6 +233,18 @@ public:
return is_rescaled;
}
+ [[nodiscard]] bool HasResolveColor() const noexcept {
+ return !resolve_images.empty();
+ }
+
+ [[nodiscard]] VkImage ResolveColorImage(size_t index) const noexcept {
+ return index < resolve_images.size() ? *resolve_images[index] : VK_NULL_HANDLE;
+ }
+
+ [[nodiscard]] bool DiscardsMsaaColor() const noexcept {
+ return discard_msaa_color;
+ }
+
private:
vk::Framebuffer framebuffer;
VkRenderPass renderpass{};
@@ -219,6 +258,11 @@ private:
bool has_depth{};
bool has_stencil{};
bool is_rescaled{};
+ std::vector<vk::Image> resolve_images;
+ std::vector<vk::ImageView> resolve_image_views;
+ RenderPassKey render_pass_key{};
+ RenderPassCache* render_pass_cache{nullptr};
+ bool discard_msaa_color{};
};
class Image : public VideoCommon::ImageBase {