diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 4ee2c86418..2cfdbf5d97 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -33,6 +33,8 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/convert_float_to_depth.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_sint.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_uint.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag new file mode 100644 index 0000000000..26fdd48b36 --- /dev/null +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform sampler2DMS msaa_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset; + const ivec2 msaa_coord = coord / scale; + const ivec2 sample_offset = coord % scale; + const int sample_id = sample_offset.x + scale.x * sample_offset.y; + gl_FragDepth = texelFetch(msaa_in, msaa_coord, sample_id).r; +} diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag new file mode 100644 index 0000000000..cd34a1a025 --- /dev/null +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core +#extension GL_ARB_shader_stencil_export : require + +layout(binding = 0) uniform sampler2DMS depth_tex; +layout(binding = 1) uniform usampler2DMS stencil_tex; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset; + const ivec2 msaa_coord = coord / scale; + const ivec2 sample_offset = coord % scale; + const int sample_id = sample_offset.x + scale.x * sample_offset.y; + gl_FragDepth = texelFetch(depth_tex, msaa_coord, sample_id).r; + gl_FragStencilRefARB = int(texelFetch(stencil_tex, msaa_coord, sample_id).r); +} diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index b6d395ebc4..5d82bfe3b8 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -22,6 +22,8 @@ #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_msaa_to_non_msaa_depth_frag_spv.h" +#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil_frag_spv.h" #include "video_core/host_shaders/convert_msaa_to_non_msaa_sint_frag_spv.h" #include "video_core/host_shaders/convert_msaa_to_non_msaa_uint_frag_spv.h" #include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h" @@ -636,6 +638,10 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_SINT_FRAG_SPV)), convert_msaa_to_non_msaa_uint_frag( BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_UINT_FRAG_SPV)), + convert_msaa_to_non_msaa_depth_frag( + BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_FRAG_SPV)), + convert_msaa_to_non_msaa_depth_stencil_frag( + BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_STENCIL_FRAG_SPV)), convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)), convert_non_msaa_to_msaa_sint_frag( BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_SINT_FRAG_SPV)), @@ -1606,14 +1612,15 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage VideoCore::Surface::PixelFormat dst_format, VkImage src_image, VideoCore::Surface::PixelFormat src_format, u32 num_samples, std::span copies, - bool copy_stencil) { + bool copy_stencil, 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(num_samples)); const s32 scale_x = 1 << samples_x; const s32 scale_y = 1 << samples_y; - const VkSampleCountFlagBits samples = SampleCountFlag(num_samples); + 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.depth_format = dst_format; @@ -1622,7 +1629,7 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage const MSAACopyPipelineKey key{ .renderpass = renderpass, .samples = samples, - .msaa_to_non_msaa = false, + .msaa_to_non_msaa = msaa_to_non_msaa, .format_class = MSAACopyFormatClass::Float, }; const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil); @@ -1859,16 +1866,22 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPip return *pipelines[std::distance(keys.begin(), it)]; } keys.push_back(key); - const std::array stages = - MakeStages(*clear_color_vert, copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag - : *convert_non_msaa_to_msaa_depth_frag); + VkShaderModule frag_module; + if (key.msaa_to_non_msaa) { + frag_module = copy_stencil ? *convert_msaa_to_non_msaa_depth_stencil_frag + : *convert_msaa_to_non_msaa_depth_frag; + } else { + frag_module = copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag + : *convert_non_msaa_to_msaa_depth_frag; + } + const std::array stages = MakeStages(*clear_color_vert, frag_module); 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, + .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, diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 66a70c6215..d4fdf9bc13 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -132,7 +132,8 @@ public: void CopyMSAADepth(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 copies, bool copy_stencil); + std::span copies, bool copy_stencil, + bool msaa_to_non_msaa); private: void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, @@ -207,6 +208,8 @@ private: vk::ShaderModule convert_msaa_to_non_msaa_frag; vk::ShaderModule convert_msaa_to_non_msaa_sint_frag; vk::ShaderModule convert_msaa_to_non_msaa_uint_frag; + vk::ShaderModule convert_msaa_to_non_msaa_depth_frag; + vk::ShaderModule convert_msaa_to_non_msaa_depth_stencil_frag; vk::ShaderModule convert_non_msaa_to_msaa_frag; vk::ShaderModule convert_non_msaa_to_msaa_sint_frag; vk::ShaderModule convert_non_msaa_to_msaa_uint_frag; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 29fe7db424..eeb58b9919 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -854,12 +854,10 @@ void RasterizerVulkan::WaitForIdle() { // fragment shaders can still write storage buffers. VkPipelineStageFlags flags = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; - if (device.SupportsTessellationShader()) { - flags |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | - VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; - } + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT; if (device.SupportsGeometryShader()) { flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 432117ab0f..35a04bd808 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1749,8 +1749,20 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, return; } } - if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT || - VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) { + const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); + if ((dst_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { + const bool copies_stencil = (dst_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + device.IsExtShaderStencilExportSupported(); + if ((dst_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && !copies_stencil) { + UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); + return; + } + blit_image_helper.CopyMSAADepth(render_pass_cache, dst.Handle(), dst.info.format, + src.Handle(), src.info.format, num_samples, copies, + copies_stencil, msaa_to_non_msaa); + return; + } + if ((dst_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) == 0) { UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); return; } @@ -1767,7 +1779,17 @@ u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { } bool TextureCacheRuntime::CanDownloadMsaa(const VideoCommon::ImageInfo& info) const { - return ImageAspectMask(info.format) == VK_IMAGE_ASPECT_COLOR_BIT; + const VkImageAspectFlags aspect_mask = ImageAspectMask(info.format); + if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + return true; + } + if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0) { + return false; + } + if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) { + return device.IsExtShaderStencilExportSupported(); + } + return true; } bool TextureCacheRuntime::CanReportMemoryUsage() const { @@ -1953,7 +1975,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(), info.format, temp_vk_image, info.format, info.num_samples, image_copies, - msaa_upload_copies_stencil); + msaa_upload_copies_stencil, false); } else { runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format, temp_vk_image, info.format, info.num_samples, @@ -2024,33 +2046,53 @@ void Image::DownloadMemory(std::span buffers_span, std::span o 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; + const bool msaa_download_is_depth = + (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; + const bool msaa_download_copies_stencil = + msaa_download_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + runtime->device.IsExtShaderStencilExportSupported(); + image_ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (msaa_download_is_depth) { + image_ci.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + image_ci.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); const VkImage temp_vk_image = *temp_image; + const VkImageAspectFlags temp_aspect_mask = aspect_mask; + const VkAccessFlags attachment_access = + msaa_download_is_depth ? (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) + : (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + const VkPipelineStageFlags attachment_stage = + msaa_download_is_depth ? (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT) + : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + scheduler->RequestOutsideRenderPassOperationContext(); - scheduler->Record([temp_vk_image](vk::CommandBuffer cmdbuf) { + scheduler->Record([temp_vk_image, temp_aspect_mask, attachment_access, + attachment_stage](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, + .dstAccessMask = attachment_access, .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, + .aspectMask = temp_aspect_mask, .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, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, attachment_stage, 0, init_barrier); }); @@ -2065,9 +2107,15 @@ void Image::DownloadMemory(std::span buffers_span, std::span o image_copies.push_back(image_copy); } - runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, temp_vk_image, - info.format, Handle(), info.format, - info.num_samples, image_copies, true); + if (msaa_download_is_depth) { + runtime->blit_image_helper.CopyMSAADepth( + runtime->render_pass_cache, temp_vk_image, info.format, Handle(), info.format, + info.num_samples, image_copies, msaa_download_copies_stencil, true); + } else { + 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 buffers_vector{}; boost::container::small_vector, 8> diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 6f27e74e28..d32aa95668 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -1023,25 +1023,19 @@ FN_MAX_LIMIT_LIST return features2.features.multiViewport; } - bool SupportsTessellationShader() const { - return features2.features.tessellationShader; - } - bool SupportsGeometryShader() const { return features2.features.geometryShader; } VkPipelineStageFlags AttachmentConsumerStages() const { VkPipelineStageFlags stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - if (features2.features.tessellationShader) { - stages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | - VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; - } if (features2.features.geometryShader) { stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; }