From 607bc18ad4382e6cc84f2df91199ebb3f65836e3 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 10 Aug 2026 01:29:27 -0400 Subject: [PATCH 01/72] [TEST] Bring the unswizzle work from tiled-gpu-v2 --- src/video_core/host_shaders/CMakeLists.txt | 2 + .../block_linear_unswizzle_2d_buffer.comp | 104 ++++ .../block_linear_unswizzle_3d_buffer.comp | 105 ++++ .../renderer_vulkan/vk_compute_pass.cpp | 477 ++++++++++++++++++ .../renderer_vulkan/vk_compute_pass.h | 38 ++ .../renderer_vulkan/vk_texture_cache.cpp | 20 + .../renderer_vulkan/vk_texture_cache.h | 2 + 7 files changed, 748 insertions(+) create mode 100644 src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp create mode 100644 src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 5b8f47a7a6..6be3e3bd06 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -17,11 +17,13 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp + ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp + ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag diff --git a/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp new file mode 100644 index 0000000000..3fc71e05b6 --- /dev/null +++ b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 430 + +#extension GL_EXT_shader_16bit_storage : require +#extension GL_EXT_shader_8bit_storage : require + +#define BINDING_INPUT_BUFFER 0 +#define BINDING_OUTPUT_BUFFER 1 + +layout(push_constant) uniform PushConstants { + uvec3 dim; + uint bytes_per_block_log2; + + uvec3 origin; + uint layer_stride; + + uint block_size; + uint x_shift; + uint block_height; + uint block_height_mask; +} pc; + +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; + +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { + uint out_u32[]; +}; + +layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT; + +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); + +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + +uint SwizzleOffset(uvec2 pos) { + pos = pos & SWIZZLE_MASK; + return SwizzleTable(pos.y * 64u + pos.x); +} + +uvec4 ReadTexel(uint offset) { + switch (pc.bytes_per_block_log2) { + case 2u: + return uvec4(u32data[offset / 4u], 0u, 0u, 0u); + case 3u: + return uvec4(u64data[offset / 8u], 0u, 0u); + case 4u: + return u128data[offset / 16u]; + } + return uvec4(0u); +} + +void main() { + uvec3 coord = gl_GlobalInvocationID; + if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) { + return; + } + + uvec3 pos = coord + pc.origin; + pos.x <<= pc.bytes_per_block_log2; + + uint swizzle = SwizzleOffset(pos.xy); + uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + + uint offset = 0u; + offset += pos.z * pc.layer_stride; + offset += (block_y >> pc.block_height) * pc.block_size; + offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift; + offset += swizzle; + + uvec4 texel = ReadTexel(offset); + + uint words = 1u << (pc.bytes_per_block_log2 - 2u); + uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y; + uint out_idx = linear_index * words; + + out_u32[out_idx] = texel.x; + if (words > 1u) { + out_u32[out_idx + 1u] = texel.y; + } + if (words > 2u) { + out_u32[out_idx + 2u] = texel.z; + out_u32[out_idx + 3u] = texel.w; + } +} diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp new file mode 100644 index 0000000000..7333568eb7 --- /dev/null +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 430 + +#define BINDING_INPUT_BUFFER 0 +#define BINDING_OUTPUT_BUFFER 1 + +layout(push_constant) uniform PushConstants { + uvec3 dim; + uint bytes_per_block_log2; + + uvec3 origin; + uint slice_size; + + uint block_size; + uint x_shift; + uint block_height; + uint block_height_mask; + + uint block_depth; + uint block_depth_mask; +} pc; + +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; + +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { + uint out_u32[]; +}; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT; + +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); + +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + +uint SwizzleOffset(uvec2 pos) { + pos = pos & SWIZZLE_MASK; + return SwizzleTable(pos.y * 64u + pos.x); +} + +uvec4 ReadTexel(uint offset) { + switch (pc.bytes_per_block_log2) { + case 2u: + return uvec4(u32data[offset / 4u], 0u, 0u, 0u); + case 3u: + return uvec4(u64data[offset / 8u], 0u, 0u); + case 4u: + return u128data[offset / 16u]; + } + return uvec4(0u); +} + +void main() { + uvec3 coord = gl_GlobalInvocationID; + if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) { + return; + } + + uvec3 pos = coord + pc.origin; + pos.x <<= pc.bytes_per_block_log2; + + uint swizzle = SwizzleOffset(pos.xy); + uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + + uint offset = 0u; + offset += (pos.z >> pc.block_depth) * pc.slice_size; + offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height); + offset += (block_y >> pc.block_height) * pc.block_size; + offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift; + offset += swizzle; + + uvec4 texel = ReadTexel(offset); + + uint words = 1u << (pc.bytes_per_block_log2 - 2u); + uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y; + uint out_idx = linear_index * words; + + out_u32[out_idx] = texel.x; + if (words > 1u) { + out_u32[out_idx + 1u] = texel.y; + } + if (words > 2u) { + out_u32[out_idx + 2u] = texel.z; + out_u32[out_idx + 3u] = texel.w; + } +} diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 80c434dc8f..084e2b57ee 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -22,7 +22,9 @@ #include "video_core/host_shaders/resolve_conditional_render_comp_spv.h" #include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/surface.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -872,4 +874,479 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( }); } +namespace { +constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0; +constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1; + +struct alignas(16) BlockLinearUnswizzle2DPushConstants { + std::array dim; + u32 bytes_per_block_log2; + std::array origin; + u32 layer_stride; + u32 block_size; + u32 x_shift; + u32 block_height; + u32 block_height_mask; +}; +static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128); + +constexpr std::array BL2D_BINDINGS{{ + { + .binding = BL2D_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = BL2D_BINDING_OUTPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, +}}; + +constexpr std::array BL2D_TEMPLATE{{ + { + .dstBinding = BL2D_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = BL2D_BINDING_OUTPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, +}}; + +constexpr DescriptorBankInfo BL2D_BANK_INFO{ + .uniform_buffers = 0, + .storage_buffers = 2, + .texture_buffers = 0, + .image_buffers = 0, + .textures = 0, + .images = 0, + .score = 2, +}; +} // Anonymous namespace + +BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE, + BL2D_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV), + scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default; + +bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) { + if (info.type != VideoCommon::ImageType::e2D) { + return false; + } + if (info.resources.levels != 1 || info.resources.layers != 1) { + return false; + } + if (info.num_samples > 1) { + return false; + } + if (VideoCore::Surface::IsPixelFormatASTC(info.format) || + VideoCore::Surface::IsPixelFormatBCn(info.format)) { + return false; + } + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); + if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) { + return false; + } + return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 && + VideoCore::Surface::DefaultBlockHeight(info.format) == 1; +} + +void BlockLinearUnswizzle2DPass::Unswizzle( + Image& image, const StagingBufferRef& swizzled, + std::span swizzles) { + if (swizzles.empty()) { + return; + } + const VideoCommon::SwizzleParameters& sw = swizzles.front(); + const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info); + + const u32 width = sw.num_tiles.width; + const u32 height = sw.num_tiles.height; + const u32 depth = image.info.resources.layers; + const u32 bytes_per_block = 1u << params.bytes_per_block_log2; + const VkDeviceSize output_size = + static_cast(width) * height * depth * bytes_per_block; + + const StagingBufferRef output = + staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); + + BlockLinearUnswizzle2DPushConstants pc{}; + pc.dim = {width, height, depth}; + pc.bytes_per_block_log2 = params.bytes_per_block_log2; + pc.origin = params.origin; + pc.layer_stride = params.layer_stride; + pc.block_size = params.block_size; + pc.x_shift = params.x_shift; + pc.block_height = params.block_height; + pc.block_height_mask = params.block_height_mask; + + scheduler.RequestOutsideRenderPassOperationContext(); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset, + image.guest_size_bytes - sw.buffer_offset); + compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); + + const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); + const VkDescriptorSet set = descriptor_allocator.Commit(); + + const u32 gx = Common::DivCeil(width, 16u); + const u32 gy = Common::DivCeil(height, 8u); + const bool is_initialized = image.ExchangeInitialization(); + + const VkBuffer out_buffer = output.buffer; + const VkDeviceSize out_offset = output.offset; + const VkImage dst_image = image.Handle(); + const VkImageAspectFlags aspect = image.AspectMask(); + + scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer, + out_offset, dst_image, aspect, width, height, + is_initialized](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { + return; + } + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); + cmdbuf.Dispatch(gx, gy, depth); + + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = out_offset, + .size = output_size, + }; + const VkImageMemoryBarrier pre_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT + : VK_ACCESS_NONE), + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE + : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + + const VkBufferImageCopy copy{ + .bufferOffset = out_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = aspect, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = depth, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = {width, height, 1}, + }; + cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + + const VkImageMemoryBarrier post_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_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{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, {}, {}, post_copy); + }); +} + +namespace { +constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0; +constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1; + +struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants { + std::array dim; + u32 bytes_per_block_log2; + std::array origin; + u32 slice_size; + u32 block_size; + u32 x_shift; + u32 block_height; + u32 block_height_mask; + u32 block_depth; + u32 block_depth_mask; +}; +static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128); + +constexpr std::array BL3DB_BINDINGS{{ + { + .binding = BL3DB_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = BL3DB_BINDING_OUTPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, +}}; + +constexpr std::array BL3DB_TEMPLATE{{ + { + .dstBinding = BL3DB_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = BL3DB_BINDING_OUTPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, +}}; + +constexpr DescriptorBankInfo BL3DB_BANK_INFO{ + .uniform_buffers = 0, + .storage_buffers = 2, + .texture_buffers = 0, + .image_buffers = 0, + .textures = 0, + .images = 0, + .score = 2, +}; +} // Anonymous namespace + +BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE, + BL3DB_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV), + scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default; + +bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device, + const VideoCommon::ImageInfo& info) { + if (info.type != VideoCommon::ImageType::e3D) { + return false; + } + if (info.resources.levels != 1 || info.resources.layers != 1) { + return false; + } + if (info.num_samples > 1) { + return false; + } + if (info.size.depth <= 1) { + return false; + } + if (VideoCore::Surface::IsPixelFormatASTC(info.format)) { + return false; + } + if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) { + return false; + } + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); + return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16; +} + +void BlockLinearUnswizzle3DBufferPass::Unswizzle( + Image& image, const StagingBufferRef& swizzled, + std::span swizzles) { + if (swizzles.empty()) { + return; + } + + const VideoCommon::SwizzleParameters& sw = swizzles.front(); + const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info); + + const u32 blocks_x = sw.num_tiles.width; + const u32 blocks_y = sw.num_tiles.height; + const u32 blocks_z = sw.num_tiles.depth; + const u32 bytes_per_block = 1u << params.bytes_per_block_log2; + const VkDeviceSize output_size = + static_cast(blocks_x) * blocks_y * blocks_z * bytes_per_block; + + const StagingBufferRef output = + staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); + + BlockLinearUnswizzle3DBufferPushConstants pc{}; + pc.dim = {blocks_x, blocks_y, blocks_z}; + pc.bytes_per_block_log2 = params.bytes_per_block_log2; + pc.origin = params.origin; + pc.slice_size = params.slice_size; + pc.block_size = params.block_size; + pc.x_shift = params.x_shift; + pc.block_height = params.block_height; + pc.block_height_mask = params.block_height_mask; + pc.block_depth = params.block_depth; + pc.block_depth_mask = params.block_depth_mask; + + scheduler.RequestOutsideRenderPassOperationContext(); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset, + image.guest_size_bytes - sw.buffer_offset); + compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); + + const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); + const VkDescriptorSet set = descriptor_allocator.Commit(); + + const u32 gx = Common::DivCeil(blocks_x, 8u); + const u32 gy = Common::DivCeil(blocks_y, 8u); + const u32 gz = Common::DivCeil(blocks_z, 4u); + const bool is_initialized = image.ExchangeInitialization(); + + const VkBuffer out_buffer = output.buffer; + const VkDeviceSize out_offset = output.offset; + const VkImage dst_image = image.Handle(); + const VkImageAspectFlags aspect = image.AspectMask(); + const VkExtent3D extent{ + .width = image.info.size.width, + .height = image.info.size.height, + .depth = image.info.size.depth, + }; + + scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer, + out_offset, dst_image, aspect, extent, + is_initialized](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { + return; + } + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); + cmdbuf.Dispatch(gx, gy, gz); + + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = out_offset, + .size = output_size, + }; + const VkImageMemoryBarrier pre_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT + : VK_ACCESS_NONE), + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE + : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + + const VkBufferImageCopy copy{ + .bufferOffset = out_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = aspect, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = extent, + }; + cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + + const VkImageMemoryBarrier post_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_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{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, {}, {}, post_copy); + }); +} + } // 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 3d30aa6bdc..5150e95b90 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -164,4 +164,42 @@ private: ComputePassDescriptorQueue& compute_pass_descriptor_queue; }; +class BlockLinearUnswizzle2DPass final : public ComputePass { +public: + explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~BlockLinearUnswizzle2DPass(); + + [[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info); + + void Unswizzle(Image& image, const StagingBufferRef& swizzled, + std::span swizzles); + +private: + Scheduler& scheduler; + StagingBufferPool& staging_buffer_pool; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + +class BlockLinearUnswizzle3DBufferPass final : public ComputePass { +public: + explicit BlockLinearUnswizzle3DBufferPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~BlockLinearUnswizzle3DBufferPass(); + + [[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info); + + void Unswizzle(Image& image, const StagingBufferRef& swizzled, + std::span swizzles); + +private: + Scheduler& scheduler; + StagingBufferPool& staging_buffer_pool; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index bd3117037f..db9c5bf497 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -933,6 +933,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue); } + bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, + compute_pass_descriptor_queue); + bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, + compute_pass_descriptor_queue); } void TextureCacheRuntime::Finish() { @@ -1796,6 +1800,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu } flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::CostlyLoad; + } else if (runtime->bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(info)) { + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; + flags |= VideoCommon::ImageFlagBits::CostlyLoad; + } else if (runtime->bl3db_unswizzle_pass && + BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) { + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; + flags |= VideoCommon::ImageFlagBits::CostlyLoad; } if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) { flags |= VideoCommon::ImageFlagBits::Converted; @@ -2843,6 +2854,15 @@ void TextureCacheRuntime::AccelerateImageUpload( return astc_decoder_pass->Assemble(image, map, swizzles); } + if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(image.info)) { + return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles); + } + + if (bl3db_unswizzle_pass && + BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) { + return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles); + } + if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 4b4900e5d6..f183cc160a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -149,6 +149,8 @@ public: std::optional astc_decoder_pass; std::optional bl3d_unswizzle_pass; + std::optional bl2d_unswizzle_pass; + std::optional bl3db_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; From 3423d3c6a56deecdda0b84a9568cc58dd2c34817 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 10 Aug 2026 01:41:21 -0400 Subject: [PATCH 02/72] [TEST] fast path on swizzle decoding --- src/video_core/textures/decoders.cpp | 94 +++++++++++++++++----------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 25b0860b0f..ffba46181c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -1,9 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -30,12 +31,63 @@ constexpr u32 pdep(u32 value) { return result; } +constexpr u32 SWIZZLE_RUN_BYTES = 16; +constexpr u32 SWIZZLE_RUN_SHIFT = 4; +constexpr u32 SWIZZLE_RUN_MASK = SWIZZLE_RUN_BYTES - 1; +constexpr u32 SWIZZLE_RUN_INDEX_MASK = GOB_SIZE_X / SWIZZLE_RUN_BYTES - 1; + +static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK, + "A swizzled run is only contiguous while the low bits of X map to themselves"); + +constexpr std::array SWIZZLE_X_RUN_TABLE = [] { + std::array table{}; + for (u32 index = 0; index < static_cast(table.size()); ++index) { + table[index] = pdep(index << SWIZZLE_RUN_SHIFT); + } + return table; +}(); + template void incrpdep(u32& value) { static constexpr u32 swizzled_incr = pdep(incr_amount); value = ((value | ~mask) + swizzled_incr) & mask; } +template +void SwizzleRow(std::span output, std::span input, u32 offset_zy, u32 swizzled_y, + u32 x_shift, u32 x, u32 num_bytes, u32 linear) { + const auto copy = [&](u32 swizzled_x, u32 count) { + const u32 swizzled = + offset_zy + ((x >> GOB_SIZE_X_SHIFT) << x_shift) + (swizzled_x | swizzled_y); + u8* const dst = &output[TO_LINEAR ? swizzled : linear]; + const u8* const src = &input[TO_LINEAR ? linear : swizzled]; + std::memcpy(dst, src, count); + x += count; + linear += count; + }; + + u32 swizzled_run = SWIZZLE_X_RUN_TABLE[(x >> SWIZZLE_RUN_SHIFT) & SWIZZLE_RUN_INDEX_MASK]; + u32 remaining = num_bytes; + + const u32 head = + (std::min)(SWIZZLE_RUN_BYTES - (x & SWIZZLE_RUN_MASK), remaining) & SWIZZLE_RUN_MASK; + if (head != 0) { + copy(swizzled_run | (x & SWIZZLE_RUN_MASK), head); + remaining -= head; + incrpdep(swizzled_run); + } + + while (remaining >= SWIZZLE_RUN_BYTES) { + copy(swizzled_run, SWIZZLE_RUN_BYTES); + remaining -= SWIZZLE_RUN_BYTES; + incrpdep(swizzled_run); + } + + if (remaining != 0) { + copy(swizzled_run, remaining); + } +} + template void SwizzleImpl(std::span output, std::span input, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride) { @@ -70,23 +122,9 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 const u32 offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); - for (u32 column = 0; column < width; - ++column, incrpdep(swizzled_x)) { - const u32 x = (column + origin_x) * BYTES_PER_PIXEL; - const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; - - const u32 base_swizzled_offset = offset_z + offset_y + offset_x; - const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); - - const u32 unswizzled_offset = - slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; - - u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; - const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; - - std::memcpy(dst, src, BYTES_PER_PIXEL); - } + SwizzleRow(output, input, offset_z + offset_y, swizzled_y, x_shift, + origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL, + slice * pitch * height + line * pitch); } } } @@ -129,23 +167,9 @@ void SwizzleSubrectImpl(std::span output, std::span input, u32 wid const u32 offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); - for (u32 column = 0; column < extent_x; - ++column, incrpdep(swizzled_x)) { - const u32 x = (column + origin_x) * BYTES_PER_PIXEL; - const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; - - const u32 base_swizzled_offset = offset_z + offset_y + offset_x; - const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); - - const u32 unswizzled_offset = - slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; - - u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; - const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; - - std::memcpy(dst, src, BYTES_PER_PIXEL); - } + SwizzleRow(output, input, offset_z + offset_y, swizzled_y, x_shift, + origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL, + slice * pitch * height + line * pitch); } unprocessed_lines -= lines_in_y; if (unprocessed_lines == 0) { From 35aeb0cf556489aa603e6509ee154ee349748235 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 10 Aug 2026 02:57:05 -0400 Subject: [PATCH 03/72] [TEST] Playing with SGSR --- .../settings/model/view/SettingsItem.kt | 1 + .../yuzu_emu/fragments/EmulationFragment.kt | 2 +- src/video_core/host_shaders/sgsr1_shader.vert | 3 +- .../host_shaders/sgsr1_shader_mobile.frag | 1 + .../sgsr1_shader_mobile_edge_direction.frag | 1 + .../renderer_vulkan/present/sgsr.cpp | 32 +++++++++++-------- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 0ca31062b3..c05539c306 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -624,6 +624,7 @@ abstract class SettingsItem( IntSetting.FSR_SHARPENING_SLIDER, titleId = R.string.fsr_sharpness, descriptionId = R.string.fsr_sharpness_description, + max = 200, units = "%" ) ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 5b93c88161..016ac57356 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { container, IntSetting.FSR_SHARPENING_SLIDER, minValue = 0, - maxValue = 100, + maxValue = 200, units = "%" ) } diff --git a/src/video_core/host_shaders/sgsr1_shader.vert b/src/video_core/host_shaders/sgsr1_shader.vert index 0d2a935a9d..e995997060 100644 --- a/src/video_core/host_shaders/sgsr1_shader.vert +++ b/src/video_core/host_shaders/sgsr1_shader.vert @@ -7,6 +7,7 @@ layout(push_constant) uniform constants { vec2 scale; vec2 size; vec2 resize_factor; + vec2 crop_offset; float edge_sharpness; }; layout(location = 0) out highp vec2 texcoord; @@ -15,5 +16,5 @@ void main() { float x = float((gl_VertexIndex & 1) << 2); float y = float((gl_VertexIndex & 2) << 1); gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f); - texcoord = vec2(x, y) * abs(resize_factor) * 0.5; + texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5; } diff --git a/src/video_core/host_shaders/sgsr1_shader_mobile.frag b/src/video_core/host_shaders/sgsr1_shader_mobile.frag index 2e62d60af3..3453ca2cae 100644 --- a/src/video_core/host_shaders/sgsr1_shader_mobile.frag +++ b/src/video_core/host_shaders/sgsr1_shader_mobile.frag @@ -14,6 +14,7 @@ layout(push_constant) uniform constants { vec2 scale; vec2 size; vec2 resize_factor; + vec2 crop_offset; float edge_sharpness; }; layout(set = 0, binding = 0) uniform sampler2D sampler0; diff --git a/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag b/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag index 10a8ff8e00..66417e9c46 100644 --- a/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag +++ b/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag @@ -13,6 +13,7 @@ layout( push_constant ) uniform constants { vec4 ViewportInfo[1]; vec2 ResizeFactor; + vec2 CropOffset; float EdgeSharpness; }; layout(set = 0, binding = 0) uniform sampler2D ps0; diff --git a/src/video_core/renderer_vulkan/present/sgsr.cpp b/src/video_core/renderer_vulkan/present/sgsr.cpp index 5fc746458d..fa37225e43 100644 --- a/src/video_core/renderer_vulkan/present/sgsr.cpp +++ b/src/video_core/renderer_vulkan/present/sgsr.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include + #include "common/common_types.h" #include "common/div_ceil.h" #include "common/settings.h" @@ -17,7 +19,7 @@ namespace Vulkan { -using PushConstants = std::array; +using PushConstants = std::array; SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir) : m_memory_allocator{memory_allocator} @@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_ const f32 input_image_width = f32(input_image_extent.width); const f32 input_image_height = f32(input_image_extent.height); - const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; - const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; - // expected [0, 2] - const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width; + const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height; + static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f; + const f32 edge_sharpness = + EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f; - // p = (tex * viewport) / input = [0,n] (normalized texcoords) - // p * input = [0,1024], [0,768] // layout( push_constant ) uniform constants { // highp vec4 ViewportInfo[1]; // highp vec2 ResizeFactor; + // highp vec2 CropOffset; // highp float EdgeSharpness; // }; PushConstants viewport_con{}; - viewport_con[0] = std::bit_cast(std::abs(1.f / viewport_width)); - viewport_con[1] = std::bit_cast(std::abs(1.f / viewport_height)); - viewport_con[2] = std::bit_cast(std::abs(viewport_width)); - viewport_con[3] = std::bit_cast(std::abs(viewport_height)); - viewport_con[4] = std::bit_cast(viewport_width / input_image_width); - viewport_con[5] = std::bit_cast(viewport_height / input_image_height); - viewport_con[6] = std::bit_cast(sharpening); + viewport_con[0] = std::bit_cast(1.f / input_image_width); + viewport_con[1] = std::bit_cast(1.f / input_image_height); + viewport_con[2] = std::bit_cast(input_image_width); + viewport_con[3] = std::bit_cast(input_image_height); + viewport_con[4] = std::bit_cast(crop_width / input_image_width); + viewport_con[5] = std::bit_cast(crop_height / input_image_height); + viewport_con[6] = std::bit_cast((std::min)(crop_rect.left, crop_rect.right)); + viewport_con[7] = std::bit_cast((std::min)(crop_rect.top, crop_rect.bottom)); + viewport_con[8] = std::bit_cast(edge_sharpness); UploadImages(device, scheduler); UpdateDescriptorSets(device, source_image_view, image_index); From db647f9a402a027a88a043ae5034c018baa04130 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 10 Aug 2026 03:30:58 -0400 Subject: [PATCH 04/72] [TEST] Another bind vertex buffer optimization from previous handling --- src/video_core/buffer_cache/buffer_cache.h | 55 +++++++++++----------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e40aad1fb5..95c67f865a 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -809,46 +810,46 @@ void BufferCache

::BindHostVertexBuffers() { if (use_optimized_vertex_buffers) { auto& flags = maxwell3d->dirty.flags; - u32 enabled_mask = enabled_vertex_buffers_mask; - HostBindings bindings{}; - u32 last_index = (std::numeric_limits::max)(); - const auto flush_bindings = [&]() { - if (bindings.buffers.empty()) { - return; - } - bindings.max_index = bindings.min_index + static_cast(bindings.buffers.size()); - runtime.BindVertexBuffers(bindings); - bindings = HostBindings{}; - last_index = (std::numeric_limits::max)(); - }; - while (enabled_mask != 0) { - const u32 index = std::countr_zero(enabled_mask); - enabled_mask &= (enabled_mask - 1); + const u32 enabled_mask = enabled_vertex_buffers_mask; + bool any_dirty = false; + u32 pending_mask = enabled_mask; + while (pending_mask != 0) { + const u32 index = std::countr_zero(pending_mask); + pending_mask &= (pending_mask - 1); const Binding& binding = VertexBufferSlot(index); Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); SynchronizeBuffer(buffer, binding.device_addr, binding.size); - if (!flags[Dirty::VertexBuffer0 + index]) { - flush_bindings(); - continue; - } + any_dirty |= flags[Dirty::VertexBuffer0 + index]; + } + if (enabled_mask == 0 || !any_dirty) { + return; + } + const u32 min_index = static_cast(std::countr_zero(enabled_mask)); + const u32 max_index = 32u - static_cast(std::countl_zero(enabled_mask)); + HostBindings bindings{}; + bindings.min_index = min_index; + bindings.max_index = max_index; + for (u32 index = min_index; index < max_index; ++index) { flags[Dirty::VertexBuffer0 + index] = false; const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + if ((enabled_mask & (1u << index)) == 0) { + bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]); + bindings.offsets.push_back(0); + bindings.sizes.push_back(0); + bindings.strides.push_back(stride); + continue; + } + const Binding& binding = VertexBufferSlot(index); + Buffer& buffer = slot_buffers[binding.buffer_id]; const u32 offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, binding.size); - if (!bindings.buffers.empty() && index != last_index + 1) { - flush_bindings(); - } - if (bindings.buffers.empty()) { - bindings.min_index = index; - } bindings.buffers.push_back(&buffer); bindings.offsets.push_back(offset); bindings.sizes.push_back(binding.size); bindings.strides.push_back(stride); - last_index = index; } - flush_bindings(); + runtime.BindVertexBuffers(bindings); } else { HostBindings host_bindings; bool any_valid{false}; From 27942142cf5b59c81b941fcd73d948968ba32ad0 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 10 Aug 2026 03:54:18 -0400 Subject: [PATCH 05/72] [TEST] Ported PSO optimizations from tiled-gpu-v2 --- src/video_core/renderer_vulkan/blit_image.cpp | 18 ++-- .../renderer_vulkan/present/util.cpp | 2 +- .../renderer_vulkan/vk_compute_pass.cpp | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 41 ++++++- .../renderer_vulkan/vk_pipeline_cache.h | 8 ++ .../vulkan_common/vulkan_device.cpp | 100 ++++++++++++++++++ src/video_core/vulkan_common/vulkan_device.h | 9 ++ 7 files changed, 167 insertions(+), 13 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 89083d29fb..e06226e918 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1191,7 +1191,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *blit_color_pipelines.back(); } @@ -1223,7 +1223,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *blit_depth_stencil_pipelines.back(); } @@ -1276,7 +1276,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *clear_color_pipelines.back(); } @@ -1332,7 +1332,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *clear_stencil_pipelines.back(); } @@ -1375,7 +1375,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *blit_msaa_color_pipelines.back(); } @@ -1413,7 +1413,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *pipelines.back(); } @@ -1458,7 +1458,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *msaa_copy_pipelines.back(); } @@ -1499,7 +1499,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - }); + }, device.StaticPipelineCache()); } void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, @@ -1542,7 +1542,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - }); + }, device.StaticPipelineCache()); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 11250687d6..2ea7870f01 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -521,7 +521,7 @@ static vk::Pipeline CreateWrappedPipelineImpl( .subpass = 0, .basePipelineHandle = 0, .basePipelineIndex = 0, - }); + }, device.StaticPipelineCache()); } vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 084e2b57ee..7654d49aac 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -270,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor .layout = *layout, .basePipelineHandle = {}, .basePipelineIndex = 0, - }); + }, device.StaticPipelineCache()); } ComputePass::~ComputePass() = default; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 785602b33a..acb4219213 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,6 +63,8 @@ using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; constexpr u32 CACHE_VERSION = 18; +constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; +constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template @@ -699,6 +701,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading if (use_vulkan_pipeline_cache) { SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); + size_t size = 0; + vulkan_pipeline_cache.Read(&size, nullptr); + last_cache_size.store(size, std::memory_order_relaxed); + last_flush = std::chrono::steady_clock::now(); } if (state.statistics) { @@ -706,6 +712,35 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading } } +void PipelineCache::QueueVulkanPipelineCacheFlush() { + if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) { + return; + } + if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) { + return; + } + const auto now = std::chrono::steady_clock::now(); + const auto megabytes = last_cache_size.load(std::memory_order_relaxed) / (1024 * 1024); + const std::chrono::seconds interval{ + std::max(VULKAN_CACHE_FLUSH_MIN_SECONDS, megabytes)}; + if (last_flush.time_since_epoch().count() != 0 && now - last_flush < interval) { + return; + } + if (flush_in_flight.exchange(true, std::memory_order_acq_rel)) { + return; + } + pipelines_since_flush = 0; + last_flush = now; + serialization_thread.QueueWork([this] { + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); + size_t size = 0; + vulkan_pipeline_cache.Read(&size, nullptr); + last_cache_size.store(size, std::memory_order_relaxed); + flush_in_flight.store(false, std::memory_order_release); + }); +} + GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; auto& pipeline{pair->second}; @@ -744,7 +779,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( std::span envs, PipelineStatistics* statistics, bool build_in_parallel) try { auto hash = key.Hash(); - LOG_INFO(Render_Vulkan, "{:#016x}", hash); + LOG_DEBUG(Render_Vulkan, "{:#016x}", hash); size_t env_index{0}; std::array programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; @@ -880,6 +915,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { } SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION); }); + QueueVulkanPipelineCacheFlush(); return pipeline; } @@ -899,6 +935,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( SerializePipeline(key, std::array{&env_}, pipeline_cache_filename, CACHE_VERSION); }); + QueueVulkanPipelineCacheFlush(); return pipeline; } @@ -911,7 +948,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( return nullptr; } - LOG_INFO(Render_Vulkan, "{:#016x}", hash); + LOG_DEBUG(Render_Vulkan, "{:#016x}", hash); Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index ce89e981d2..9d66b663f5 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -7,6 +7,8 @@ #pragma once #include +#include +#include #include #include #include @@ -144,6 +146,8 @@ private: vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename, u32 expected_cache_version); + void QueueVulkanPipelineCacheFlush(); + const Device& device; Scheduler& scheduler; DescriptorPool& descriptor_pool; @@ -171,6 +175,10 @@ private: std::filesystem::path vulkan_pipeline_cache_filename; vk::PipelineCache vulkan_pipeline_cache; + size_t pipelines_since_flush{}; + std::chrono::steady_clock::time_point last_flush{}; + std::atomic last_cache_size{}; + std::atomic_bool flush_in_flight{}; Common::ThreadWorker workers; Common::ThreadWorker serialization_thread; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8c41a7eb67..6506bdb596 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,6 +18,8 @@ #include #include "common/assert.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "common/literals.h" #include #include "common/settings.h" @@ -393,6 +397,17 @@ std::vector ExtensionListForVulkan( return output; } +constexpr std::array STATIC_CACHE_MAGIC_NUMBER{'e', 'd', 'e', 'n', 's', 't', 'p', 'c'}; +constexpr u32 STATIC_CACHE_VERSION = 1; + +std::filesystem::path StaticPipelineCacheFilename() { + const auto shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); + if (!Common::FS::CreateDir(shader_dir)) { + return {}; + } + return shader_dir / "vulkan_static_pipelines.bin"; +} + } // Anonymous namespace void Device::RemoveExtension(bool& extension, const std::string& extension_name) { @@ -780,15 +795,100 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR vk::Check(vmaCreateAllocator(&allocator_info, &allocator)); + owns_static_pipeline_cache = surface != VkSurfaceKHR{}; + LoadStaticPipelineCache(); + // Initialize GPU logging if enabled InitializeGPULogging(); } Device::~Device() { + SaveStaticPipelineCache(); ShutdownGPULogging(); vmaDestroyAllocator(allocator); } +void Device::LoadStaticPipelineCache() { + const auto create = [this](size_t size, const void* data) { + static_pipeline_cache = logical.CreatePipelineCache({ + .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .initialDataSize = size, + .pInitialData = data, + }); + }; + if (!owns_static_pipeline_cache) { + create(0, nullptr); + return; + } + const auto filename = StaticPipelineCacheFilename(); + if (filename.empty()) { + create(0, nullptr); + return; + } + std::vector data; + try { + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + create(0, nullptr); + return; + } + file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + const size_t total = static_cast(file.tellg()); + file.seekg(0, std::ios::beg); + std::array magic{}; + u32 version{}; + if (total < magic.size() + sizeof(version)) { + create(0, nullptr); + return; + } + file.read(magic.data(), magic.size()) + .read(reinterpret_cast(&version), sizeof(version)); + if (magic != STATIC_CACHE_MAGIC_NUMBER || version != STATIC_CACHE_VERSION) { + create(0, nullptr); + return; + } + data.resize(total - magic.size() - sizeof(version)); + file.read(data.data(), static_cast(data.size())); + } catch (const std::ios_base::failure& e) { + create(0, nullptr); + return; + } + create(data.size(), data.empty() ? nullptr : data.data()); +} + +void Device::SaveStaticPipelineCache() const { + if (!owns_static_pipeline_cache || !static_pipeline_cache) { + return; + } + const auto filename = StaticPipelineCacheFilename(); + if (filename.empty()) { + return; + } + size_t size = 0; + std::vector data; + static_pipeline_cache.Read(&size, nullptr); + if (size == 0) { + return; + } + data.resize(size); + static_pipeline_cache.Read(&size, data.data()); + try { + std::ofstream file(filename, std::ios::binary | std::ios::trunc); + file.exceptions(std::ofstream::failbit); + if (!file.is_open()) { + return; + } + file.write(STATIC_CACHE_MAGIC_NUMBER.data(), STATIC_CACHE_MAGIC_NUMBER.size()) + .write(reinterpret_cast(&STATIC_CACHE_VERSION), + sizeof(STATIC_CACHE_VERSION)) + .write(data.data(), static_cast(size)); + } catch (const std::ios_base::failure& e) { + Common::FS::RemoveFile(filename); + } +} + VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, FormatType format_type) const { if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 1ae073c279..08ea697892 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -269,6 +269,10 @@ public: return physical; } + VkPipelineCache StaticPipelineCache() const noexcept { + return *static_pipeline_cache; + } + /// Returns the main graphics queue. vk::Queue GetGraphicsQueue() const { return graphics_queue; @@ -1094,6 +1098,9 @@ private: /// Returns true if the device natively supports blitting depth stencil images. bool TestDepthStencilBlits(VkFormat format) const; + void LoadStaticPipelineCache(); + void SaveStaticPipelineCache() const; + private: VkInstance instance; ///< Vulkan instance. VmaAllocator allocator; ///< VMA allocator. @@ -1102,6 +1109,8 @@ private: vk::Device logical; ///< Logical device. vk::Queue graphics_queue; ///< Main graphics queue. vk::Queue present_queue; ///< Main present queue. + vk::PipelineCache static_pipeline_cache; + bool owns_static_pipeline_cache{}; u32 instance_version{}; ///< Vulkan instance version. u32 graphics_family{}; ///< Main graphics queue family index. u32 present_family{}; ///< Main present queue family index. From 5a8fb951dd75c7f6b3afb9fdb0dac4e54bb3f3d5 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 11 Aug 2026 20:10:53 -0400 Subject: [PATCH 06/72] [TEST] Remove subpass dependency --- .../renderer_vulkan/vk_render_pass_cache.cpp | 16 ++-------------- .../renderer_vulkan/vk_texture_cache.cpp | 12 +++--------- 2 files changed, 5 insertions(+), 23 deletions(-) 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 66bade9a2e..66a7e874a0 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -145,18 +145,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .preserveAttachmentCount = 0, .pPreserveAttachments = nullptr, }; - const VkSubpassDependency dependency{ - .srcSubpass = 0, // Current subpass - .dstSubpass = 0, // Same subpass (self-dependency) - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT - }; pair->second = device->GetLogical().CreateRenderPass({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = nullptr, @@ -165,8 +153,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .pAttachments = descriptions.empty() ? nullptr : descriptions.data(), .subpassCount = 1, .pSubpasses = &subpass, - .dependencyCount = 1, - .pDependencies = &dependency, + .dependencyCount = 0, + .pDependencies = nullptr, }); return *pair->second; } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index db9c5bf497..b80194a1fe 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -628,18 +628,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im .subresourceRange = subresource_range, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier); cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies); // TODO: Move this to another API - cmdbuf.PipelineBarrier( - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, nullptr, nullptr, write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, + nullptr, nullptr, write_barrier); } [[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region, From d3bd4f42cfc85fe09c3f722c6a427d0b779ad024 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 11 Aug 2026 20:34:08 -0400 Subject: [PATCH 07/72] [TEST] Adjustments on pipeline hash collisions --- .../renderer_vulkan/vk_render_pass_cache.h | 28 +++++++++++++------ .../renderer_vulkan/vk_texture_cache.cpp | 16 ++++++++++- .../renderer_vulkan/vk_texture_cache.h | 5 ++++ src/video_core/textures/decoders.cpp | 3 +- 4 files changed, 40 insertions(+), 12 deletions(-) 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 75999a655e..4769d65f39 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -9,6 +9,7 @@ #include #include +#include "common/container_hash.h" #include "video_core/surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -31,17 +32,26 @@ struct RenderPassKey { namespace std { template <> struct hash { + static_assert(std::tuple_size_v <= 8); + static_assert(static_cast(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF); + static_assert(static_cast(VideoCore::Surface::PixelFormat::Max) <= 0xFF); + static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF); + [[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept { - size_t value = static_cast(key.depth_format) << 48; - value ^= static_cast(key.samples) << 52; - value ^= static_cast(key.resolve_color) << 63; - value ^= static_cast(key.color_clear_mask) << 54; - value ^= static_cast(key.depth_stencil_clear) << 62; - value ^= static_cast(key.color_discard_mask) << 24; - for (size_t i = 0; i < key.color_formats.size(); ++i) { - value ^= static_cast(key.color_formats[i]) << (i * 6); + u64 formats = 0; + for (size_t index = 0; index < key.color_formats.size(); ++index) { + formats |= static_cast(key.color_formats[index]) << (index * 8); } - return value; + const u64 state = static_cast(key.depth_format) | + (static_cast(key.samples) << 8) | + (static_cast(key.color_clear_mask) << 16) | + (static_cast(key.color_discard_mask) << 24) | + (static_cast(key.resolve_color) << 32) | + (static_cast(key.depth_stencil_clear) << 33); + size_t seed = 0; + Common::HashCombine(seed, formats); + Common::HashCombine(seed, state); + return seed; } }; } // namespace std diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index b80194a1fe..3db16d43ae 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2832,11 +2832,25 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) { return renderpass; } + static_assert(NUM_RT <= 8); + const u32 variant_key = color_clear_mask | (color_discard_mask << 8) | + (static_cast(depth_stencil_clear) << 16); + for (u32 index = 0; index < num_memoized_variants; ++index) { + if (variant_keys[index] == variant_key) { + return variant_render_passes[index]; + } + } 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); + const VkRenderPass variant = render_pass_cache->Get(key); + if (num_memoized_variants < variant_keys.size()) { + variant_keys[num_memoized_variants] = variant_key; + variant_render_passes[num_memoized_variants] = variant; + ++num_memoized_variants; + } + return variant; } void TextureCacheRuntime::AccelerateImageUpload( diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index f183cc160a..881fcc4de0 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -248,6 +248,8 @@ public: } private: + static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8; + vk::Framebuffer framebuffer; VkRenderPass renderpass{}; VkExtent2D render_area{}; @@ -265,6 +267,9 @@ private: RenderPassKey render_pass_key{}; RenderPassCache* render_pass_cache{nullptr}; bool discard_msaa_color{}; + mutable std::array variant_keys{}; + mutable std::array variant_render_passes{}; + mutable u32 num_memoized_variants{}; }; class Image : public VideoCommon::ImageBase { diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index ffba46181c..97458b80ca 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -36,8 +36,7 @@ constexpr u32 SWIZZLE_RUN_SHIFT = 4; constexpr u32 SWIZZLE_RUN_MASK = SWIZZLE_RUN_BYTES - 1; constexpr u32 SWIZZLE_RUN_INDEX_MASK = GOB_SIZE_X / SWIZZLE_RUN_BYTES - 1; -static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK, - "A swizzled run is only contiguous while the low bits of X map to themselves"); +static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK); constexpr std::array SWIZZLE_X_RUN_TABLE = [] { std::array table{}; From 27e7fa10d96c12f2b8c22b8f97e3c0e82486b470 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 11 Aug 2026 21:00:58 -0400 Subject: [PATCH 08/72] [TEST] Fix missing barrier + removal of required aligments checks inside swizzle textures --- .../translate/impl/texture_fetch_swizzled.cpp | 22 ------------------- .../impl/texture_gather_swizzled.cpp | 11 ---------- .../translate/impl/texture_load_swizzled.cpp | 15 ------------- .../renderer_vulkan/vk_texture_cache.cpp | 3 ++- 4 files changed, 2 insertions(+), 49 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp index 50e618c2f1..98e177a92e 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp @@ -50,12 +50,6 @@ constexpr std::array RGBA_LUT{ R | G | B | A, // }; -void CheckAlignment(IR::Reg reg, size_t alignment) { - if (!IR::IsAligned(reg, alignment)) { - throw NotImplementedException("Unaligned source register {}", reg); - } -} - template IR::Value Composite(TranslatorVisitor& v, Args... regs) { return v.ir.CompositeConstruct(v.F(regs)...); @@ -86,67 +80,53 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) { info.type.Assign(TextureType::Color2D); return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info); case 3: // 2D.LL - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::Color2D); return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {}, info); case 4: // 2D.DC - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::Color2D); info.is_depth.Assign(1); return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {}, {}, {}, info); case 5: // 2D.LL.DC - CheckAlignment(reg_a, 2); - CheckAlignment(reg_b, 2); info.type.Assign(TextureType::Color2D); info.is_depth.Assign(1); return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b + 1), v.F(reg_b), {}, info); case 6: // 2D.LZ.DC - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::Color2D); info.is_depth.Assign(1); return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), zero, {}, info); case 7: // ARRAY_2D - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::ColorArray2D); return v.ir.ImageSampleImplicitLod( handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), {}, {}, {}, info); case 8: // ARRAY_2D.LZ - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::ColorArray2D); return v.ir.ImageSampleExplicitLod( handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), zero, {}, info); case 9: // ARRAY_2D.LZ.DC - CheckAlignment(reg_a, 2); - CheckAlignment(reg_b, 2); info.type.Assign(TextureType::ColorArray2D); info.is_depth.Assign(1); return v.ir.ImageSampleDrefExplicitLod( handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), v.F(reg_b + 1), zero, {}, info); case 10: // 3D - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::Color3D); return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {}, {}, info); case 11: // 3D.LZ - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::Color3D); return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {}, info); case 12: // CUBE - CheckAlignment(reg_a, 2); info.type.Assign(TextureType::ColorCube); return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {}, {}, info); case 13: // CUBE.LL - CheckAlignment(reg_a, 2); - CheckAlignment(reg_b, 2); info.type.Assign(TextureType::ColorCube); return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), v.F(reg_b + 1), {}, info); @@ -187,12 +167,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) { case 0: return texs.dest_reg_a; case 1: - CheckAlignment(texs.dest_reg_a, 2); return texs.dest_reg_a + 1; case 2: return texs.dest_reg_b; case 3: - CheckAlignment(texs.dest_reg_b, 2); return texs.dest_reg_b + 1; } throw LogicError("Invalid store index {}", index); diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp index e4c6581450..4a27433739 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp @@ -34,12 +34,6 @@ union Encoding { BitField<36, 13, u64> cbuf_offset; }; -void CheckAlignment(IR::Reg reg, size_t alignment) { - if (!IR::IsAligned(reg, alignment)) { - throw NotImplementedException("Unaligned source register {}", reg); - } -} - IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) { const IR::U32 value{v.X(reg)}; return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true), @@ -60,18 +54,15 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) { info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0); IR::Value coords; if (tld4s.aoffi != 0) { - CheckAlignment(reg_a, 2); coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1)); IR::Value offset = MakeOffset(v, reg_b); if (tld4s.dc != 0) { - CheckAlignment(reg_b, 2); IR::F32 dref = v.F(reg_b + 1); return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info); } return v.ir.ImageGather(handle, coords, offset, {}, info); } if (tld4s.dc != 0) { - CheckAlignment(reg_a, 2); coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1)); IR::F32 dref = v.F(reg_b); return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info); @@ -86,12 +77,10 @@ IR::Reg RegStoreComponent32(u64 insn, size_t index) { case 0: return tlds4.dest_reg_a; case 1: - CheckAlignment(tlds4.dest_reg_a, 2); return tlds4.dest_reg_a + 1; case 2: return tlds4.dest_reg_b; case 3: - CheckAlignment(tlds4.dest_reg_b, 2); return tlds4.dest_reg_b + 1; } throw LogicError("Invalid store index {}", index); diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp index 0362f076af..0fd7c1cda5 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp @@ -55,12 +55,6 @@ union Encoding { BitField<53, 4, u64> encoding; }; -void CheckAlignment(IR::Reg reg, size_t alignment) { - if (!IR::IsAligned(reg, alignment)) { - throw NotImplementedException("Unaligned source register {}", reg); - } -} - IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) { const IR::U32 value{v.X(reg)}; return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true), @@ -92,38 +86,31 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) { coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b)); break; case 4: - CheckAlignment(reg_a, 2); texture_type = Shader::TextureType::Color2D; coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); offsets = MakeOffset(v, reg_b); break; case 5: - CheckAlignment(reg_a, 2); texture_type = Shader::TextureType::Color2D; coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); lod = v.X(reg_b); break; case 6: - CheckAlignment(reg_a, 2); texture_type = Shader::TextureType::Color2D; coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); multisample = v.X(reg_b); break; case 7: - CheckAlignment(reg_a, 2); texture_type = Shader::TextureType::Color3D; coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b)); break; case 8: { - CheckAlignment(reg_b, 2); const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))}; texture_type = Shader::TextureType::ColorArray2D; coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array); break; } case 12: - CheckAlignment(reg_a, 2); - CheckAlignment(reg_b, 2); texture_type = Shader::TextureType::Color2D; coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); lod = v.X(reg_b); @@ -166,12 +153,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) { case 0: return tlds.dest_reg_a; case 1: - CheckAlignment(tlds.dest_reg_a, 2); return tlds.dest_reg_a + 1; case 2: return tlds.dest_reg_b; case 3: - CheckAlignment(tlds.dest_reg_b, 2); return tlds.dest_reg_b + 1; } throw LogicError("Invalid store index {}", index); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 3db16d43ae..db4e8a00a3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1193,7 +1193,8 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER, {}, middle_out_barrier); - cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies); + cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + vk_out_copies); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, post_barriers); }); From ceffda663c894208e6a466f528bb0b45b7d3c991 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 11 Aug 2026 21:30:31 -0400 Subject: [PATCH 09/72] [TEST] Fix TFB subdependency --- src/video_core/renderer_vulkan/vk_query_cache.cpp | 6 ++++++ .../renderer_vulkan/vk_render_pass_cache.cpp | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index a12dc00771..24ea8e5ce9 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -725,6 +725,9 @@ public: if (!device.IsExtTransformFeedbackSupported()) { return; } + if (!scheduler.IsRenderPassActive()) { + return; + } FlushBeginTFB(); has_started = true; } @@ -741,6 +744,9 @@ public: if (has_flushed_end_pending) { if (scheduler.IsRenderPassActive()) { FlushEndTFB(); + } else { + has_flushed_end_pending = false; + has_started = false; } } runtime.View3DRegs([this](Maxwell3D& maxwell3d) { 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 66a7e874a0..17bd42c642 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -145,6 +145,16 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .preserveAttachmentCount = 0, .pPreserveAttachments = nullptr, }; + const VkSubpassDependency counter_resume_dependency{ + .srcSubpass = 0, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + .dstStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT, + .dependencyFlags = 0, + }; + const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported(); pair->second = device->GetLogical().CreateRenderPass({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = nullptr, @@ -153,8 +163,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .pAttachments = descriptions.empty() ? nullptr : descriptions.data(), .subpassCount = 1, .pSubpasses = &subpass, - .dependencyCount = 0, - .pDependencies = nullptr, + .dependencyCount = can_resume_transform_feedback ? 1u : 0u, + .pDependencies = can_resume_transform_feedback ? &counter_resume_dependency : nullptr, }); return *pair->second; } From 894f2544a0a5391cbd9caaccc4d2d749d78d4d80 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 11 Aug 2026 23:07:23 -0400 Subject: [PATCH 10/72] [TEST] Geometry shader changes + barrier fixes --- .../frontend/maxwell/translate_program.cpp | 61 +++++++++++------- .../frontend/maxwell/translate_program.h | 6 +- .../renderer_opengl/gl_shader_cache.cpp | 62 +++++++++---------- .../renderer_vulkan/vk_pipeline_cache.cpp | 62 +++++++++---------- .../renderer_vulkan/vk_scheduler.cpp | 28 ++++++--- src/video_core/vulkan_common/vulkan_device.h | 15 +++++ 6 files changed, 138 insertions(+), 96 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index ebc5a825dd..728d405674 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -169,11 +169,36 @@ std::map GenerateLegacyToGenericMappings( return mapping; } +struct PassthroughVertices { + u32 count; + u32 first; + u32 stride; +}; + +PassthroughVertices GetPassthroughVertices(InputTopology input_topology) { + switch (input_topology) { + case InputTopology::Points: + return {1, 0, 1}; + case InputTopology::Lines: + return {2, 0, 1}; + case InputTopology::LinesAdjacency: + return {2, 1, 1}; + case InputTopology::Triangles: + return {3, 0, 1}; + case InputTopology::TrianglesAdjacency: + return {3, 0, 2}; + } + return {3, 0, 1}; +} + void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const Shader::VaryingState& passthrough_mask, bool passthrough_position, - std::optional passthrough_layer_attr) { - for (u32 i = 0; i < program.output_vertices; i++) { + std::optional passthrough_layer_attr, + InputTopology input_topology) { + const PassthroughVertices vertices{GetPassthroughVertices(input_topology)}; + for (u32 vertex = 0; vertex < vertices.count; vertex++) { + const u32 i = vertices.first + vertex * vertices.stride; // Assign generics from input for (u32 j = 0; j < 32; j++) { if (!passthrough_mask.Generic(j)) { @@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, ir.EndPrimitive(ir.Imm32(0)); } -u32 GetOutputTopologyVertices(OutputTopology output_topology) { - switch (output_topology) { - case OutputTopology::PointList: - return 1; - case OutputTopology::LineStrip: - return 2; - default: - return 3; - } -} - -void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) { +void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info, + InputTopology input_topology) { for (IR::Block* const block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { if (inst.GetOpcode() == IR::Opcode::Epilogue) { IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; EmitGeometryPassthrough( ir, program, program.info.passthrough, - program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}); + program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}, + input_topology); } } } @@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf } // Anonymous namespace IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, - Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { + Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info, + InputTopology input_topology) { HostTranslateInfo normalized_host_info{host_info}; normalized_host_info.ApplyDescriptorLimitPolicy(); @@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& inst_pool, ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology) { + Shader::OutputTopology output_topology, + InputTopology input_topology) { IR::Program program; program.stage = Stage::Geometry; program.output_topology = output_topology; - program.output_vertices = GetOutputTopologyVertices(output_topology); + program.output_vertices = GetPassthroughVertices(input_topology).count; program.is_geometry_passthrough = false; program.info.loads.mask = source_program.info.stores.mask; @@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool& inst_pool, IR::IREmitter ir{*current_block}; EmitGeometryPassthrough(ir, program, program.info.stores, true, - source_program.info.emulated_layer); + source_program.info.emulated_layer, input_topology); IR::Block* return_block{block_pool.Create(inst_pool)}; IR::IREmitter{*return_block}.Epilogue(); diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 497afe7cb9..49d2800d08 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -18,7 +18,8 @@ namespace Shader::Maxwell { [[nodiscard]] IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, Environment& env, - Flow::CFG& cfg, const HostTranslateInfo& host_info); + Flow::CFG& cfg, const HostTranslateInfo& host_info, + InputTopology input_topology); [[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b); @@ -32,6 +33,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology); + Shader::OutputTopology output_topology, + InputTopology input_topology); } // namespace Shader::Maxwell diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index cde03d5822..1218c8580c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -72,6 +72,25 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo } } +Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::InputTopology::Points; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::InputTopology::Lines; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Shader::InputTopology::LinesAdjacency; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Shader::InputTopology::TrianglesAdjacency; + default: + return Shader::InputTopology::Triangles; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -127,33 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, default: break; } - switch (key.gs_input_topology) { - case Maxwell::PrimitiveTopology::Points: - info.input_topology = Shader::InputTopology::Points; - break; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - info.input_topology = Shader::InputTopology::Lines; - break; - case Maxwell::PrimitiveTopology::Triangles: - case Maxwell::PrimitiveTopology::TriangleStrip: - case Maxwell::PrimitiveTopology::TriangleFan: - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - case Maxwell::PrimitiveTopology::Polygon: - case Maxwell::PrimitiveTopology::Patches: - info.input_topology = Shader::InputTopology::Triangles; - break; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - info.input_topology = Shader::InputTopology::LinesAdjacency; - break; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - info.input_topology = Shader::InputTopology::TrianglesAdjacency; - break; - } + info.input_topology = MaxwellToInputTopology(key.gs_input_topology); info.glasm_use_storage_buffers = glasm_use_storage_buffers; return info; } @@ -483,8 +476,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( && index == u32(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { auto topology = MaxwellToOutputTopology(key.gs_input_topology); - programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology); + programs[index] = + GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology, + MaxwellToInputTopology(key.gs_input_topology)); continue; } if (key.unique_hashes[index] == 0) { @@ -502,13 +497,15 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.gs_input_topology)); total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.gs_input_topology))}; total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -597,7 +594,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + Shader::InputTopology::Points)}; const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; Shader::RuntimeInfo info; info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index acb4219213..697540981d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -152,6 +152,25 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde return Shader::AttributeType::Disabled; } +Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::InputTopology::Points; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::InputTopology::Lines; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Shader::InputTopology::LinesAdjacency; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Shader::InputTopology::TrianglesAdjacency; + default: + return Shader::InputTopology::Triangles; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -270,33 +289,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program default: break; } - switch (key.state.topology) { - case Maxwell::PrimitiveTopology::Points: - info.input_topology = Shader::InputTopology::Points; - break; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - info.input_topology = Shader::InputTopology::Lines; - break; - case Maxwell::PrimitiveTopology::Triangles: - case Maxwell::PrimitiveTopology::TriangleStrip: - case Maxwell::PrimitiveTopology::TriangleFan: - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - case Maxwell::PrimitiveTopology::Polygon: - case Maxwell::PrimitiveTopology::Patches: - info.input_topology = Shader::InputTopology::Triangles; - break; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - info.input_topology = Shader::InputTopology::LinesAdjacency; - break; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - info.input_topology = Shader::InputTopology::TrianglesAdjacency; - break; - } + info.input_topology = MaxwellToInputTopology(key.state.topology); info.force_early_z = key.state.early_z != 0; info.y_negate = key.state.y_negate != 0; return info; @@ -793,8 +786,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( index == static_cast(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { auto topology = MaxwellToOutputTopology(key.state.topology); - programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology); + programs[index] = + GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology, + MaxwellToInputTopology(key.state.topology)); continue; } if (key.unique_hashes[index] == 0) { @@ -807,11 +802,13 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.state.topology)); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.state.topology))}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -957,7 +954,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + Shader::InputTopology::Points)}; const VkDriverIdKHR driver_id = device.GetDriverID(); const bool needs_shared_mem_clamp = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3601a3e891..f1f04d0b8c 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -410,8 +410,17 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, + consumer_stages = device.AttachmentConsumerStages(), has_transform_feedback = device.IsExtTransformFeedbackSupported()]( vk::CommandBuffer cmdbuf) { + static constexpr VkAccessFlags SHADER_ACCESS = + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + static constexpr VkAccessFlags COLOR_ACCESS = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + std::array barriers; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; @@ -421,24 +430,25 @@ void Scheduler::EndRenderPass() | VK_IMAGE_ASPECT_STENCIL_BIT)) !=0; VkAccessFlags src_access = 0; + VkAccessFlags dst_access = SHADER_ACCESS; - if (is_color) + if (is_color) { src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - else if (is_depth_stencil) + dst_access |= COLOR_ACCESS; + } else if (is_depth_stencil) { src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - else + dst_access |= DEPTH_STENCIL_ACCESS; + } else { src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS; + } barriers[i] = VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = src_access, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT - | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT - | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -449,7 +459,7 @@ void Scheduler::EndRenderPass() } cmdbuf.EndRenderPass(); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 08ea697892..2602d97103 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -967,6 +967,21 @@ FN_MAX_LIMIT_LIST return features2.features.multiViewport; } + 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.geometryShader) { + stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + } + return stages; + } + /// Returns true if the device supports VK_KHR_maintenance1. bool IsKhrMaintenance1Supported() const { return extensions.maintenance1; From 4aa35bc835bf3d825c6bcba732fe021ea9a7459f Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 11 Aug 2026 23:25:46 -0400 Subject: [PATCH 11/72] [TEST] Fix vkFormat accessors --- .../vi/application_display_service.cpp | 17 +- .../renderer_vulkan/vk_texture_cache.cpp | 184 +++++++----------- 2 files changed, 68 insertions(+), 133 deletions(-) diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp index d842c9dd42..dfefbf5cb0 100644 --- a/src/core/hle/service/vi/application_display_service.cpp +++ b/src/core/hle/service/vi/application_display_service.cpp @@ -88,25 +88,10 @@ Result IApplicationDisplayService::GetIndirectDisplayTransactionService( } Result IApplicationDisplayService::OpenDisplay(Out out_display_id, DisplayName display_name) { - LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data()); - // Ensure the display name is null-terminated display_name[display_name.size() - 1] = '\0'; - // According to switchbrew, only "Default", "External", "Edid", "Internal" and "Null" are valid - const std::array valid_names = { - "Default", "External", "Edid", "Internal", "Null" - }; - - bool valid_name = false; - for (const auto& name : valid_names) { - if (name == display_name.data()) { - valid_name = true; - break; - } - } - - R_UNLESS(valid_name, ResultOperationFailed); + LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data()); R_RETURN(m_container->OpenDisplay(out_display_id, display_name)); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index db4e8a00a3..22990cf95d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1356,133 +1356,83 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im } switch (dst_view.format) { - case PixelFormat::D24_UNORM_S8_UINT: - if (src_view.format == PixelFormat::A8B8G8R8_UNORM - || src_view.format == PixelFormat::B8G8R8A8_UNORM - || src_view.format == PixelFormat::A8B8G8R8_SRGB - || src_view.format == PixelFormat::B8G8R8A8_SRGB) { - return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); + case PixelFormat::R16_UNORM: + if (src_view.format == PixelFormat::D16_UNORM) { + return blit_image_helper.ConvertD16ToR16(dst, src_view); + } + break; + case PixelFormat::A8B8G8R8_SRGB: + case PixelFormat::B8G8R8A8_SRGB: + case PixelFormat::B8G8R8A8_UNORM: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); } break; case PixelFormat::A8B8G8R8_UNORM: - case PixelFormat::A8B8G8R8_SNORM: - case PixelFormat::A8B8G8R8_SINT: - case PixelFormat::A8B8G8R8_UINT: - case PixelFormat::R5G6B5_UNORM: - case PixelFormat::B5G6R5_UNORM: - case PixelFormat::A1R5G5B5_UNORM: - case PixelFormat::A2B10G10R10_UNORM: - case PixelFormat::A2B10G10R10_UINT: - case PixelFormat::A2R10G10B10_UNORM: - case PixelFormat::A1B5G5R5_UNORM: - case PixelFormat::A5B5G5R1_UNORM: - case PixelFormat::R8_UNORM: - case PixelFormat::R8_SNORM: - case PixelFormat::R8_SINT: - case PixelFormat::R8_UINT: - case PixelFormat::R16G16B16A16_FLOAT: - case PixelFormat::R16G16B16A16_UNORM: - case PixelFormat::R16G16B16A16_SNORM: - case PixelFormat::R16G16B16A16_SINT: - case PixelFormat::R16G16B16A16_UINT: - case PixelFormat::B10G11R11_FLOAT: - case PixelFormat::R32G32B32A32_UINT: - case PixelFormat::BC1_RGBA_UNORM: - case PixelFormat::BC2_UNORM: - case PixelFormat::BC3_UNORM: - case PixelFormat::BC4_UNORM: - case PixelFormat::BC4_SNORM: - case PixelFormat::BC5_UNORM: - case PixelFormat::BC5_SNORM: - case PixelFormat::BC7_UNORM: - case PixelFormat::BC6H_UFLOAT: - case PixelFormat::BC6H_SFLOAT: - case PixelFormat::ASTC_2D_4X4_UNORM: - case PixelFormat::B8G8R8A8_UNORM: - case PixelFormat::R32G32B32A32_FLOAT: - case PixelFormat::R32G32B32A32_SINT: - case PixelFormat::R32G32_FLOAT: - case PixelFormat::R32G32_SINT: - case PixelFormat::R32_FLOAT: - if (src_view.format == PixelFormat::D32_FLOAT && - (dst_view.format == PixelFormat::B5G6R5_UNORM || - Settings::values.fix_bloom_effects.GetValue())) { - const Region2D region{ - .start = {0, 0}, - .end = {static_cast(dst->RenderArea().width), - static_cast(dst->RenderArea().height)}, - }; - return blit_image_helper.BlitColor(dst, src_view, region, region, - Tegra::Engines::Fermi2D::Filter::Point, - Tegra::Engines::Fermi2D::Operation::SrcCopy); + if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { + return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); + } + if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) { + return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view); + } + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); + } + break; + case PixelFormat::R32_FLOAT: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32ToR32(dst, src_view); } break; - case PixelFormat::R16_FLOAT: - case PixelFormat::R16_UNORM: - case PixelFormat::R16_SNORM: - case PixelFormat::R16_UINT: - case PixelFormat::R16_SINT: - case PixelFormat::R16G16_UNORM: - case PixelFormat::R16G16_FLOAT: - case PixelFormat::R16G16_UINT: - case PixelFormat::R16G16_SINT: - case PixelFormat::R16G16_SNORM: - case PixelFormat::R32G32B32_FLOAT: - case PixelFormat::A8B8G8R8_SRGB: - case PixelFormat::R8G8_UNORM: - case PixelFormat::R8G8_SNORM: - case PixelFormat::R8G8_SINT: - case PixelFormat::R8G8_UINT: - case PixelFormat::R32G32_UINT: - case PixelFormat::R16G16B16X16_FLOAT: - case PixelFormat::R32_UINT: - case PixelFormat::R32_SINT: - case PixelFormat::ASTC_2D_8X8_UNORM: - case PixelFormat::ASTC_2D_8X5_UNORM: - case PixelFormat::ASTC_2D_5X4_UNORM: - case PixelFormat::B8G8R8A8_SRGB: - case PixelFormat::BC1_RGBA_SRGB: - case PixelFormat::BC2_SRGB: - case PixelFormat::BC3_SRGB: - case PixelFormat::BC7_SRGB: - case PixelFormat::A4B4G4R4_UNORM: - case PixelFormat::G4R4_UNORM: - case PixelFormat::ASTC_2D_4X4_SRGB: - case PixelFormat::ASTC_2D_8X8_SRGB: - case PixelFormat::ASTC_2D_8X5_SRGB: - case PixelFormat::ASTC_2D_5X4_SRGB: - case PixelFormat::ASTC_2D_5X5_UNORM: - case PixelFormat::ASTC_2D_5X5_SRGB: - case PixelFormat::ASTC_2D_10X8_UNORM: - case PixelFormat::ASTC_2D_10X8_SRGB: - case PixelFormat::ASTC_2D_6X6_UNORM: - case PixelFormat::ASTC_2D_6X6_SRGB: - case PixelFormat::ASTC_2D_10X6_UNORM: - case PixelFormat::ASTC_2D_10X6_SRGB: - case PixelFormat::ASTC_2D_10X5_UNORM: - case PixelFormat::ASTC_2D_10X5_SRGB: - case PixelFormat::ASTC_2D_10X10_UNORM: - case PixelFormat::ASTC_2D_10X10_SRGB: - case PixelFormat::ASTC_2D_12X10_UNORM: - case PixelFormat::ASTC_2D_12X10_SRGB: - case PixelFormat::ASTC_2D_12X12_UNORM: - case PixelFormat::ASTC_2D_12X12_SRGB: - case PixelFormat::ASTC_2D_8X6_UNORM: - case PixelFormat::ASTC_2D_8X6_SRGB: - case PixelFormat::ASTC_2D_6X5_UNORM: - case PixelFormat::ASTC_2D_6X5_SRGB: - case PixelFormat::E5B9G9R9_FLOAT: - case PixelFormat::D32_FLOAT: case PixelFormat::D16_UNORM: - case PixelFormat::X8_D24_UNORM: - case PixelFormat::S8_UINT: + if (src_view.format == PixelFormat::R16_UNORM) { + return blit_image_helper.ConvertR16ToD16(dst, src_view); + } + break; case PixelFormat::S8_UINT_D24_UNORM: - case PixelFormat::D32_FLOAT_S8_UINT: - case PixelFormat::Invalid: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM) { + return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); + } + break; + case PixelFormat::D32_FLOAT: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM || + src_view.format == PixelFormat::A8B8G8R8_SRGB || + src_view.format == PixelFormat::B8G8R8A8_SRGB) { + return blit_image_helper.ConvertABGR8ToD32F(dst, src_view); + } + if (src_view.format == PixelFormat::R32_FLOAT) { + return blit_image_helper.ConvertR32ToD32(dst, src_view); + } + break; + case PixelFormat::D24_UNORM_S8_UINT: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM || + src_view.format == PixelFormat::A8B8G8R8_SRGB || + src_view.format == PixelFormat::B8G8R8A8_SRGB) { + return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); + } + break; default: - LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format); break; } + + if (src_view.format == PixelFormat::D32_FLOAT && + VideoCore::Surface::GetFormatType(dst_view.format) == SurfaceType::ColorTexture && + (dst_view.format == PixelFormat::B5G6R5_UNORM || + Settings::values.fix_bloom_effects.GetValue())) { + const Region2D region{ + .start = {0, 0}, + .end = {static_cast(dst->RenderArea().width), + static_cast(dst->RenderArea().height)}, + }; + return blit_image_helper.BlitColor(dst, src_view, region, region, + Tegra::Engines::Fermi2D::Filter::Point, + Tegra::Engines::Fermi2D::Operation::SrcCopy); + } + + LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format); } VkFormat TextureCacheRuntime::GetSupportedFormat(VkFormat requested_format, From f3a674c06415eb7f07ae932c32f465c20355011a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 00:44:14 -0400 Subject: [PATCH 12/72] [TEST] MSAA uploads --- src/video_core/host_shaders/CMakeLists.txt | 2 + .../convert_non_msaa_to_msaa_depth.frag | 19 ++ ...onvert_non_msaa_to_msaa_depth_stencil.frag | 22 ++ src/video_core/renderer_vulkan/blit_image.cpp | 288 +++++++++++++++++- src/video_core/renderer_vulkan/blit_image.h | 15 + .../renderer_vulkan/vk_texture_cache.cpp | 29 +- 6 files changed, 365 insertions(+), 10 deletions(-) create mode 100644 src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag create mode 100644 src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 6be3e3bd06..be46842cd9 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -34,6 +34,8 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag ${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert ${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag new file mode 100644 index 0000000000..f883b3196f --- /dev/null +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform sampler2D img_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; + const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); + const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; + gl_FragDepth = texelFetch(img_in, coord, 0).r; +} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag new file mode 100644 index 0000000000..d9a308f5c3 --- /dev/null +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag @@ -0,0 +1,22 @@ +// 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 sampler2D depth_tex; +layout(binding = 1) uniform usampler2D stencil_tex; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; + const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); + const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; + gl_FragDepth = texelFetch(depth_tex, coord, 0).r; + gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r); +} diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index e06226e918..c4560177b8 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_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_non_msaa_to_msaa_depth_frag_spv.h" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_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" @@ -519,7 +521,8 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) } [[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image, - VkFormat format, u32 base_level) { + VkFormat format, u32 base_level, + VkImageAspectFlags aspect_mask) { return device.CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, @@ -534,7 +537,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) .a = VK_COMPONENT_SWIZZLE_IDENTITY, }, .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .baseMipLevel = base_level, .levelCount = 1, .baseArrayLayer = 0, @@ -586,6 +589,10 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( one_texture_set_layout.address(), PUSH_CONSTANT_RANGE))), + msaa_copy_depth_stencil_pipeline_layout( + device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( + two_textures_set_layout.address(), + PUSH_CONSTANT_RANGE))), 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)), @@ -610,6 +617,12 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, 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)), + convert_non_msaa_to_msaa_depth_frag( + BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)), + convert_non_msaa_to_msaa_depth_stencil_frag( + device.IsExtShaderStencilExportSupported() + ? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV) + : vk::ShaderModule{}), linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO)), nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO)) {} @@ -920,10 +933,12 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i ASSERT(copy.dst_subresource.num_layers == 1); vk::ImageView src_view = MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, - static_cast(copy.src_subresource.base_level)); + static_cast(copy.src_subresource.base_level), + VK_IMAGE_ASPECT_COLOR_BIT); vk::ImageView dst_view = MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, - static_cast(copy.dst_subresource.base_level)); + static_cast(copy.dst_subresource.base_level), + VK_IMAGE_ASPECT_COLOR_BIT); 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{ @@ -1417,6 +1432,198 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas return *pipelines.back(); } +void BlitImageHelper::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) { + 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); + RenderPassKey renderpass_key{}; + renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid); + renderpass_key.depth_format = dst_format; + renderpass_key.samples = samples; + const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key); + const MSAACopyPipelineKey key{ + .renderpass = renderpass, + .samples = samples, + .msaa_to_non_msaa = false, + }; + const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil); + const VkPipelineLayout layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_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; + const VkImageAspectFlags attachment_aspect = + VideoCore::Surface::GetFormatType(dst_format) == + VideoCore::Surface::SurfaceType::DepthStencil + ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT + : VK_IMAGE_ASPECT_DEPTH_BIT; + 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(copy.src_subresource.base_level), + VK_IMAGE_ASPECT_DEPTH_BIT); + vk::ImageView src_stencil_view = + copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, + static_cast(copy.src_subresource.base_level), + VK_IMAGE_ASPECT_STENCIL_BIT) + : vk::ImageView{}; + vk::ImageView dst_view = + MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, + static_cast(copy.dst_subresource.base_level), + attachment_aspect); + 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(dst_offset.x) + dst_extent.width, + .height = static_cast(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(); + const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE; + scheduler.Record([this, pipeline, layout, sampler, renderpass, + framebuffer_handle = *framebuffer, src_view_handle = *src_view, + src_stencil_handle, src = src_image, dst = dst_image, render_area, + attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) { + const VkImageSubresourceRange src_range{ + .aspectMask = attachment_aspect, + .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_DEPTH_STENCIL_ATTACHMENT_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 = src_range, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_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 = src_range, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_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 = + src_stencil_handle != VK_NULL_HANDLE + ? two_textures_descriptor_allocator.Commit() + : one_texture_descriptor_allocator.Commit(); + if (src_stencil_handle != VK_NULL_HANDLE) { + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle, + src_stencil_handle); + } else { + 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(render_area.offset.x), + .y = static_cast(render_area.offset.y), + .width = static_cast(render_area.extent.width), + .height = static_cast(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_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_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 = src_range, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 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), + }); + if (copy_stencil) { + msaa_copy_resources.push_back(MSAACopyResources{ + .tick = scheduler.CurrentTick(), + .src_view = std::move(src_stencil_view), + .dst_view = vk::ImageView{}, + .framebuffer = vk::Framebuffer{}, + }); + } + } +} + VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) { const auto it = std::ranges::find(msaa_copy_keys, key); if (it != msaa_copy_keys.end()) { @@ -1462,6 +1669,79 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline return *msaa_copy_pipelines.back(); } +VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key, + bool copy_stencil) { + auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys; + auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines; + const auto it = std::ranges::find(keys, key); + if (it != keys.end()) { + 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); + 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, + }; + static constexpr VkStencilOpState REPLACE_STENCIL_OP{ + .failOp = VK_STENCIL_OP_REPLACE, + .passOp = VK_STENCIL_OP_REPLACE, + .depthFailOp = VK_STENCIL_OP_REPLACE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .compareMask = 0xFF, + .writeMask = 0xFF, + .reference = 0, + }; + const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ + .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 = copy_stencil ? VK_TRUE : VK_FALSE, + .front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{}, + .back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{}, + .minDepthBounds = 0.0f, + .maxDepthBounds = 0.0f, + }; + 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, + .stageCount = static_cast(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 = &depth_stencil_ci, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, + .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout + : *msaa_copy_pipeline_layout, + .renderPass = key.renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + }, device.StaticPipelineCache())); + return *pipelines.back(); +} + void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { ConvertPipeline(pipeline, renderpass, false); } diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 67294d594b..3e6c2b09dd 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -116,6 +116,11 @@ public: VideoCore::Surface::PixelFormat src_format, u32 num_samples, std::span copies, bool msaa_to_non_msaa); + 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); + private: void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view); @@ -131,6 +136,9 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline( const BlitDepthStencilPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key); + + [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key, + bool copy_stencil); [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil); @@ -162,6 +170,7 @@ private: vk::PipelineLayout two_textures_pipeline_layout; vk::PipelineLayout clear_color_pipeline_layout; vk::PipelineLayout msaa_copy_pipeline_layout; + vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout; vk::ShaderModule full_screen_vert; vk::ShaderModule blit_color_to_color_frag; vk::ShaderModule blit_color_msaa_frag; @@ -180,6 +189,8 @@ private: vk::ShaderModule convert_s8d24_to_abgr8_frag; vk::ShaderModule convert_msaa_to_non_msaa_frag; vk::ShaderModule convert_non_msaa_to_msaa_frag; + vk::ShaderModule convert_non_msaa_to_msaa_depth_frag; + vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag; vk::Sampler linear_sampler; vk::Sampler nearest_sampler; @@ -193,6 +204,10 @@ private: std::vector clear_stencil_pipelines; std::vector msaa_copy_keys; std::vector msaa_copy_pipelines; + std::vector msaa_copy_depth_keys; + std::vector msaa_copy_depth_pipelines; + std::vector msaa_copy_depth_stencil_keys; + std::vector msaa_copy_depth_stencil_pipelines; std::vector blit_msaa_color_keys; std::vector blit_msaa_color_pipelines; std::vector resolve_depth_keys; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 22990cf95d..bcf4fa1021 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1838,11 +1838,21 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, ScaleDown(true); } + const bool msaa_upload_is_depth = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; const bool wants_msaa_upload = info.num_samples > 1 - && (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 + && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth) && !VideoCore::Surface::IsPixelFormatInteger(info.format); if (wants_msaa_upload) { + const bool msaa_upload_copies_stencil = + msaa_upload_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + runtime->device.IsExtShaderStencilExportSupported(); + const VkImageAspectFlags upload_aspect_mask = + msaa_upload_is_depth + ? (msaa_upload_copies_stencil + ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT + : VK_IMAGE_ASPECT_DEPTH_BIT) + : aspect_mask; ImageInfo temp_info = info; temp_info.num_samples = 1; @@ -1854,10 +1864,10 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); scheduler->RequestOutsideRenderPassOperationContext(); - auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); + auto vk_copies = TransformBufferImageCopies(copies, offset, upload_aspect_mask); const VkBuffer src_buffer = buffer; const VkImage temp_vk_image = *temp_image; - const VkImageAspectFlags vk_aspect_mask = aspect_mask; + const VkImageAspectFlags vk_aspect_mask = upload_aspect_mask; scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { @@ -1879,9 +1889,16 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, image_copies.push_back(image_copy); } - runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format, - temp_vk_image, info.format, info.num_samples, - image_copies, false); + if (msaa_upload_is_depth) { + 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); + } else { + 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)); From f9e5ac191e16f0fc65462be61cf07c0fb0834365 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 01:19:52 -0400 Subject: [PATCH 13/72] [TEST] Depth stencil resolve + MSAA depth stencil --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 2 + .../renderer_vulkan/vk_render_pass_cache.cpp | 173 +++++++++++++++++- .../renderer_vulkan/vk_render_pass_cache.h | 7 +- .../renderer_vulkan/vk_texture_cache.cpp | 117 ++++++++---- .../renderer_vulkan/vk_texture_cache.h | 5 +- .../vulkan_common/vulkan_device.cpp | 11 ++ src/video_core/vulkan_common/vulkan_device.h | 34 ++++ .../vulkan_common/vulkan_wrapper.cpp | 12 ++ src/video_core/vulkan_common/vulkan_wrapper.h | 3 + 9 files changed, 330 insertions(+), 34 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8b5b0bc9c5..345278ab41 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -140,6 +140,8 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& d }); key.resolve_color = key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler(); + key.resolve_depth_stencil = key.samples != VK_SAMPLE_COUNT_1_BIT && device.IsTiler() && + SupportsDepthStencilResolve(device, key.depth_format); return key; } 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 17bd42c642..7b67665829 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -65,8 +65,61 @@ using VideoCore::Surface::SurfaceType; .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; } + + struct ResolveAspects { + bool depth; + bool stencil; + }; + + struct ResolveModes { + VkResolveModeFlagBits depth; + VkResolveModeFlagBits stencil; + }; + + constexpr ResolveAspects GetResolveAspects(PixelFormat format) { + const SurfaceType surface_type = GetSurfaceType(format); + return ResolveAspects{ + .depth = surface_type == SurfaceType::Depth || + surface_type == SurfaceType::DepthStencil, + .stencil = surface_type == SurfaceType::Stencil || + surface_type == SurfaceType::DepthStencil, + }; + } + + ResolveModes PickResolveModes(const Device& device, PixelFormat format) { + constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + + const ResolveAspects aspects = GetResolveAspects(format); + ResolveModes modes{ + .depth = VK_RESOLVE_MODE_NONE, + .stencil = VK_RESOLVE_MODE_NONE, + }; + if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) { + modes.depth = mode; + } + if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) { + modes.stencil = mode; + } + return modes; + } } // Anonymous namespace +bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) { + if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) { + return false; + } + const ResolveAspects aspects = GetResolveAspects(depth_format); + if (!aspects.depth && !aspects.stencil) { + return false; + } + const ResolveModes modes = PickResolveModes(device, depth_format); + if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) || + (aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) { + return false; + } + return modes.depth == modes.stencil || device.SupportsIndependentResolveNone(); +} + RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { @@ -75,7 +128,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { if (!is_new) { return *pair->second; } - boost::container::static_vector descriptions; + static constexpr size_t MAX_ATTACHMENTS = + 2 * std::tuple_size_v + 2; + boost::container::static_vector descriptions; std::array references{}; u32 num_attachments{}; u32 num_colors{}; @@ -133,6 +188,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { } } } + const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth && + key.samples != VK_SAMPLE_COUNT_1_BIT && + SupportsDepthStencilResolve(*device, key.depth_format); + VkAttachmentReference depth_resolve_reference{}; + if (do_resolve_depth_stencil) { + depth_resolve_reference = VkAttachmentReference{ + .attachment = static_cast(descriptions.size()), + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + VkAttachmentDescription resolve_desc = + AttachmentDescription(*device, key.depth_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, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -155,6 +225,107 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .dependencyFlags = 0, }; const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported(); + + if (device->IsKhrCreateRenderPass2Supported()) { + boost::container::static_vector descriptions2; + for (const VkAttachmentDescription& description : descriptions) { + descriptions2.push_back(VkAttachmentDescription2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + .pNext = nullptr, + .flags = description.flags, + .format = description.format, + .samples = description.samples, + .loadOp = description.loadOp, + .storeOp = description.storeOp, + .stencilLoadOp = description.stencilLoadOp, + .stencilStoreOp = description.stencilStoreOp, + .initialLayout = description.initialLayout, + .finalLayout = description.finalLayout, + }); + } + const auto promote = [](const VkAttachmentReference& reference) { + return VkAttachmentReference2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + .pNext = nullptr, + .attachment = reference.attachment, + .layout = reference.layout, + .aspectMask = 0, + }; + }; + std::array references2{}; + std::array resolve_references2{}; + for (size_t index = 0; index < references.size(); ++index) { + references2[index] = promote(references[index]); + resolve_references2[index] = promote(resolve_references[index]); + } + const VkAttachmentReference2 depth_reference2 = promote(depth_reference); + const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference); + const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format); + const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, + .pNext = nullptr, + .depthResolveMode = resolve_modes.depth, + .stencilResolveMode = resolve_modes.stencil, + .pDepthStencilResolveAttachment = &depth_resolve_reference2, + }; + const VkSubpassDescription2 subpass2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + .pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr, + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .viewMask = 0, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = num_attachments, + .pColorAttachments = references2.data(), + .pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr, + .pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + const VkMemoryBarrier2 counter_resume_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, + .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, + .dstAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT, + }; + VkSubpassDependency2 counter_resume_dependency2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + .pNext = nullptr, + .srcSubpass = counter_resume_dependency.srcSubpass, + .dstSubpass = counter_resume_dependency.dstSubpass, + .srcStageMask = counter_resume_dependency.srcStageMask, + .dstStageMask = counter_resume_dependency.dstStageMask, + .srcAccessMask = counter_resume_dependency.srcAccessMask, + .dstAccessMask = counter_resume_dependency.dstAccessMask, + .dependencyFlags = counter_resume_dependency.dependencyFlags, + .viewOffset = 0, + }; + if (device->HasSynchronization2()) { + counter_resume_dependency2.pNext = &counter_resume_barrier; + counter_resume_dependency2.srcStageMask = 0; + counter_resume_dependency2.dstStageMask = 0; + counter_resume_dependency2.srcAccessMask = 0; + counter_resume_dependency2.dstAccessMask = 0; + } + pair->second = device->GetLogical().CreateRenderPass2({ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + .pNext = nullptr, + .flags = 0, + .attachmentCount = static_cast(descriptions2.size()), + .pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(), + .subpassCount = 1, + .pSubpasses = &subpass2, + .dependencyCount = can_resume_transform_feedback ? 1u : 0u, + .pDependencies = can_resume_transform_feedback ? &counter_resume_dependency2 : nullptr, + .correlatedViewMaskCount = 0, + .pCorrelatedViewMasks = nullptr, + }); + return *pair->second; + } + pair->second = device->GetLogical().CreateRenderPass({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = 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 4769d65f39..0b01fc0194 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -22,6 +22,7 @@ struct RenderPassKey { VideoCore::Surface::PixelFormat depth_format; VkSampleCountFlagBits samples; bool resolve_color; + bool resolve_depth_stencil; u32 color_clear_mask; bool depth_stencil_clear; u32 color_discard_mask; @@ -47,7 +48,8 @@ struct hash { (static_cast(key.color_clear_mask) << 16) | (static_cast(key.color_discard_mask) << 24) | (static_cast(key.resolve_color) << 32) | - (static_cast(key.depth_stencil_clear) << 33); + (static_cast(key.depth_stencil_clear) << 33) | + (static_cast(key.resolve_depth_stencil) << 34); size_t seed = 0; Common::HashCombine(seed, formats); Common::HashCombine(seed, state); @@ -60,6 +62,9 @@ namespace Vulkan { class Device; +[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device, + VideoCore::Surface::PixelFormat depth_format); + class RenderPassCache { public: explicit RenderPassCache(const Device& device_); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index bcf4fa1021..2f15a90977 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -993,13 +993,25 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { } VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, - VkExtent2D extent, u32 layers) { + VkExtent2D extent, u32 layers, + VkImageAspectFlags aspect_mask) { 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.extent.height == extent.height && shadow.layers == layers && + shadow.aspect_mask == aspect_mask) { shadow.up_to_date = true; return *shadow.view; } + VkImageUsageFlags shadow_usage = + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + shadow_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + shadow_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (shadow.image) { + pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(shadow)); + } shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, @@ -1011,8 +1023,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .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, + .usage = shadow_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1027,7 +1038,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .format = format, .components{}, .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, @@ -1037,6 +1048,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk shadow.format = format; shadow.extent = extent; shadow.layers = layers; + shadow.aspect_mask = aspect_mask; shadow.up_to_date = true; return *shadow.view; } @@ -1058,7 +1070,14 @@ void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) { } void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) { - resolve_shadows.erase(msaa_image); + const auto it = resolve_shadows.find(msaa_image); + if (it == resolve_shadows.end()) { + return; + } + if (it->second.image) { + pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(it->second)); + } + resolve_shadows.erase(it); } void TextureCacheRuntime::BarrierFeedbackLoop() { @@ -1589,31 +1608,41 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, std::span copies) { const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1; 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 && + if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() && + copy.src_offset.x == 0 && copy.src_offset.y == 0 && copy.src_subresource.base_level == 0 && static_cast(copy.extent.width) <= shadow->extent.width && - static_cast(copy.extent.height) <= shadow->extent.height) { + static_cast(copy.extent.height) <= shadow->extent.height && + static_cast(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <= + shadow->layers) { + const VkImageAspectFlags aspect_mask = shadow->aspect_mask; + VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkAccessFlags attachment_read_write = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } const VkImage shadow_image = *shadow->image; const VkImage dst_image = dst.Handle(); const VkImageCopy region{ .srcSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .mipLevel = 0, .baseArrayLayer = static_cast(copy.src_subresource.base_layer), .layerCount = static_cast(copy.src_subresource.num_layers), }, .srcOffset = {0, 0, 0}, .dstSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .mipLevel = static_cast(copy.dst_subresource.base_level), .baseArrayLayer = static_cast(copy.dst_subresource.base_layer), .layerCount = static_cast(copy.dst_subresource.num_layers), @@ -1622,26 +1651,27 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .extent = {copy.extent.width, copy.extent.height, 1}, }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) { + scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage, + attachment_write, + attachment_read_write](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, + .srcAccessMask = attachment_write, .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, + .subresourceRange{aspect_mask, 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 | + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write | VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1649,7 +1679,7 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .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, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, }; @@ -1664,28 +1694,25 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .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, + .subresourceRange{aspect_mask, 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 | + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write | 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, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(attachment_stage | 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, @@ -1697,6 +1724,11 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, return; } } + 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; + } blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(), src.info.format, num_samples, copies, msaa_to_non_msaa); } @@ -1721,6 +1753,9 @@ void TextureCacheRuntime::TickFrame() { std::erase_if(pending_msaa_images, [this](const auto& pending) { return scheduler.IsFree(pending.first); }); + std::erase_if(pending_resolve_shadows, [this](const auto& pending) { + return scheduler.IsFree(pending.first); + }); } Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, @@ -2663,7 +2698,7 @@ Framebuffer::~Framebuffer() = default; void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, std::span color_buffers, ImageView* depth_buffer, bool is_rescaled_) { - boost::container::small_vector attachments; + boost::container::small_vector attachments; RenderPassKey renderpass_key{}; s32 num_layers = 1; @@ -2692,6 +2727,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ++num_images; } const size_t num_colors = attachments.size(); + VkImage depth_image = VK_NULL_HANDLE; + VkImageAspectFlags depth_aspect_mask = 0; if (depth_buffer) { width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) : depth_buffer->size.width); @@ -2707,6 +2744,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ++num_images; has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; + depth_image = depth_buffer->ImageHandle(); + depth_aspect_mask = subresource_range.aspectMask; } else { renderpass_key.depth_format = PixelFormat::Invalid; } @@ -2715,6 +2754,12 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); renderpass_key.resolve_color = do_resolve_color; + const bool do_resolve_depth_stencil = + samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE && + runtime.device.IsTiler() && + SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format); + renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil; + discard_msaa_color = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; @@ -2735,8 +2780,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, 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)); + attachments.push_back(runtime.GetOrCreateResolveShadow( + msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT)); continue; } VkImageCreateInfo resolve_ci{ @@ -2781,6 +2826,16 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } } + if (do_resolve_depth_stencil) { + const u32 layers = static_cast((std::max)(num_layers, 1)); + const VkFormat vk_format = + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, + renderpass_key.depth_format) + .format; + attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area, + layers, depth_aspect_mask)); + } + num_color_buffers = static_cast(num_colors); framebuffer = runtime.device.GetLogical().CreateFramebuffer({ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 881fcc4de0..5dc8f81e4b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -117,11 +117,13 @@ public: VkFormat format = VK_FORMAT_UNDEFINED; VkExtent2D extent{}; u32 layers = 0; + VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; bool up_to_date = false; }; [[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, - VkExtent2D extent, u32 layers); + VkExtent2D extent, u32 layers, + VkImageAspectFlags aspect_mask); [[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const; @@ -158,6 +160,7 @@ public: std::array buffers{}; std::vector> pending_msaa_images; ankerl::unordered_dense::map resolve_shadows; + std::vector> pending_resolve_shadows; }; class Framebuffer { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 6506bdb596..ff8f834785 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1075,6 +1075,12 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + extensions.depth_stencil_resolve = + extensions.depth_stencil_resolve && + (instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2); + RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve, + VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME); @@ -1222,6 +1228,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; SetNext(next, properties.push_descriptor); } + if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) { + properties.depth_stencil_resolve.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES; + SetNext(next, properties.depth_stencil_resolve); + } if (extensions.descriptor_buffer) { properties.descriptor_buffer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 2602d97103..2a51024645 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -91,6 +91,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ + EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \ + EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ @@ -617,6 +619,37 @@ FN_MAX_LIMIT_LIST return extensions.shader_stencil_export; } + /// Returns true if the device supports VK_KHR_create_renderpass2. + bool IsKhrCreateRenderPass2Supported() const { + return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2; + } + + /// Returns true if the device supports VK_KHR_depth_stencil_resolve. + bool IsKhrDepthStencilResolveSupported() const { + return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) && + IsKhrCreateRenderPass2Supported(); + } + + /// Returns the supported resolve modes for the depth aspect. + VkResolveModeFlags GetDepthResolveModes() const { + return properties.depth_stencil_resolve.supportedDepthResolveModes; + } + + /// Returns the supported resolve modes for the stencil aspect. + VkResolveModeFlags GetStencilResolveModes() const { + return properties.depth_stencil_resolve.supportedStencilResolveModes; + } + + /// Returns true if the depth and stencil aspects may resolve with different modes. + bool SupportsIndependentResolve() const { + return properties.depth_stencil_resolve.independentResolve == VK_TRUE; + } + + /// Returns true if only one of the depth and stencil aspects may be resolved. + bool SupportsIndependentResolveNone() const { + return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE; + } + /// Returns true if depth/stencil operations can be performed efficiently. /// Either through shader export or hardware blits. bool CanPerformDepthStencilOperations() const { @@ -1172,6 +1205,7 @@ private: VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; + VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{}; VkPhysicalDeviceProperties properties{}; }; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 24f9da0a9e..33173e956d 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -184,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreatePipelineLayout); X(vkCreateQueryPool); X(vkCreateRenderPass); + X(vkCreateRenderPass2); X(vkCreateSampler); X(vkCreateSemaphore); X(vkCreateShaderModule); @@ -270,6 +271,11 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { if (!dld.vkQueueSubmit2) { Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device); } + + // Render pass creation v2 is core in Vulkan 1.2, otherwise requires VK_KHR_create_renderpass2 + if (!dld.vkCreateRenderPass2) { + Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device); + } #undef X } @@ -725,6 +731,12 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const { return RenderPass(object, handle, *dld); } +RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const { + VkRenderPass object; + Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object)); + return RenderPass(object, handle, *dld); +} + DescriptorSetLayout Device::CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const { VkDescriptorSetLayout object; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index c2d867838d..dd9a3a21d5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -300,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreateQueryPool vkCreateQueryPool{}; PFN_vkCreateRenderPass vkCreateRenderPass{}; + PFN_vkCreateRenderPass2 vkCreateRenderPass2{}; PFN_vkCreateSampler vkCreateSampler{}; PFN_vkCreateSemaphore vkCreateSemaphore{}; PFN_vkCreateShaderModule vkCreateShaderModule{}; @@ -1045,6 +1046,8 @@ public: [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; + [[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const; + [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const; From bdd1785b040257a6a0beaa0b1dcdf2c67acf14a4 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 01:30:23 -0400 Subject: [PATCH 14/72] [TEST] Quick change on depth's handling --- .../renderer_vulkan/vk_rasterizer.cpp | 47 ++++++++++++------- src/video_core/vulkan_common/vulkan_wrapper.h | 2 +- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a10d2e01e6..4b064c84e4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1286,33 +1286,42 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM; + const bool forces_unorm_representation = device.IsExtDepthBiasControlSupported(); if (is_d24 && !device.SupportsD24DepthBuffer()) { - static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]); + static constexpr double GUEST_TO_HOST_UNORM_BITS = + static_cast(1ULL << (32 - 24)); - static constexpr const u64* start = NEEDS_D24; - static constexpr const u64* end = NEEDS_D24 + length; + if (forces_unorm_representation) { + units = static_cast(static_cast(units) * GUEST_TO_HOST_UNORM_BITS); + } else { + static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]); - const u64* it = std::find(start, end, program_id); + static constexpr const u64* start = NEEDS_D24; + static constexpr const u64* end = NEEDS_D24 + length; - if (it != end) { - // the base formulas can be obtained from here: - // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias - const double rescale_factor = - static_cast(1ULL << (32 - 24)) / (static_cast(0x1.ep+127)); - units = static_cast(static_cast(units) * rescale_factor); + const u64* it = std::find(start, end, program_id); + + if (it != end) { + // the base formulas can be obtained from here: + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias + const double rescale_factor = + GUEST_TO_HOST_UNORM_BITS / (static_cast(0x1.ep+127)); + units = static_cast(static_cast(units) * rescale_factor); + } } } scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, - factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) { - if (device.IsExtDepthBiasControlSupported()) { - static VkDepthBiasRepresentationInfoEXT bias_info{ + factor = regs.slope_scale_depth_bias, + forces_unorm_representation, this](vk::CommandBuffer cmdbuf) { + if (forces_unorm_representation) { + const VkDepthBiasRepresentationInfoEXT bias_info{ .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT, .pNext = nullptr, .depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT, - .depthBiasExact = VK_FALSE, + .depthBiasExact = static_cast(device.HasExactDepthBiasControl()), }; cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info); @@ -1336,8 +1345,14 @@ void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs) if (!state_tracker.TouchDepthBounds()) { return; } - scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]]( - vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); }); + float min = regs.depth_bounds[0]; + float max = regs.depth_bounds[1]; + if (!device.IsExtDepthRangeUnrestrictedSupported()) { + min = std::clamp(min, 0.0f, 1.0f); + max = std::clamp(max, 0.0f, 1.0f); + } + scheduler.Record( + [min, max](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); }); } void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) { diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index dd9a3a21d5..d4e8e2aebc 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -1529,7 +1529,7 @@ public: } void SetDepthBias(float constant_factor, float clamp, float slope_factor, - VkDepthBiasRepresentationInfoEXT* extra) const noexcept { + const VkDepthBiasRepresentationInfoEXT* extra) const noexcept { VkDepthBiasInfoEXT info{ .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT, .pNext = extra, From 95e67e653307626a6bf6edceb5bb30dca86aa614 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 15:11:41 -0400 Subject: [PATCH 15/72] [TEST] Aniso fixes --- .../app/src/main/res/values-ar/strings.xml | 1 - .../app/src/main/res/values-de/strings.xml | 1 - .../app/src/main/res/values-es/strings.xml | 1 - .../app/src/main/res/values-fa/strings.xml | 3 --- .../app/src/main/res/values-fr/strings.xml | 1 - .../app/src/main/res/values-pl/strings.xml | 1 - .../app/src/main/res/values-pt-rBR/strings.xml | 1 - .../app/src/main/res/values-ru/strings.xml | 1 - .../app/src/main/res/values-uk/strings.xml | 1 - .../app/src/main/res/values-zh-rCN/strings.xml | 1 - .../app/src/main/res/values-zh-rTW/strings.xml | 1 - src/android/app/src/main/res/values/arrays.xml | 6 ------ src/android/app/src/main/res/values/strings.xml | 3 --- src/common/settings_enums.h | 2 +- src/qt_common/config/shared_translation.cpp | 3 --- src/video_core/textures/texture.cpp | 16 ++++++++-------- 16 files changed, 9 insertions(+), 34 deletions(-) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index dd03ec453f..bad0e25dac 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -1088,7 +1088,6 @@ فاتح داكن - لا شيء خلفيات سوداء diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 1c3b6c148f..b76e15d1b7 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -1004,7 +1004,6 @@ Wirklich fortfahren? Hell Dunkel - Keine Schwarze Hintergründe diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index a5092c4b2f..c07bbb6fe9 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -1080,7 +1080,6 @@ Claro Oscuro - Nada Fondos oscuros diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index 39f0432d8e..98b94d166d 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -808,9 +808,6 @@ x4 x8 x16 - x32 - x64 - None پس‌زمینه مشکی diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index 602a542c5f..8e2b3796a9 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -1016,7 +1016,6 @@ Lumineux Sombre - Aucun Arrière-plan noir diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 4bcebd5a8c..5921f6e588 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -948,7 +948,6 @@ Jasny Ciemny - Brak Czarne tła diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 8581415757..ae1467c492 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -897,7 +897,6 @@ Claro Escuro - Nenhum Planos de fundo pretos diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 3dbeb6ffdb..370c6d206e 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -1084,7 +1084,6 @@ Светлая Темная - Отключено Чёрный фон diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 83893210bd..0109943222 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -1066,7 +1066,6 @@ Світла Темна - Жодного Чорний фон diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 1e6ba26041..7af46a99dd 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -1078,7 +1078,6 @@ 浅色 深色 - 无 使用黑色背景 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 1612494b73..8f24400511 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -926,7 +926,6 @@ 淺色 深色 - 無 黑色背景 diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 53dd0436dc..daf74bd767 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -513,9 +513,6 @@ @string/multiplier_x4 @string/multiplier_x8 @string/multiplier_x16 - @string/multiplier_x32 - @string/multiplier_x64 - @string/multiplier_none 0 @@ -524,9 +521,6 @@ 3 4 5 - 6 - 7 - 8 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 213bd1bee6..052a96eaf6 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1246,9 +1246,6 @@ x4 x8 x16 - x32 - x64 - None Black backgrounds diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 3d0131de70..a43df6924c 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -128,7 +128,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica, Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt, Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); -ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None); +ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16); ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous); ENUM(AstcRecompression, Uncompressed, Bc1, Bc3); ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120); diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 26597d412c..7fd55ce038 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -522,9 +522,6 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { PAIR(AnisotropyMode, X4, tr("4x")), PAIR(AnisotropyMode, X8, tr("8x")), PAIR(AnisotropyMode, X16, tr("16x")), - PAIR(AnisotropyMode, X32, tr("32x")), - PAIR(AnisotropyMode, X64, tr("64x")), - PAIR(AnisotropyMode, None, tr("None")), }}); translations->insert( {Settings::EnumMetadata::Index(), diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index d32872e35d..78eb577e10 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -80,16 +80,16 @@ float TSCEntry::MaxAnisotropy() const noexcept { case Settings::AnisotropyMode::X4: case Settings::AnisotropyMode::X8: case Settings::AnisotropyMode::X16: - case Settings::AnisotropyMode::X32: - case Settings::AnisotropyMode::X64: - added_anisotropic = u32(anisotropic_settings) - 1U; + added_anisotropic = s32(anisotropic_settings) - 1; break; - case Settings::AnisotropyMode::Automatic: - added_anisotropic = Settings::values.resolution_info.up_scale >> Settings::values.resolution_info.down_shift; - added_anisotropic = (std::max)(added_anisotropic - 1U, 0U); + case Settings::AnisotropyMode::Automatic: { + const u32 resolution_scale = Settings::values.resolution_info.up_scale >> + Settings::values.resolution_info.down_shift; + if (resolution_scale > 1U) { + added_anisotropic = static_cast(resolution_scale - 1U); + } break; - case Settings::AnisotropyMode::None: - return 1.0f; //No use of anisotropy + } } return float(1U << (max_anisotropy + added_anisotropic)); } From d3e8c5e9777f79b53efddd6d6b76515502efbcd1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 16:08:50 -0400 Subject: [PATCH 16/72] [TEST] CustomBoderColor changes --- .../renderer_vulkan/pipeline_helper.h | 6 ++ .../renderer_vulkan/vk_render_pass_cache.cpp | 15 ++++- .../renderer_vulkan/vk_texture_cache.cpp | 59 ++++++++++++++++--- .../renderer_vulkan/vk_texture_cache.h | 28 +++++++++ src/video_core/textures/texture.cpp | 56 +++++------------- .../vulkan_common/vulkan_device.cpp | 6 -- src/video_core/vulkan_common/vulkan_device.h | 20 ++----- 7 files changed, 117 insertions(+), 73 deletions(-) diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 051305ec42..8a4cb6273f 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -383,6 +383,12 @@ inline void PushImageDescriptors(TextureCache& texture_cache, !image_view.SupportsDepthComparison()) { vk_sampler = sampler.HandleWithoutDepthComparison(); } + if (sampler.HasMinmaxReduction() && !image_view.SupportsMinmaxFilter()) { + vk_sampler = sampler.HandleWithDefaultReduction(); + } + if (sampler.HasCustomBorderColor() && image_view.RequiresBorderColorFormat()) { + vk_sampler = sampler.HandleWithDefaultBorderColor(); + } guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); const bool element_rescaled{texture_cache.IsRescaling(image_view)}; is_rescaled |= element_rescaled; 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 7b67665829..efb37bc787 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -90,16 +90,27 @@ using VideoCore::Surface::SurfaceType; constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; const ResolveAspects aspects = GetResolveAspects(format); + const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0; + const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0; + ResolveModes modes{ .depth = VK_RESOLVE_MODE_NONE, .stencil = VK_RESOLVE_MODE_NONE, }; - if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) { + if (aspects.depth && depth_mode_supported) { modes.depth = mode; } - if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) { + if (aspects.stencil && stencil_mode_supported) { modes.stencil = mode; } + if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) { + return modes; + } + if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) { + modes.stencil = mode; + } else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) { + modes.depth = mode; + } return modes; } } // Anonymous namespace diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2f15a90977..7aa9f19935 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -55,6 +55,17 @@ namespace { constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; +[[nodiscard]] constexpr bool RequiresBorderColorFormat(VkFormat format) { + switch (format) { + case VK_FORMAT_B4G4R4A4_UNORM_PACK16: + case VK_FORMAT_B5G6R5_UNORM_PACK16: + case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + return true; + default: + return false; + } +} + constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (color == std::array{0, 0, 0, 0}) { return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; @@ -2385,9 +2396,15 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI supports_depth_comparison = (properties3.optimalTilingFeatures & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0; + supports_minmax_filter = (properties3.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } else { supports_depth_comparison = true; + supports_minmax_filter = + (device->GetPhysical().GetFormatProperties(format_info.format).optimalTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } + requires_border_color_format = RequiresBorderColorFormat(format_info.format); const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); const VkImageUsageFlags image_usage = image.UsageFlags(); const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; @@ -2588,11 +2605,7 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { const auto& device = runtime.device; - const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported(); - const bool has_format_undefined = - has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported(); - const bool has_custom_border_colors = - has_format_undefined && runtime.device.IsCustomBorderColorsSupported(); + const bool has_custom_border_colors = runtime.device.IsCustomBorderColorUsable(); const auto color = tsc.BorderColor(); const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ @@ -2618,8 +2631,18 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .pNext = pnext, .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), }; + const VkSamplerReductionModeCreateInfoEXT reduction_ci_without_border{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, + .pNext = nullptr, + .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), + }; + const void* pnext_without_border = nullptr; + const bool has_minmax_reduction = + runtime.device.IsExtSamplerFilterMinmaxSupported() && + reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; if (runtime.device.IsExtSamplerFilterMinmaxSupported()) { pnext = &reduction_ci; + pnext_without_border = &reduction_ci_without_border; } else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) { LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required"); } @@ -2634,10 +2657,22 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR}; const auto create_sampler = [&](const f32 anisotropy, bool force_nearest, - bool disable_compare = false) { + bool disable_compare = false, + bool disable_custom_border = false, + bool disable_minmax = false) { + const bool custom_border = has_custom_border_colors && !disable_custom_border; + const bool minmax = has_minmax_reduction && !disable_minmax; + const void* chain = nullptr; + if (custom_border && minmax) { + chain = pnext; + } else if (minmax) { + chain = pnext_without_border; + } else if (custom_border) { + chain = &border_ci; + } return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = pnext, + .pNext = chain, .flags = 0, .magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter, .minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter, @@ -2654,8 +2689,8 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), - .borderColor = has_custom_border_colors ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT - : ConvertBorderColor(color), + .borderColor = custom_border ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT + : ConvertBorderColor(color), .unnormalizedCoordinates = VK_FALSE, }); }; @@ -2672,6 +2707,12 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t if (tsc.depth_compare_enabled) { sampler_noncompare = create_sampler(max_anisotropy, false, true); } + if (has_custom_border_colors) { + sampler_default_border = create_sampler(max_anisotropy, false, false, true, true); + } + if (has_minmax_reduction) { + sampler_default_reduction = create_sampler(max_anisotropy, false, false, false, true); + } } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 5dc8f81e4b..bea9964313 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -414,6 +414,14 @@ public: return supports_depth_comparison; } + [[nodiscard]] bool RequiresBorderColorFormat() const noexcept { + return requires_border_color_format; + } + + [[nodiscard]] bool SupportsMinmaxFilter() const noexcept { + return supports_minmax_filter; + } + [[nodiscard]] GPUVAddr GpuAddr() const noexcept { return gpu_addr; } @@ -447,6 +455,8 @@ private: u32 buffer_size = 0; bool supports_depth_comparison = false; + bool requires_border_color_format = false; + bool supports_minmax_filter = false; }; class ImageAlloc : public VideoCommon::ImageAllocBase {}; @@ -483,11 +493,29 @@ public: return static_cast(sampler_noncompare); } + [[nodiscard]] VkSampler HandleWithDefaultBorderColor() const noexcept { + return *sampler_default_border; + } + + [[nodiscard]] bool HasCustomBorderColor() const noexcept { + return static_cast(sampler_default_border); + } + + [[nodiscard]] VkSampler HandleWithDefaultReduction() const noexcept { + return *sampler_default_reduction; + } + + [[nodiscard]] bool HasMinmaxReduction() const noexcept { + return static_cast(sampler_default_reduction); + } + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; vk::Sampler sampler_nearest; vk::Sampler sampler_noncompare; + vk::Sampler sampler_default_border; + vk::Sampler sampler_default_reduction; }; struct TextureCacheParams { diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index 78eb577e10..1d899eb490 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/cityhash.h" #include "common/settings.h" @@ -17,51 +18,22 @@ namespace Tegra::Texture { namespace { -[[maybe_unused]] constexpr std::array SRGB_CONVERSION_LUT = { - 0.000000f, 0.000000f, 0.000000f, 0.000012f, 0.000021f, 0.000033f, 0.000046f, 0.000062f, - 0.000081f, 0.000102f, 0.000125f, 0.000151f, 0.000181f, 0.000214f, 0.000251f, 0.000293f, - 0.000338f, 0.000388f, 0.000443f, 0.000503f, 0.000568f, 0.000639f, 0.000715f, 0.000798f, - 0.000887f, 0.000983f, 0.001085f, 0.001195f, 0.001312f, 0.001437f, 0.001569f, 0.001710f, - 0.001860f, 0.002019f, 0.002186f, 0.002364f, 0.002551f, 0.002748f, 0.002955f, 0.003174f, - 0.003403f, 0.003643f, 0.003896f, 0.004160f, 0.004436f, 0.004725f, 0.005028f, 0.005343f, - 0.005672f, 0.006015f, 0.006372f, 0.006744f, 0.007130f, 0.007533f, 0.007950f, 0.008384f, - 0.008834f, 0.009301f, 0.009785f, 0.010286f, 0.010805f, 0.011342f, 0.011898f, 0.012472f, - 0.013066f, 0.013680f, 0.014313f, 0.014967f, 0.015641f, 0.016337f, 0.017054f, 0.017793f, - 0.018554f, 0.019337f, 0.020144f, 0.020974f, 0.021828f, 0.022706f, 0.023609f, 0.024536f, - 0.025489f, 0.026468f, 0.027473f, 0.028504f, 0.029563f, 0.030649f, 0.031762f, 0.032904f, - 0.034074f, 0.035274f, 0.036503f, 0.037762f, 0.039050f, 0.040370f, 0.041721f, 0.043103f, - 0.044518f, 0.045964f, 0.047444f, 0.048956f, 0.050503f, 0.052083f, 0.053699f, 0.055349f, - 0.057034f, 0.058755f, 0.060513f, 0.062307f, 0.064139f, 0.066008f, 0.067915f, 0.069861f, - 0.071845f, 0.073869f, 0.075933f, 0.078037f, 0.080182f, 0.082369f, 0.084597f, 0.086867f, - 0.089180f, 0.091535f, 0.093935f, 0.096378f, 0.098866f, 0.101398f, 0.103977f, 0.106601f, - 0.109271f, 0.111988f, 0.114753f, 0.117565f, 0.120426f, 0.123335f, 0.126293f, 0.129301f, - 0.132360f, 0.135469f, 0.138629f, 0.141841f, 0.145105f, 0.148421f, 0.151791f, 0.155214f, - 0.158691f, 0.162224f, 0.165810f, 0.169453f, 0.173152f, 0.176907f, 0.180720f, 0.184589f, - 0.188517f, 0.192504f, 0.196549f, 0.200655f, 0.204820f, 0.209046f, 0.213334f, 0.217682f, - 0.222093f, 0.226567f, 0.231104f, 0.235704f, 0.240369f, 0.245099f, 0.249894f, 0.254754f, - 0.259681f, 0.264674f, 0.269736f, 0.274864f, 0.280062f, 0.285328f, 0.290664f, 0.296070f, - 0.301546f, 0.307094f, 0.312713f, 0.318404f, 0.324168f, 0.330006f, 0.335916f, 0.341902f, - 0.347962f, 0.354097f, 0.360309f, 0.366597f, 0.372961f, 0.379403f, 0.385924f, 0.392524f, - 0.399202f, 0.405960f, 0.412798f, 0.419718f, 0.426719f, 0.433802f, 0.440967f, 0.448216f, - 0.455548f, 0.462965f, 0.470465f, 0.478052f, 0.485725f, 0.493484f, 0.501329f, 0.509263f, - 0.517285f, 0.525396f, 0.533595f, 0.541885f, 0.550265f, 0.558736f, 0.567299f, 0.575954f, - 0.584702f, 0.593542f, 0.602477f, 0.611507f, 0.620632f, 0.629852f, 0.639168f, 0.648581f, - 0.658092f, 0.667700f, 0.677408f, 0.687214f, 0.697120f, 0.707127f, 0.717234f, 0.727443f, - 0.737753f, 0.748167f, 0.758685f, 0.769305f, 0.780031f, 0.790861f, 0.801798f, 0.812839f, - 0.823989f, 0.835246f, 0.846611f, 0.858085f, 0.869668f, 0.881360f, 0.893164f, 0.905078f, - 0.917104f, 0.929242f, 0.941493f, 0.953859f, 0.966338f, 1.000000f, 1.000000f, 1.000000f, -}; +float SrgbToLinear(u32 value) { + const float encoded = static_cast(value) / 255.0f; + if (encoded <= 0.04045f) { + return encoded / 12.92f; + } + return std::pow((encoded + 0.055f) / 1.055f, 2.4f); +} } // Anonymous namespace std::array TSCEntry::BorderColor() const noexcept { - // TODO: Handle SRGB correctly. Using this breaks shadows in some games (Xenoblade). - // if (!srgb_conversion) { - // return border_color; - //} - // return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g], - // SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]}; - return border_color; + if (!srgb_conversion) { + return border_color; + } + return {SrgbToLinear(srgb_border_color_r), SrgbToLinear(srgb_border_color_g), + SrgbToLinear(srgb_border_color_b), border_color[3]}; } float TSCEntry::MaxAnisotropy() const noexcept { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ff8f834785..09e7f88f71 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -523,12 +523,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation."); has_broken_descriptor_aliasing = true; LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing."); - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color."); - RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, - VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken border color swizzle."); - RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle, - VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable."); RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable, VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 2a51024645..76100b5067 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -651,7 +651,6 @@ FN_MAX_LIMIT_LIST } /// Returns true if depth/stencil operations can be performed efficiently. - /// Either through shader export or hardware blits. bool CanPerformDepthStencilOperations() const { return extensions.shader_stencil_export || is_blit_depth24_stencil8_supported || is_blit_depth32_stencil8_supported; @@ -708,19 +707,12 @@ FN_MAX_LIMIT_LIST return features.transform_feedback.geometryStreams; } - /// Returns true if the device supports VK_EXT_custom_border_color. - bool IsExtCustomBorderColorSupported() const { - return extensions.custom_border_color; - } - - /// Returns true if customBorderColors feature is available. - bool IsCustomBorderColorsSupported() const { - return features.custom_border_color.customBorderColors; - } - - /// Returns true if customBorderColorWithoutFormat feature is available. - bool IsCustomBorderColorWithoutFormatSupported() const { - return features.custom_border_color.customBorderColorWithoutFormat; + /// Returns true if custom border colors stay defined under non-identity view swizzles. + bool IsCustomBorderColorUsable() const { + return extensions.custom_border_color && + features.custom_border_color.customBorderColors && + features.custom_border_color.customBorderColorWithoutFormat && + extensions.border_color_swizzle; } /// Returns true if the device supports VK_EXT_color_write_enable. From 8e687924b8fcd16dce023f66c5e4f341933c21f2 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 17:52:25 -0400 Subject: [PATCH 17/72] Funny meows --- src/video_core/renderer_vulkan/vk_texture_cache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 7aa9f19935..7dc73274dd 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -55,7 +55,7 @@ namespace { constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; -[[nodiscard]] constexpr bool RequiresBorderColorFormat(VkFormat format) { +[[nodiscard]] constexpr bool NeedsExplicitBorderColorFormat(VkFormat format) { switch (format) { case VK_FORMAT_B4G4R4A4_UNORM_PACK16: case VK_FORMAT_B5G6R5_UNORM_PACK16: @@ -2404,7 +2404,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI (device->GetPhysical().GetFormatProperties(format_info.format).optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } - requires_border_color_format = RequiresBorderColorFormat(format_info.format); + requires_border_color_format = NeedsExplicitBorderColorFormat(format_info.format); const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); const VkImageUsageFlags image_usage = image.UsageFlags(); const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; From 3379556d8930dd59abc4ab7dbba5f9640401663f Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 16 Aug 2026 18:57:37 -0400 Subject: [PATCH 18/72] [TEST] Return Xenoblade workaround + extend srgb handling on border colors --- .../renderer_vulkan/pipeline_helper.h | 4 ++ .../renderer_vulkan/vk_texture_cache.cpp | 37 +++++++++++++++++-- .../renderer_vulkan/vk_texture_cache.h | 9 +++++ src/video_core/textures/texture.cpp | 7 ++-- src/video_core/textures/texture.h | 2 + 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 8a4cb6273f..7a71c68c08 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -383,6 +383,10 @@ inline void PushImageDescriptors(TextureCache& texture_cache, !image_view.SupportsDepthComparison()) { vk_sampler = sampler.HandleWithoutDepthComparison(); } + if (sampler.HasSrgbBorderColor() && + VideoCore::Surface::IsPixelFormatSRGB(image_view.format)) { + vk_sampler = sampler.HandleWithSrgbBorderColor(); + } if (sampler.HasMinmaxReduction() && !image_view.SupportsMinmaxFilter()) { vk_sampler = sampler.HandleWithDefaultReduction(); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 7dc73274dd..69d3f745a8 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2607,6 +2607,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t const auto& device = runtime.device; const bool has_custom_border_colors = runtime.device.IsCustomBorderColorUsable(); const auto color = tsc.BorderColor(); + const auto srgb_color = tsc.SrgbBorderColor(); const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, @@ -2614,9 +2615,17 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .customBorderColor = std::bit_cast(color), .format = VK_FORMAT_UNDEFINED, }; + const VkSamplerCustomBorderColorCreateInfoEXT srgb_border_ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, + .pNext = nullptr, + .customBorderColor = std::bit_cast(srgb_color), + .format = VK_FORMAT_UNDEFINED, + }; const void* pnext = nullptr; + const void* srgb_pnext = nullptr; if (has_custom_border_colors) { pnext = &border_ci; + srgb_pnext = &srgb_border_ci; if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( "VK_EXT_custom_border_color", "Sampler::Sampler"); @@ -2631,6 +2640,11 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .pNext = pnext, .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), }; + const VkSamplerReductionModeCreateInfoEXT reduction_ci_srgb{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, + .pNext = srgb_pnext, + .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), + }; const VkSamplerReductionModeCreateInfoEXT reduction_ci_without_border{ .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, .pNext = nullptr, @@ -2642,6 +2656,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; if (runtime.device.IsExtSamplerFilterMinmaxSupported()) { pnext = &reduction_ci; + srgb_pnext = &reduction_ci_srgb; pnext_without_border = &reduction_ci_without_border; } else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) { LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required"); @@ -2659,16 +2674,29 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t const auto create_sampler = [&](const f32 anisotropy, bool force_nearest, bool disable_compare = false, bool disable_custom_border = false, - bool disable_minmax = false) { + bool disable_minmax = false, bool use_srgb_border = false) { const bool custom_border = has_custom_border_colors && !disable_custom_border; const bool minmax = has_minmax_reduction && !disable_minmax; const void* chain = nullptr; if (custom_border && minmax) { chain = pnext; + if (use_srgb_border) { + chain = srgb_pnext; + } } else if (minmax) { chain = pnext_without_border; } else if (custom_border) { chain = &border_ci; + if (use_srgb_border) { + chain = &srgb_border_ci; + } + } + VkBorderColor fixed_border = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT; + if (!custom_border) { + fixed_border = ConvertBorderColor(color); + if (use_srgb_border) { + fixed_border = ConvertBorderColor(srgb_color); + } } return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, @@ -2689,8 +2717,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), - .borderColor = custom_border ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT - : ConvertBorderColor(color), + .borderColor = fixed_border, .unnormalizedCoordinates = VK_FALSE, }); }; @@ -2713,6 +2740,10 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t if (has_minmax_reduction) { sampler_default_reduction = create_sampler(max_anisotropy, false, false, false, true); } + if (tsc.srgb_conversion && srgb_color != color) { + sampler_srgb_border = + create_sampler(max_anisotropy, false, false, false, false, true); + } } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index bea9964313..0dfd87292d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -509,6 +509,14 @@ public: return static_cast(sampler_default_reduction); } + [[nodiscard]] VkSampler HandleWithSrgbBorderColor() const noexcept { + return *sampler_srgb_border; + } + + [[nodiscard]] bool HasSrgbBorderColor() const noexcept { + return static_cast(sampler_srgb_border); + } + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; @@ -516,6 +524,7 @@ private: vk::Sampler sampler_noncompare; vk::Sampler sampler_default_border; vk::Sampler sampler_default_reduction; + vk::Sampler sampler_srgb_border; }; struct TextureCacheParams { diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index 1d899eb490..dc3b0b8244 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -29,9 +29,10 @@ float SrgbToLinear(u32 value) { } // Anonymous namespace std::array TSCEntry::BorderColor() const noexcept { - if (!srgb_conversion) { - return border_color; - } + return border_color; +} + +std::array TSCEntry::SrgbBorderColor() const noexcept { return {SrgbToLinear(srgb_border_color_r), SrgbToLinear(srgb_border_color_g), SrgbToLinear(srgb_border_color_b), border_color[3]}; } diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 7e5837b206..112885f0c3 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -378,6 +378,8 @@ struct TSCEntry { std::array BorderColor() const noexcept; + std::array SrgbBorderColor() const noexcept; + float MaxAnisotropy() const noexcept; float MinLod() const { From 20a775ed08d86c985a9c49fbe79b20223ead4a73 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 16 Aug 2026 19:34:45 -0400 Subject: [PATCH 19/72] Another meow --- src/video_core/vulkan_common/vulkan_device.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 76100b5067..45dc6587f2 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -707,12 +707,11 @@ FN_MAX_LIMIT_LIST return features.transform_feedback.geometryStreams; } - /// Returns true if custom border colors stay defined under non-identity view swizzles. + /// Returns true if custom border colors can be created without a format. bool IsCustomBorderColorUsable() const { return extensions.custom_border_color && features.custom_border_color.customBorderColors && - features.custom_border_color.customBorderColorWithoutFormat && - extensions.border_color_swizzle; + features.custom_border_color.customBorderColorWithoutFormat; } /// Returns true if the device supports VK_EXT_color_write_enable. From f6668ef01b5ff50f94710e88f7c20b4d313c82ed Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 16 Aug 2026 19:59:23 -0400 Subject: [PATCH 20/72] [TEST] Adjustments over the vkformat undefined and border color swizzle --- .../renderer_vulkan/pipeline_helper.h | 9 +- .../renderer_vulkan/vk_texture_cache.cpp | 120 ++++++++++++++---- .../renderer_vulkan/vk_texture_cache.h | 32 +++++ .../vulkan_common/vulkan_device.cpp | 4 +- src/video_core/vulkan_common/vulkan_device.h | 6 + 5 files changed, 137 insertions(+), 34 deletions(-) diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 7a71c68c08..481a4d8181 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -370,7 +370,7 @@ inline void PushImageDescriptors(TextureCache& texture_cache, const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)}; if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view; } - const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; + Sampler& sampler{texture_cache.GetSampler(sampler_id)}; const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && !image_view.SupportsAnisotropy()}; VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() @@ -383,8 +383,11 @@ inline void PushImageDescriptors(TextureCache& texture_cache, !image_view.SupportsDepthComparison()) { vk_sampler = sampler.HandleWithoutDepthComparison(); } - if (sampler.HasSrgbBorderColor() && - VideoCore::Surface::IsPixelFormatSRGB(image_view.format)) { + const bool srgb_border{sampler.HasSrgbBorderColor() && + VideoCore::Surface::IsPixelFormatSRGB(image_view.format)}; + if (sampler.NeedsSwizzleMapping() && !image_view.HasIdentitySwizzle()) { + vk_sampler = sampler.HandleWithSwizzle(image_view.Swizzle(), srgb_border); + } else if (srgb_border) { vk_sampler = sampler.HandleWithSrgbBorderColor(); } if (sampler.HasMinmaxReduction() && !image_view.SupportsMinmaxFilter()) { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 69d3f745a8..2e37a99497 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2405,6 +2405,14 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } requires_border_color_format = NeedsExplicitBorderColorFormat(format_info.format); + swizzle_mapping = VkComponentMapping{ + .r = ComponentSwizzle(swizzle[0]), + .g = ComponentSwizzle(swizzle[1]), + .b = ComponentSwizzle(swizzle[2]), + .a = ComponentSwizzle(swizzle[3]), + }; + has_identity_swizzle = swizzle[0] == SwizzleSource::R && swizzle[1] == SwizzleSource::G && + swizzle[2] == SwizzleSource::B && swizzle[3] == SwizzleSource::A; const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); const VkImageUsageFlags image_usage = image.UsageFlags(); const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; @@ -2429,12 +2437,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI .image = image.Handle(), .viewType = VkImageViewType{}, .format = format_info.format, - .components{ - .r = ComponentSwizzle(swizzle[0]), - .g = ComponentSwizzle(swizzle[1]), - .b = ComponentSwizzle(swizzle[2]), - .a = ComponentSwizzle(swizzle[3]), - }, + .components = swizzle_mapping, .subresourceRange = MakeSubresourceRange(aspect_mask, info.range), }; const auto create = [&](TextureType tex_type, std::optional num_layers) { @@ -2671,6 +2674,33 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t min_filter == VK_FILTER_LINEAR || mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR}; + const auto make_create_info = [&](const f32 anisotropy, bool force_nearest, + bool disable_compare, const void* chain, + VkBorderColor fixed_border) { + return VkSamplerCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = chain, + .flags = 0, + .magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter, + .minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter, + .mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode, + .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), + .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), + .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), + .mipLodBias = tsc.LodBias(), + .anisotropyEnable = + static_cast(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE), + .maxAnisotropy = force_nearest ? 1.0f : anisotropy, + .compareEnable = disable_compare ? VK_FALSE + : static_cast(tsc.depth_compare_enabled), + .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), + .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), + .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), + .borderColor = fixed_border, + .unnormalizedCoordinates = VK_FALSE, + }; + }; + const auto create_sampler = [&](const f32 anisotropy, bool force_nearest, bool disable_compare = false, bool disable_custom_border = false, @@ -2698,28 +2728,8 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t fixed_border = ConvertBorderColor(srgb_color); } } - return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = chain, - .flags = 0, - .magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter, - .minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter, - .mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode, - .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), - .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), - .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), - .mipLodBias = tsc.LodBias(), - .anisotropyEnable = - static_cast(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE), - .maxAnisotropy = force_nearest ? 1.0f : anisotropy, - .compareEnable = disable_compare ? VK_FALSE - : static_cast(tsc.depth_compare_enabled), - .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), - .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), - .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), - .borderColor = fixed_border, - .unnormalizedCoordinates = VK_FALSE, - }); + return device.GetLogical().CreateSampler( + make_create_info(anisotropy, force_nearest, disable_compare, chain, fixed_border)); }; sampler = create_sampler(max_anisotropy, false); @@ -2744,6 +2754,60 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t sampler_srgb_border = create_sampler(max_anisotropy, false, false, false, false, true); } + needs_swizzle_mapping = has_custom_border_colors && device.NeedsBorderColorSwizzleMapping(); + if (needs_swizzle_mapping) { + device_ptr = &device; + border_color_value = color; + srgb_border_color_value = srgb_color; + swizzle_reduction_mode = reduction_ci.reductionMode; + swizzle_uses_reduction = has_minmax_reduction; + swizzle_base_ci = make_create_info(max_anisotropy, false, false, nullptr, + VK_BORDER_COLOR_FLOAT_CUSTOM_EXT); + } +} + +VkSampler Sampler::HandleWithSwizzle(const VkComponentMapping& mapping, bool srgb) { + const auto matches = [&](const SwizzleVariant& variant) { + return variant.srgb == srgb && variant.mapping.r == mapping.r && + variant.mapping.g == mapping.g && variant.mapping.b == mapping.b && + variant.mapping.a == mapping.a; + }; + const auto it = std::ranges::find_if(swizzle_variants, matches); + if (it != swizzle_variants.end()) { + return *it->sampler; + } + std::array value = border_color_value; + if (srgb) { + value = srgb_border_color_value; + } + const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, + .pNext = nullptr, + .customBorderColor = std::bit_cast(value), + .format = VK_FORMAT_UNDEFINED, + }; + const VkSamplerBorderColorComponentMappingCreateInfoEXT mapping_ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT, + .pNext = &border_ci, + .components = mapping, + .srgb = VK_FALSE, + }; + const VkSamplerReductionModeCreateInfoEXT reduction_ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, + .pNext = &mapping_ci, + .reductionMode = swizzle_reduction_mode, + }; + VkSamplerCreateInfo create_info = swizzle_base_ci; + create_info.pNext = &mapping_ci; + if (swizzle_uses_reduction) { + create_info.pNext = &reduction_ci; + } + swizzle_variants.push_back(SwizzleVariant{ + .mapping = mapping, + .srgb = srgb, + .sampler = device_ptr->GetLogical().CreateSampler(create_info), + }); + return *swizzle_variants.back().sampler; } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 0dfd87292d..68fcc1eac6 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -422,6 +422,14 @@ public: return supports_minmax_filter; } + [[nodiscard]] const VkComponentMapping& Swizzle() const noexcept { + return swizzle_mapping; + } + + [[nodiscard]] bool HasIdentitySwizzle() const noexcept { + return has_identity_swizzle; + } + [[nodiscard]] GPUVAddr GpuAddr() const noexcept { return gpu_addr; } @@ -454,9 +462,12 @@ private: VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; u32 buffer_size = 0; + VkComponentMapping swizzle_mapping{}; + bool supports_depth_comparison = false; bool requires_border_color_format = false; bool supports_minmax_filter = false; + bool has_identity_swizzle = true; }; class ImageAlloc : public VideoCommon::ImageAllocBase {}; @@ -517,7 +528,19 @@ public: return static_cast(sampler_srgb_border); } + [[nodiscard]] bool NeedsSwizzleMapping() const noexcept { + return needs_swizzle_mapping; + } + + [[nodiscard]] VkSampler HandleWithSwizzle(const VkComponentMapping& mapping, bool srgb); + private: + struct SwizzleVariant { + VkComponentMapping mapping; + bool srgb; + vk::Sampler sampler; + }; + vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; vk::Sampler sampler_nearest; @@ -525,6 +548,15 @@ private: vk::Sampler sampler_default_border; vk::Sampler sampler_default_reduction; vk::Sampler sampler_srgb_border; + + std::vector swizzle_variants; + const Device* device_ptr = nullptr; + VkSamplerCreateInfo swizzle_base_ci{}; + VkSamplerReductionModeEXT swizzle_reduction_mode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; + std::array border_color_value{}; + std::array srgb_border_color_value{}; + bool swizzle_uses_reduction = false; + bool needs_swizzle_mapping = false; }; struct TextureCacheParams { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 09e7f88f71..092d985f4f 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1343,9 +1343,7 @@ void Device::RemoveUnsuitableExtensions() { // VK_EXT_border_color_swizzle if (extensions.border_color_swizzle) { extensions.border_color_swizzle = - extensions.custom_border_color && - features.border_color_swizzle.borderColorSwizzle && - features.border_color_swizzle.borderColorSwizzleFromImage; + extensions.custom_border_color && features.border_color_swizzle.borderColorSwizzle; } RemoveExtensionFeatureIfUnsuitable(extensions.border_color_swizzle, features.border_color_swizzle, diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 45dc6587f2..7e40f8eeb6 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -724,6 +724,12 @@ FN_MAX_LIMIT_LIST return extensions.border_color_swizzle; } + /// Returns true if samplers must be carried with border color swizzle mapping. + bool NeedsBorderColorSwizzleMapping() const { + return extensions.border_color_swizzle && + !features.border_color_swizzle.borderColorSwizzleFromImage; + } + /// Returns true if borderColorSwizzleFromImage is available. bool IsBorderColorSwizzleFromImageSupported() const { return features.border_color_swizzle.borderColorSwizzleFromImage; From 2bf037c7bb9a886297c6cf13b11092f7ca171817 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 16 Aug 2026 20:52:37 -0400 Subject: [PATCH 21/72] [TEST] Swizzling adjustment on the new fast path --- src/video_core/textures/decoders.cpp | 42 ++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 97458b80ca..4435ffe5f0 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -87,6 +87,34 @@ void SwizzleRow(std::span output, std::span input, u32 offset_zy, } } +template +void SwizzleRowPerPixel(std::span output, std::span input, u32 offset_zy, + u32 swizzled_y, u32 x_shift, u32 x, u32 num_bytes, u32 linear) { + u32 swizzled_x = pdep(x); + for (u32 byte = 0; byte < num_bytes; + byte += BYTES_PER_PIXEL, incrpdep(swizzled_x)) { + const u32 offset_x = ((x + byte) >> GOB_SIZE_X_SHIFT) << x_shift; + const u32 swizzled = offset_zy + offset_x + (swizzled_x | swizzled_y); + const u32 unswizzled = linear + byte; + + u8* const dst = &output[TO_LINEAR ? swizzled : unswizzled]; + const u8* const src = &input[TO_LINEAR ? unswizzled : swizzled]; + + std::memcpy(dst, src, BYTES_PER_PIXEL); + } +} + +template +void SwizzleRowDispatch(std::span output, std::span input, u32 offset_zy, + u32 swizzled_y, u32 x_shift, u32 x, u32 num_bytes, u32 linear) { + if constexpr (SWIZZLE_RUN_BYTES % BYTES_PER_PIXEL == 0) { + SwizzleRow(output, input, offset_zy, swizzled_y, x_shift, x, num_bytes, linear); + } else { + SwizzleRowPerPixel(output, input, offset_zy, swizzled_y, x_shift, + x, num_bytes, linear); + } +} + template void SwizzleImpl(std::span output, std::span input, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride) { @@ -121,9 +149,10 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 const u32 offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - SwizzleRow(output, input, offset_z + offset_y, swizzled_y, x_shift, - origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL, - slice * pitch * height + line * pitch); + SwizzleRowDispatch( + output, input, offset_z + offset_y, swizzled_y, x_shift, + origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL, + slice * pitch * height + line * pitch); } } } @@ -166,9 +195,10 @@ void SwizzleSubrectImpl(std::span output, std::span input, u32 wid const u32 offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - SwizzleRow(output, input, offset_z + offset_y, swizzled_y, x_shift, - origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL, - slice * pitch * height + line * pitch); + SwizzleRowDispatch( + output, input, offset_z + offset_y, swizzled_y, x_shift, + origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL, + slice * pitch * height + line * pitch); } unprocessed_lines -= lines_in_y; if (unprocessed_lines == 0) { From 04d08f0cf10ea88416403854726a04ee7a8f2669 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 16 Aug 2026 22:41:06 -0400 Subject: [PATCH 22/72] [TEST] Blitting on MSAA --- .../host_shaders/blit_depth_msaa.frag | 2 +- .../host_shaders/blit_depth_stencil_msaa.frag | 4 +- src/video_core/renderer_vulkan/blit_image.cpp | 89 +++++++++++++++++++ src/video_core/renderer_vulkan/blit_image.h | 9 ++ .../renderer_vulkan/vk_texture_cache.cpp | 49 ++++++---- .../vulkan_common/vulkan_device.cpp | 7 -- src/video_core/vulkan_common/vulkan_device.h | 5 -- 7 files changed, 134 insertions(+), 31 deletions(-) diff --git a/src/video_core/host_shaders/blit_depth_msaa.frag b/src/video_core/host_shaders/blit_depth_msaa.frag index 064289ad04..9e955d471e 100644 --- a/src/video_core/host_shaders/blit_depth_msaa.frag +++ b/src/video_core/host_shaders/blit_depth_msaa.frag @@ -8,5 +8,5 @@ layout(binding = 0) uniform sampler2DMS depth_tex; layout(location = 0) in vec2 texcoord; void main() { - gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r; + gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r; } diff --git a/src/video_core/host_shaders/blit_depth_stencil_msaa.frag b/src/video_core/host_shaders/blit_depth_stencil_msaa.frag index d4264f2d14..7d4555bfd4 100644 --- a/src/video_core/host_shaders/blit_depth_stencil_msaa.frag +++ b/src/video_core/host_shaders/blit_depth_stencil_msaa.frag @@ -10,6 +10,6 @@ layout(binding = 1) uniform usampler2DMS stencil_tex; layout(location = 0) in vec2 texcoord; void main() { - gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r; - gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), 0).r); + gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r; + gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), gl_SampleID).r); } diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index c4560177b8..d0d64d936a 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -710,6 +710,46 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, scheduler.InvalidateState(); } +void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, + ImageView& src_image_view, const Region2D& dst_region, + const Region2D& src_region) { + const bool blit_stencil = + dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported(); + const BlitMSAAPipelineKey key{ + .renderpass = dst_framebuffer->RenderPass(), + .samples = dst_framebuffer->Samples(), + }; + const VkPipeline pipeline = FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil); + const VkPipelineLayout layout = + blit_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 = + blit_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, blit_stencil](vk::CommandBuffer cmdbuf) { + if (blit_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::ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region) { @@ -1394,6 +1434,55 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip return *blit_msaa_color_pipelines.back(); } +VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline( + const BlitMSAAPipelineKey& key, bool blit_stencil) { + auto& keys = blit_stencil ? blit_msaa_depth_stencil_keys : blit_msaa_depth_keys; + auto& pipelines = blit_stencil ? blit_msaa_depth_stencil_pipelines : blit_msaa_depth_pipelines; + const auto it = std::ranges::find(keys, key); + if (it != keys.end()) { + return *pipelines[std::distance(keys.begin(), it)]; + } + keys.push_back(key); + const std::array stages = + MakeStages(*full_screen_vert, + blit_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_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); + pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(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 = blit_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 = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout, + .renderPass = key.renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + }, device.StaticPipelineCache())); + return *pipelines.back(); +} + VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil) { auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys; diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 3e6c2b09dd..0ff73064a1 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -78,6 +78,9 @@ public: void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region); + void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, 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); @@ -140,6 +143,8 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key, bool copy_stencil); [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); + [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline( + const BlitMSAAPipelineKey& key, bool blit_stencil); [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil); @@ -210,6 +215,10 @@ private: std::vector msaa_copy_depth_stencil_pipelines; std::vector blit_msaa_color_keys; std::vector blit_msaa_color_pipelines; + std::vector blit_msaa_depth_keys; + std::vector blit_msaa_depth_pipelines; + std::vector blit_msaa_depth_stencil_keys; + std::vector blit_msaa_depth_stencil_pipelines; std::vector resolve_depth_keys; std::vector resolve_depth_pipelines; std::vector resolve_depth_stencil_keys; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2e37a99497..bfac4c5071 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -682,6 +682,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im }; } +[[nodiscard]] bool HaveSameExtent(const Region2D& dst_region, const Region2D& src_region) { + return dst_region.end.x - dst_region.start.x == src_region.end.x - src_region.start.x && + dst_region.end.y - dst_region.start.y == src_region.end.y - src_region.start.y; +} + [[nodiscard]] VkImageResolve MakeImageResolve(const Region2D& dst_region, const Region2D& src_region, const VkImageSubresourceLayers& dst_layers, @@ -779,15 +784,15 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dst_image, src_image, extent, resources, aspect_mask, resolution, is_2d, vk_filter, up_scaling](vk::CommandBuffer cmdbuf) { + const u32 scaled_width = resolution.ScaleUp(extent.width); + const u32 scaled_height = is_2d ? resolution.ScaleUp(extent.height) : extent.height; const VkOffset2D src_size{ - .x = static_cast(up_scaling ? extent.width : resolution.ScaleUp(extent.width)), - .y = static_cast(is_2d && up_scaling ? extent.height - : resolution.ScaleUp(extent.height)), + .x = static_cast(up_scaling ? extent.width : scaled_width), + .y = static_cast(up_scaling ? extent.height : scaled_height), }; const VkOffset2D dst_size{ - .x = static_cast(up_scaling ? resolution.ScaleUp(extent.width) : extent.width), - .y = static_cast(is_2d && up_scaling ? resolution.ScaleUp(extent.height) - : extent.height), + .x = static_cast(up_scaling ? scaled_width : extent.width), + .y = static_cast(up_scaling ? scaled_height : extent.height), }; boost::container::small_vector regions; regions.reserve(resources.levels); @@ -1286,8 +1291,14 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region); return; } - if (is_msaa_to_msaa && device.CantBlitMSAA()) { - UNIMPLEMENTED_MSG("MSAA to MSAA depth-stencil blit is not supported on this driver"); + if (is_msaa_to_msaa) { + blit_image_helper.BlitDepthStencilMSAA(dst_framebuffer, src, dst_region, src_region); + return; + } + + const bool is_resolve = is_src_msaa && !is_dst_msaa; + if (is_resolve && !HaveSameExtent(dst_region, src_region)) { + blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region); return; } @@ -1295,7 +1306,6 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst 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, aspect_mask, is_resolve](vk::CommandBuffer cmdbuf) { @@ -2335,12 +2345,21 @@ bool Image::BlitScaleHelper(bool 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) && - info.num_samples == 1) { + } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (!blit_framebuffer) blit_framebuffer.emplace(*runtime, nullptr, view_ptr, extent, scale_up); - runtime->blit_image_helper.BlitDepthStencil(&*blit_framebuffer, *blit_view, - dst_region, src_region, operation, BLIT_OPERATION); + if (info.num_samples > 1) { + runtime->blit_image_helper.BlitDepthStencilMSAA(&*blit_framebuffer, *blit_view, + dst_region, src_region); + } else { + runtime->blit_image_helper.BlitDepthStencil(&*blit_framebuffer, *blit_view, + dst_region, src_region, operation, BLIT_OPERATION); + } + } else if (aspect_mask == VK_IMAGE_ASPECT_DEPTH_BIT && info.num_samples > 1) { + if (!blit_framebuffer) + blit_framebuffer.emplace(*runtime, nullptr, view_ptr, extent, scale_up); + runtime->blit_image_helper.BlitDepthStencilMSAA(&*blit_framebuffer, *blit_view, dst_region, + src_region); } else { // TODO: Use helper blits where applicable flags &= ~ImageFlagBits::Rescaled; @@ -2352,9 +2371,7 @@ 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() || aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT); - if (needs_msaa_helper) { + if (info.num_samples > 1) { return true; } static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 092d985f4f..4d9d9f7d28 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -659,13 +659,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR features.shader_float16_int8.shaderFloat16 = false; } - if (is_intel_windows) { - LOG_WARNING(Render_Vulkan, - "Intel proprietary drivers do not support MSAA->MSAA image blits. " - "MSAA scaling will use 3D helpers. MSAA resolves work normally."); - cant_blit_msaa = true; - } - has_broken_compute = CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && !Settings::values.enable_compute_pipelines.GetValue(); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 7e40f8eeb6..a271c11319 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -955,10 +955,6 @@ FN_MAX_LIMIT_LIST return supports_d24_depth; } - bool CantBlitMSAA() const { - return cant_blit_msaa; - } - bool MustEmulateScaledFormats() const { return must_emulate_scaled_formats; } @@ -1230,7 +1226,6 @@ private: bool has_nsight_graphics{}; ///< Has Nsight Graphics attached bool has_radeon_gpu_profiler{}; ///< Has Radeon GPU Profiler attached. bool supports_d24_depth{}; ///< Supports D24 depth buffers. - bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3. From 26044e18e19fc438e1bb9c4bf4ecf4c246e26672 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 17 Aug 2026 00:57:17 -0400 Subject: [PATCH 23/72] Re-apply commit --- src/video_core/host_shaders/CMakeLists.txt | 1 + src/video_core/host_shaders/blit_depth.frag | 12 ++++ src/video_core/renderer_vulkan/blit_image.cpp | 56 +++++++++++++++++++ src/video_core/renderer_vulkan/blit_image.h | 7 +++ .../renderer_vulkan/vk_texture_cache.cpp | 26 ++++++--- src/video_core/texture_cache/texture_cache.h | 6 +- 6 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 src/video_core/host_shaders/blit_depth.frag diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index be46842cd9..35d092c3bc 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -19,6 +19,7 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp diff --git a/src/video_core/host_shaders/blit_depth.frag b/src/video_core/host_shaders/blit_depth.frag new file mode 100644 index 0000000000..9e19c767aa --- /dev/null +++ b/src/video_core/host_shaders/blit_depth.frag @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform sampler2D depth_tex; + +layout(location = 0) in vec2 texcoord; + +void main() { + gl_FragDepth = textureLod(depth_tex, texcoord, 0).r; +} diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index d0d64d936a..1f3d452e16 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -12,6 +12,7 @@ #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_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" @@ -599,6 +600,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported() ? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV) : vk::ShaderModule{}), + blit_depth_frag(BuildShader(device, BLIT_DEPTH_FRAG_SPV)), 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) @@ -750,6 +752,28 @@ void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, scheduler.InvalidateState(); } +void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region) { + const VkPipeline pipeline = FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()); + const VkPipelineLayout layout = *one_texture_pipeline_layout; + const VkSampler sampler = *nearest_sampler; + const VkImageView src_depth_view = src_image_view.DepthView(); + + RecordShaderReadBarrier(scheduler, src_image_view); + scheduler.RequestRenderpass(dst_framebuffer); + scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, + src_depth_view](vk::CommandBuffer cmdbuf) { + const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); + UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_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) { @@ -1483,6 +1507,38 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline( return *pipelines.back(); } +VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) { + const auto it = std::ranges::find(blit_depth_keys, renderpass); + if (it != blit_depth_keys.end()) { + return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)]; + } + blit_depth_keys.push_back(renderpass); + const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag); + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(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 = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pDepthStencilState = &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, + .renderPass = renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + }, device.StaticPipelineCache())); + return *blit_depth_pipelines.back(); +} + VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil) { auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys; diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 0ff73064a1..3b073f2825 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -81,6 +81,9 @@ public: void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region); + void BlitDepth(const Framebuffer* dst_framebuffer, 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); @@ -145,6 +148,7 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline( const BlitMSAAPipelineKey& key, bool blit_stencil); + [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass); [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil); @@ -180,6 +184,7 @@ private: vk::ShaderModule blit_color_to_color_frag; vk::ShaderModule blit_color_msaa_frag; vk::ShaderModule blit_depth_stencil_frag; + vk::ShaderModule blit_depth_frag; vk::ShaderModule blit_depth_msaa_frag; vk::ShaderModule blit_depth_stencil_msaa_frag; vk::ShaderModule clear_color_vert; @@ -215,6 +220,8 @@ private: std::vector msaa_copy_depth_stencil_pipelines; std::vector blit_msaa_color_keys; std::vector blit_msaa_color_pipelines; + std::vector blit_depth_keys; + std::vector blit_depth_pipelines; std::vector blit_msaa_depth_keys; std::vector blit_msaa_depth_pipelines; std::vector blit_msaa_depth_stencil_keys; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index bfac4c5071..2713840d60 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2265,7 +2265,10 @@ bool Image::ScaleUp(bool ignore) { aspect_mask = ImageAspectMask(info.format); } if (NeedsScaleHelper()) { - return BlitScaleHelper(true); + if (!BlitScaleHelper(true)) { + current_image = &Image::original_image; + return false; + } } else { BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution); } @@ -2290,7 +2293,11 @@ bool Image::ScaleDown(bool ignore) { aspect_mask = ImageAspectMask(info.format); } if (NeedsScaleHelper()) { - return BlitScaleHelper(false); + if (!BlitScaleHelper(false)) { + current_image = &Image::scaled_image; + flags |= ImageFlagBits::Rescaled; + return false; + } } else { BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false); } @@ -2345,21 +2352,22 @@ bool Image::BlitScaleHelper(bool 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)) { + } else if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { if (!blit_framebuffer) blit_framebuffer.emplace(*runtime, nullptr, view_ptr, extent, scale_up); + const bool has_stencil = (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; + const bool can_blit_stencil = + has_stencil && runtime->device.IsExtShaderStencilExportSupported(); if (info.num_samples > 1) { runtime->blit_image_helper.BlitDepthStencilMSAA(&*blit_framebuffer, *blit_view, dst_region, src_region); - } else { + } else if (can_blit_stencil) { runtime->blit_image_helper.BlitDepthStencil(&*blit_framebuffer, *blit_view, dst_region, src_region, operation, BLIT_OPERATION); + } else { + runtime->blit_image_helper.BlitDepth(&*blit_framebuffer, *blit_view, dst_region, + src_region); } - } else if (aspect_mask == VK_IMAGE_ASPECT_DEPTH_BIT && info.num_samples > 1) { - if (!blit_framebuffer) - blit_framebuffer.emplace(*runtime, nullptr, view_ptr, extent, scale_up); - runtime->blit_image_helper.BlitDepthStencilMSAA(&*blit_framebuffer, *blit_view, dst_region, - src_region); } else { // TODO: Use helper blits where applicable flags &= ~ImageFlagBits::Rescaled; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 98a5e053a7..9ffe0cbb25 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1476,12 +1476,12 @@ template bool TextureCache

::ScaleUp(Image& image) { const bool has_copy = image.HasScaled(); const bool rescaled = image.ScaleUp(); + if (!has_copy && image.HasScaled()) { + total_used_memory += GetScaledImageSizeBytes(image); + } if (!rescaled) { return false; } - if (!has_copy) { - total_used_memory += GetScaledImageSizeBytes(image); - } InvalidateScale(image); return true; } From 4244a6418d779cb5299e70d93afc56c679b3756c Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 17 Aug 2026 17:57:13 -0400 Subject: [PATCH 24/72] Fix license header + removal of ternary on depth stencil routes for msaa uploads --- .../translate/impl/texture_fetch_swizzled.cpp | 3 +++ .../impl/texture_gather_swizzled.cpp | 3 +++ .../translate/impl/texture_load_swizzled.cpp | 3 +++ .../frontend/maxwell/translate_program.h | 3 +++ src/video_core/renderer_vulkan/blit_image.cpp | 10 ++++---- .../renderer_vulkan/vk_compute_pass.cpp | 24 ++++++++++++------- .../renderer_vulkan/vk_texture_cache.cpp | 13 +++++----- src/video_core/textures/texture.h | 3 +++ 8 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp index 98e177a92e..032393226d 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp index 4a27433739..54e0c6f597 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp index 0fd7c1cda5..5a38292204 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load_swizzled.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 49d2800d08..23c7ba1bbf 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 1f3d452e16..482c5d7255 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1607,11 +1607,11 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; const VkFormat dst_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format; - const VkImageAspectFlags attachment_aspect = - VideoCore::Surface::GetFormatType(dst_format) == - VideoCore::Surface::SurfaceType::DepthStencil - ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT - : VK_IMAGE_ASPECT_DEPTH_BIT; + VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + if (VideoCore::Surface::GetFormatType(dst_format) == + VideoCore::Surface::SurfaceType::DepthStencil) { + attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; + } for (const VideoCommon::ImageCopy& copy : copies) { ASSERT(copy.src_subresource.base_layer == 0); ASSERT(copy.src_subresource.num_layers == 1); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 7654d49aac..236ed51361 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -1062,10 +1062,14 @@ void BlockLinearUnswizzle2DPass::Unswizzle( .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE - : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + VkPipelineStageFlags pre_copy_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + if (is_initialized) { + pre_copy_src_stages |= vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; + } else { + pre_copy_src_stages |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } + cmdbuf.PipelineBarrier(pre_copy_src_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, + buffer_barrier, pre_copy); const VkBufferImageCopy copy{ .bufferOffset = out_offset, @@ -1306,10 +1310,14 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE - : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + VkPipelineStageFlags pre_copy_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + if (is_initialized) { + pre_copy_src_stages |= vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; + } else { + pre_copy_src_stages |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } + cmdbuf.PipelineBarrier(pre_copy_src_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, + buffer_barrier, pre_copy); const VkBufferImageCopy copy{ .bufferOffset = out_offset, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2713840d60..3fea36a91c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1903,12 +1903,13 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, const bool msaa_upload_copies_stencil = msaa_upload_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && runtime->device.IsExtShaderStencilExportSupported(); - const VkImageAspectFlags upload_aspect_mask = - msaa_upload_is_depth - ? (msaa_upload_copies_stencil - ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT - : VK_IMAGE_ASPECT_DEPTH_BIT) - : aspect_mask; + VkImageAspectFlags upload_aspect_mask = aspect_mask; + if (msaa_upload_is_depth) { + upload_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (msaa_upload_copies_stencil) { + upload_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } ImageInfo temp_info = info; temp_info.num_samples = 1; diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 112885f0c3..9fc6a631bd 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later From c7b24bad1a36eab98de1909d8472f1e5c49be7e1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 17 Aug 2026 20:00:06 -0400 Subject: [PATCH 25/72] [TEST] Vertex, Samplers limits adjustments based on hardware support + MSAA Sample Counts degradation --- .../renderer_vulkan/vk_compute_pass.cpp | 6 + .../renderer_vulkan/vk_graphics_pipeline.cpp | 14 +- .../renderer_vulkan/vk_rasterizer.cpp | 14 +- .../renderer_vulkan/vk_texture_cache.cpp | 142 ++++++++++++++---- .../renderer_vulkan/vk_texture_cache.h | 24 ++- src/video_core/texture_cache/texture_cache.h | 77 +++------- .../texture_cache/texture_cache_base.h | 6 +- .../vulkan_common/vulkan_device.cpp | 89 ++++++++--- src/video_core/vulkan_common/vulkan_device.h | 38 ++++- 9 files changed, 287 insertions(+), 123 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 236ed51361..54f8f9520f 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -964,6 +964,9 @@ bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) VideoCore::Surface::IsPixelFormatBCn(info.format)) { return false; } + if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) { + return false; + } const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) { return false; @@ -1207,6 +1210,9 @@ bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device, if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) { return false; } + if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) { + return false; + } const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 345278ab41..a2eee7f16d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -695,23 +695,30 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (!key.state.dynamic_vertex_input) { const size_t num_vertex_arrays = (std::min)( Maxwell::NumVertexArrays, static_cast(device.GetMaxVertexInputBindings())); + const u32 max_stride = device.GetMaxVertexInputBindingStride(); + const u32 max_divisor = device.GetMaxVertexAttribDivisor(); for (size_t index = 0; index < num_vertex_arrays; ++index) { const bool instanced = key.state.binding_divisors[index] != 0; const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; vertex_bindings.push_back({ .binding = static_cast(index), - .stride = key.state.vertex_strides[index], + .stride = (std::min)(u32{key.state.vertex_strides[index]}, max_stride), .inputRate = rate, }); if (instanced) { vertex_binding_divisors.push_back({ .binding = static_cast(index), - .divisor = key.state.binding_divisors[index], + .divisor = (std::min)(key.state.binding_divisors[index], max_divisor), }); } } + const size_t max_attributes = static_cast(device.GetMaxVertexInputAttributes()); + const u32 max_offset = device.GetMaxVertexInputAttributeOffset(); for (size_t index = 0; index < key.state.attributes.size(); ++index) { + if (vertex_attributes.size() >= max_attributes) { + break; + } const auto& attribute = key.state.attributes[index]; if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) { continue; @@ -720,11 +727,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .location = static_cast(index), .binding = attribute.buffer, .format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()), - .offset = attribute.offset, + .offset = (std::min)(attribute.offset.Value(), max_offset), }); } } - ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes()); VkPipelineVertexInputStateCreateInfo vertex_input_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4b064c84e4..10522958cf 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1907,7 +1907,9 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) const u32 max_bindings = static_cast(std::min(Maxwell::NumVertexArrays, device.GetMaxVertexInputBindings())); - + const u32 max_offset = device.GetMaxVertexInputAttributeOffset(); + const u32 max_stride = device.GetMaxVertexInputBindingStride(); + const u32 max_divisor = device.GetMaxVertexAttribDivisor(); for (u32 index = 0; index < max_attributes; ++index) { const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; @@ -1921,20 +1923,24 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) .location = index, .binding = binding, .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), - .offset = attribute.offset, + .offset = (std::min)(attribute.offset.Value(), max_offset), }); } for (u32 binding = 0; binding < max_bindings; ++binding) { const auto& input_binding{regs.vertex_streams[binding]}; const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; + u32 divisor = 1; + if (is_instanced) { + divisor = (std::min)(input_binding.frequency, max_divisor); + } bindings.push_back({ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, .pNext = nullptr, .binding = binding, - .stride = input_binding.stride, + .stride = (std::min)(input_binding.stride.Value(), max_stride), .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, - .divisor = is_instanced ? input_binding.frequency : 1, + .divisor = divisor, }); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 3fea36a91c..f1f5879fab 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -119,7 +120,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { } [[nodiscard]] VkImageUsageFlags ImageUsageFlags(const MaxwellToVK::FormatInfo& info, - PixelFormat format) { + PixelFormat format, bool allow_storage = true) { VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; if (info.attachable) { @@ -137,7 +138,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { break; } } - if (info.storage) { + if (info.storage && allow_storage) { usage |= VK_IMAGE_USAGE_STORAGE_BIT; } return usage; @@ -173,6 +174,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; } const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); + const bool allow_storage = + info.num_samples == 1 || device.IsStorageImageMultisampleSupported(); return VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, @@ -188,7 +191,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .arrayLayers = static_cast(info.resources.layers), .samples = ConvertSampleCount(info.num_samples), .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = ImageUsageFlags(format_info, info.format), + .usage = ImageUsageFlags(format_info, info.format, allow_storage), .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1766,8 +1769,19 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } -std::optional TextureCacheRuntime::GetSamplerHeapBudget() const { - return device.GetSamplerHeapBudget(); +bool TextureCacheRuntime::IsSampleCountSupported(const VideoCommon::ImageInfo& info) const { + if (info.num_samples <= 1) { + return true; + } + const auto format_info = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format); + const bool allow_storage = device.IsStorageImageMultisampleSupported(); + const VkImageUsageFlags usage = ImageUsageFlags(format_info, info.format, allow_storage); + const VkImageAspectFlags aspect = ImageAspectMask(info.format); + const bool is_integer = VideoCore::Surface::IsPixelFormatInteger(info.format); + const VkSampleCountFlags supported = + device.GetSupportedSampleCounts(usage, aspect, is_integer); + return (supported & ConvertSampleCount(info.num_samples)) != 0; } void TextureCacheRuntime::TickFrame() { @@ -2632,12 +2646,92 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ }); } +CustomBorderColorBudget::~CustomBorderColorBudget() { + Release(); +} + +CustomBorderColorBudget::CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept + : device_ptr{std::exchange(rhs.device_ptr, nullptr)}, held{std::exchange(rhs.held, 0)} {} + +CustomBorderColorBudget& CustomBorderColorBudget::operator=( + CustomBorderColorBudget&& rhs) noexcept { + if (this != &rhs) { + Release(); + device_ptr = std::exchange(rhs.device_ptr, nullptr); + held = std::exchange(rhs.held, 0); + } + return *this; +} + +bool CustomBorderColorBudget::TryAcquire(const Device& device, size_t count) { + if (!device.TryReserveCustomBorderColorSamplers(count)) { + return false; + } + device_ptr = &device; + held += count; + return true; +} + +void CustomBorderColorBudget::Release() noexcept { + if (device_ptr != nullptr) { + device_ptr->ReleaseCustomBorderColorSamplers(held); + } + device_ptr = nullptr; + held = 0; +} + Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { const auto& device = runtime.device; - const bool has_custom_border_colors = runtime.device.IsCustomBorderColorUsable(); const auto color = tsc.BorderColor(); const auto srgb_color = tsc.SrgbBorderColor(); + const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); + const f32 max_anisotropy_default = static_cast(1U << tsc.max_anisotropy); + + const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)}; + const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)}; + const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)}; + const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR || + min_filter == VK_FILTER_LINEAR || + mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR}; + + const VkSamplerAddressMode wrap_u{ + MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter)}; + const VkSamplerAddressMode wrap_v{ + MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter)}; + const VkSamplerAddressMode wrap_p{ + MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter)}; + const bool samples_border = wrap_u == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER || + wrap_v == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER || + wrap_p == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + + const VkSamplerReductionModeEXT reduction_mode = + MaxwellToVK::SamplerReduction(tsc.reduction_filter); + const bool has_minmax_reduction = + device.IsExtSamplerFilterMinmaxSupported() && + reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; + + size_t custom_border_color_count = 1; + if (max_anisotropy > max_anisotropy_default) { + ++custom_border_color_count; + } + if (has_linear_filtering) { + ++custom_border_color_count; + } + if (tsc.depth_compare_enabled) { + ++custom_border_color_count; + } + if (has_minmax_reduction) { + ++custom_border_color_count; + } + if (tsc.srgb_conversion && srgb_color != color) { + ++custom_border_color_count; + } + + const bool has_custom_border_colors = + samples_border && device.IsCustomBorderColorUsable() && + custom_border_color_budget.TryAcquire(device, custom_border_color_count); + const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, .pNext = nullptr, @@ -2667,38 +2761,26 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t const VkSamplerReductionModeCreateInfoEXT reduction_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, .pNext = pnext, - .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), + .reductionMode = reduction_mode, }; const VkSamplerReductionModeCreateInfoEXT reduction_ci_srgb{ .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, .pNext = srgb_pnext, - .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), + .reductionMode = reduction_mode, }; const VkSamplerReductionModeCreateInfoEXT reduction_ci_without_border{ .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, .pNext = nullptr, - .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), + .reductionMode = reduction_mode, }; const void* pnext_without_border = nullptr; - const bool has_minmax_reduction = - runtime.device.IsExtSamplerFilterMinmaxSupported() && - reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; - if (runtime.device.IsExtSamplerFilterMinmaxSupported()) { + if (device.IsExtSamplerFilterMinmaxSupported()) { pnext = &reduction_ci; srgb_pnext = &reduction_ci_srgb; pnext_without_border = &reduction_ci_without_border; - } else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) { + } else if (reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) { LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required"); } - // Some games have samplers with garbage. Sanitize them here. - const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); - - const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)}; - const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)}; - const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)}; - const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR || - min_filter == VK_FILTER_LINEAR || - mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR}; const auto make_create_info = [&](const f32 anisotropy, bool force_nearest, bool disable_compare, const void* chain, @@ -2710,9 +2792,9 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter, .minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter, .mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode, - .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), - .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), - .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), + .addressModeU = wrap_u, + .addressModeV = wrap_v, + .addressModeW = wrap_p, .mipLodBias = tsc.LodBias(), .anisotropyEnable = static_cast(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE), @@ -2760,7 +2842,6 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t sampler = create_sampler(max_anisotropy, false); - const f32 max_anisotropy_default = static_cast(1U << tsc.max_anisotropy); if (max_anisotropy > max_anisotropy_default) { sampler_default_anisotropy = create_sampler(max_anisotropy_default, false); } @@ -2785,7 +2866,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t device_ptr = &device; border_color_value = color; srgb_border_color_value = srgb_color; - swizzle_reduction_mode = reduction_ci.reductionMode; + swizzle_reduction_mode = reduction_mode; swizzle_uses_reduction = has_minmax_reduction; swizzle_base_ci = make_create_info(max_anisotropy, false, false, nullptr, VK_BORDER_COLOR_FLOAT_CUSTOM_EXT); @@ -2802,6 +2883,11 @@ VkSampler Sampler::HandleWithSwizzle(const VkComponentMapping& mapping, bool srg if (it != swizzle_variants.end()) { return *it->sampler; } + constexpr size_t MAX_SWIZZLE_VARIANTS = 8; + if (swizzle_variants.size() >= MAX_SWIZZLE_VARIANTS || + !custom_border_color_budget.TryAcquire(*device_ptr, 1)) { + return *sampler; + } std::array value = border_color_value; if (srgb) { value = srgb_border_color_value; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 68fcc1eac6..b6200bd1cd 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -66,7 +66,7 @@ public: bool CanReportMemoryUsage() const; - std::optional GetSamplerHeapBudget() const; + bool IsSampleCountSupported(const VideoCommon::ImageInfo& info) const; void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, @@ -472,6 +472,26 @@ private: class ImageAlloc : public VideoCommon::ImageAllocBase {}; +class CustomBorderColorBudget { +public: + CustomBorderColorBudget() = default; + ~CustomBorderColorBudget(); + + CustomBorderColorBudget(const CustomBorderColorBudget&) = delete; + CustomBorderColorBudget& operator=(const CustomBorderColorBudget&) = delete; + + CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept; + CustomBorderColorBudget& operator=(CustomBorderColorBudget&& rhs) noexcept; + + bool TryAcquire(const Device& device, size_t count); + +private: + void Release() noexcept; + + const Device* device_ptr = nullptr; + size_t held = 0; +}; + class Sampler { public: explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); @@ -541,6 +561,8 @@ private: vk::Sampler sampler; }; + CustomBorderColorBudget custom_border_color_budget; + vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; vk::Sampler sampler_nearest; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 9ffe0cbb25..f3ff2c455e 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1161,9 +1161,21 @@ void TextureCache

::UploadImageContents(Image& image, StagingBuffer& staging) } } +template +ImageInfo TextureCache

::ClampedSampleCount(ImageInfo info) const { + if (info.num_samples > 1) { + if constexpr (requires { runtime.IsSampleCountSupported(info); }) { + if (!runtime.IsSampleCountSupported(info)) { + info.num_samples = 1; + } + } + } + return info; +} + template ImageViewId TextureCache

::CreateImageView(const TICEntry& config) { - const ImageInfo info(config); + const ImageInfo info = ClampedSampleCount(ImageInfo(config)); if (info.type == ImageType::Buffer) { const ImageViewInfo view_info(config, 0); return slot_image_views.insert(runtime, info, view_info, config.Address()); @@ -1877,67 +1889,10 @@ SamplerId TextureCache

::FindSampler(const TSCEntry& config, bool compute) { const auto [pair, is_new] = channel_state->samplers.try_emplace(config); if (is_new) { pair->second = slot_samplers.insert(runtime, config); - EnforceSamplerBudget(); } return pair->second; } -template -std::optional TextureCache

::QuerySamplerBudget() const { - if constexpr (requires { runtime.GetSamplerHeapBudget(); }) { - return runtime.GetSamplerHeapBudget(); - } else { - return std::nullopt; - } -} - -template -void TextureCache

::EnforceSamplerBudget() { - if (auto const budget = QuerySamplerBudget(); budget) { - if (slot_samplers.size() < *budget) { - return; - } - if (!channel_state) { - return; - } - if (last_sampler_gc_frame == frame_tick) { - return; - } - last_sampler_gc_frame = frame_tick; - TrimInactiveSamplers(*budget); - } -} - -template -void TextureCache

::TrimInactiveSamplers(size_t budget) { - if (channel_state->samplers.size() > 0) { - constexpr size_t SAMPLER_GC_SLACK = 1024; - ankerl::unordered_dense::set active_sampler_ids; - for (auto const& e : channel_state->sampler_ids) - active_sampler_ids.insert(e.second); - // Elements in the map must be necesarily valid - size_t removed = 0; - for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) { - const SamplerId sampler_id = it->second; - if (!sampler_id || sampler_id == CORRUPT_ID) { - it = channel_state->samplers.erase(it); - } else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) { - ++it; - } else { - slot_samplers.erase(sampler_id); - it = channel_state->samplers.erase(it); - ++removed; - if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) { - break; - } - } - } - if (removed != 0) { - LOG_WARNING(HW_GPU, "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", budget, removed); - } - } -} - template ImageViewId TextureCache

::FindColorBuffer(size_t index) { const auto& regs = maxwell3d->regs; @@ -1952,7 +1907,8 @@ ImageViewId TextureCache

::FindColorBuffer(size_t index) { if (rt.format == Tegra::RenderTargetFormat::NONE) { return ImageViewId{}; } - const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode); + const ImageInfo info = + ClampedSampleCount(ImageInfo(regs.rt[index], regs.anti_alias_samples_mode)); return FindRenderTargetView(info, gpu_addr); } @@ -1966,7 +1922,8 @@ ImageViewId TextureCache

::FindDepthBuffer() { if (gpu_addr == 0) { return ImageViewId{}; } - const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode); + const ImageInfo info = + ClampedSampleCount(ImageInfo(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode)); return FindRenderTargetView(info, gpu_addr); } diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index fafc331ef7..e75ce4e706 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -416,11 +416,10 @@ private: bool ScaleDown(Image& image); u64 GetScaledImageSizeBytes(const ImageBase& image); + [[nodiscard]] ImageInfo ClampedSampleCount(ImageInfo info) const; + void QueueAsyncDecode(Image& image, ImageId image_id); void TickAsyncDecode(); - void EnforceSamplerBudget(); - void TrimInactiveSamplers(size_t budget); - std::optional QuerySamplerBudget() const; void QueueAsyncUnswizzle(Image& image, ImageId image_id); void TickAsyncUnswizzle(); @@ -507,7 +506,6 @@ private: u64 modification_tick = 0; u64 frame_tick = 0; - u64 last_sampler_gc_frame = (std::numeric_limits::max)(); Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {}, Common::ThreadPlacement::Efficiency}; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 4d9d9f7d28..bd833ba784 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -628,21 +628,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } } - if (is_qualcomm) { - const size_t sampler_limit = properties.properties.limits.maxSamplerAllocationCount; - if (sampler_limit > 0) { - constexpr size_t MIN_SAMPLER_BUDGET = 1024U; - const size_t reserved = sampler_limit / 4U; - const size_t derived_budget = - (std::max)(MIN_SAMPLER_BUDGET, sampler_limit - reserved); - sampler_heap_budget = derived_budget; - LOG_WARNING(Render_Vulkan, - "Qualcomm driver reports max {} samplers; reserving {} (25%) and " - "allowing Eden to use {} (75%) to avoid heap exhaustion", - sampler_limit, reserved, sampler_heap_budget); - } - } - if (extensions.sampler_filter_minmax && is_amd) { // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. if (!features.shader_float16_int8.shaderFloat16) { @@ -1240,6 +1225,16 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR; SetNext(next, properties.maintenance5); } + if (extensions.custom_border_color) { + properties.custom_border_color.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT; + SetNext(next, properties.custom_border_color); + } + if (extensions.vertex_attribute_divisor) { + properties.vertex_attribute_divisor.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT; + SetNext(next, properties.vertex_attribute_divisor); + } // Perform the property fetch. physical.GetProperties2(properties2); @@ -1582,11 +1577,67 @@ void Device::SetupFamilies(VkSurfaceKHR surface) { } } -std::optional Device::GetSamplerHeapBudget() const { - if (sampler_heap_budget == 0) { - return std::nullopt; +VkSampleCountFlags Device::GetSupportedSampleCounts(VkImageUsageFlags usage, + VkImageAspectFlags aspect, + bool is_integer) const { + const VkPhysicalDeviceLimits& limits = properties.properties.limits; + const bool has_color = (aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0; + const bool has_depth = (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; + const bool has_stencil = (aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; + + VkSampleCountFlags counts = ~VkSampleCountFlags{0}; + if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) { + if (has_color) { + if (is_integer) { + counts &= limits.sampledImageIntegerSampleCounts; + } else { + counts &= limits.sampledImageColorSampleCounts; + } + } + if (has_depth) { + counts &= limits.sampledImageDepthSampleCounts; + } + if (has_stencil) { + counts &= limits.sampledImageStencilSampleCounts; + } } - return sampler_heap_budget; + if ((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) { + counts &= limits.framebufferColorSampleCounts; + } + if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) { + if (has_depth) { + counts &= limits.framebufferDepthSampleCounts; + } + if (has_stencil) { + counts &= limits.framebufferStencilSampleCounts; + } + } + if ((usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) { + counts &= limits.storageImageSampleCounts; + } + return counts; +} + +bool Device::TryReserveCustomBorderColorSamplers(size_t count) const { + const size_t limit = properties.custom_border_color.maxCustomBorderColorSamplers; + if (limit == 0) { + return true; + } + size_t used = custom_border_color_samplers_used.load(std::memory_order_relaxed); + while (used + count <= limit) { + if (custom_border_color_samplers_used.compare_exchange_weak( + used, used + count, std::memory_order_relaxed, std::memory_order_relaxed)) { + return true; + } + } + return false; +} + +void Device::ReleaseCustomBorderColorSamplers(size_t count) const { + if (count == 0) { + return; + } + custom_border_color_samplers_used.fetch_sub(count, std::memory_order_relaxed); } u64 Device::GetDeviceMemoryUsage() const { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a271c11319..02b4767095 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -458,6 +459,10 @@ FN_MAX_LIMIT_LIST return features.features.shaderStorageImageMultisample; } + /// Returns the sample counts an image with the given usage and aspect can be created with. + VkSampleCountFlags GetSupportedSampleCounts(VkImageUsageFlags usage, VkImageAspectFlags aspect, + bool is_integer) const; + /// Returns true if the device warp size can potentially be bigger than guest's warp size. bool IsWarpSizePotentiallyBiggerThanGuest() const { return is_warp_potentially_bigger; @@ -714,6 +719,17 @@ FN_MAX_LIMIT_LIST features.custom_border_color.customBorderColorWithoutFormat; } + /// Returns how many live samplers may carry a custom border color, 0 when unknown. + u32 GetMaxCustomBorderColorSamplers() const { + return properties.custom_border_color.maxCustomBorderColorSamplers; + } + + /// Takes budget for samplers carrying a custom border color, false when exhausted. + bool TryReserveCustomBorderColorSamplers(size_t count) const; + + /// Gives back budget taken by TryReserveCustomBorderColorSamplers. + void ReleaseCustomBorderColorSamplers(size_t count) const; + /// Returns true if the device supports VK_EXT_color_write_enable. bool IsExtColorWriteEnableSupported() const { return extensions.color_write_enable; @@ -925,8 +941,6 @@ FN_MAX_LIMIT_LIST return has_broken_parallel_compiling; } - std::optional GetSamplerHeapBudget() const; - /// Returns the vendor name reported from Vulkan. std::string_view GetVendorName() const { return properties.driver.driverName; @@ -977,6 +991,22 @@ FN_MAX_LIMIT_LIST return properties.properties.limits.maxVertexInputBindings; } + u32 GetMaxVertexInputAttributeOffset() const { + return properties.properties.limits.maxVertexInputAttributeOffset; + } + + u32 GetMaxVertexInputBindingStride() const { + return properties.properties.limits.maxVertexInputBindingStride; + } + + u32 GetMaxVertexAttribDivisor() const { + const u32 limit = properties.vertex_attribute_divisor.maxVertexAttribDivisor; + if (!extensions.vertex_attribute_divisor || limit == 0) { + return 1U; + } + return limit; + } + u32 GetMaxViewports() const { return properties.properties.limits.maxViewports; } @@ -1199,6 +1229,8 @@ private: VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{}; + VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{}; + VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{}; VkPhysicalDeviceProperties properties{}; }; @@ -1237,7 +1269,7 @@ private: bool dynamic_state3_alpha_to_coverage{}; bool dynamic_state3_alpha_to_one{}; bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. - size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited). + mutable std::atomic custom_border_color_samplers_used{}; u64 device_access_memory{}; ///< Total size of device local memory in bytes. u32 sets_per_pool{}; ///< Sets per Description Pool NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer}; From 809a61f6008a49d223e304acd73942fcb86f3781 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 17 Aug 2026 22:11:43 -0400 Subject: [PATCH 26/72] [TEST] Quick revert on MSAA clamp --- .../renderer_vulkan/vk_texture_cache.cpp | 15 -------------- .../renderer_vulkan/vk_texture_cache.h | 2 -- src/video_core/texture_cache/texture_cache.h | 20 +++---------------- .../texture_cache/texture_cache_base.h | 2 -- 4 files changed, 3 insertions(+), 36 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index f1f5879fab..c88d3c85e4 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1769,21 +1769,6 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } -bool TextureCacheRuntime::IsSampleCountSupported(const VideoCommon::ImageInfo& info) const { - if (info.num_samples <= 1) { - return true; - } - const auto format_info = - MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format); - const bool allow_storage = device.IsStorageImageMultisampleSupported(); - const VkImageUsageFlags usage = ImageUsageFlags(format_info, info.format, allow_storage); - const VkImageAspectFlags aspect = ImageAspectMask(info.format); - const bool is_integer = VideoCore::Surface::IsPixelFormatInteger(info.format); - const VkSampleCountFlags supported = - device.GetSupportedSampleCounts(usage, aspect, is_integer); - return (supported & ConvertSampleCount(info.num_samples)) != 0; -} - void TextureCacheRuntime::TickFrame() { std::erase_if(pending_msaa_images, [this](const auto& pending) { return scheduler.IsFree(pending.first); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index b6200bd1cd..073b852663 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -66,8 +66,6 @@ public: bool CanReportMemoryUsage() const; - bool IsSampleCountSupported(const VideoCommon::ImageInfo& info) const; - void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index f3ff2c455e..2eb94275a8 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1161,21 +1161,9 @@ void TextureCache

::UploadImageContents(Image& image, StagingBuffer& staging) } } -template -ImageInfo TextureCache

::ClampedSampleCount(ImageInfo info) const { - if (info.num_samples > 1) { - if constexpr (requires { runtime.IsSampleCountSupported(info); }) { - if (!runtime.IsSampleCountSupported(info)) { - info.num_samples = 1; - } - } - } - return info; -} - template ImageViewId TextureCache

::CreateImageView(const TICEntry& config) { - const ImageInfo info = ClampedSampleCount(ImageInfo(config)); + const ImageInfo info(config); if (info.type == ImageType::Buffer) { const ImageViewInfo view_info(config, 0); return slot_image_views.insert(runtime, info, view_info, config.Address()); @@ -1907,8 +1895,7 @@ ImageViewId TextureCache

::FindColorBuffer(size_t index) { if (rt.format == Tegra::RenderTargetFormat::NONE) { return ImageViewId{}; } - const ImageInfo info = - ClampedSampleCount(ImageInfo(regs.rt[index], regs.anti_alias_samples_mode)); + const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode); return FindRenderTargetView(info, gpu_addr); } @@ -1922,8 +1909,7 @@ ImageViewId TextureCache

::FindDepthBuffer() { if (gpu_addr == 0) { return ImageViewId{}; } - const ImageInfo info = - ClampedSampleCount(ImageInfo(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode)); + const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode); return FindRenderTargetView(info, gpu_addr); } diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index e75ce4e706..66f64ff8b3 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -416,8 +416,6 @@ private: bool ScaleDown(Image& image); u64 GetScaledImageSizeBytes(const ImageBase& image); - [[nodiscard]] ImageInfo ClampedSampleCount(ImageInfo info) const; - void QueueAsyncDecode(Image& image, ImageId image_id); void TickAsyncDecode(); From b9c28ef6a13673f237f32c7bf9b53bfff219be27 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 18 Aug 2026 16:25:31 -0400 Subject: [PATCH 27/72] [TEST] Return the previous geometry shader passthrough handling --- .../frontend/maxwell/translate_program.cpp | 61 +++++++----------- .../frontend/maxwell/translate_program.h | 6 +- .../renderer_opengl/gl_shader_cache.cpp | 62 ++++++++++--------- .../renderer_vulkan/vk_pipeline_cache.cpp | 62 ++++++++++--------- 4 files changed, 87 insertions(+), 104 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 728d405674..ebc5a825dd 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -169,36 +169,11 @@ std::map GenerateLegacyToGenericMappings( return mapping; } -struct PassthroughVertices { - u32 count; - u32 first; - u32 stride; -}; - -PassthroughVertices GetPassthroughVertices(InputTopology input_topology) { - switch (input_topology) { - case InputTopology::Points: - return {1, 0, 1}; - case InputTopology::Lines: - return {2, 0, 1}; - case InputTopology::LinesAdjacency: - return {2, 1, 1}; - case InputTopology::Triangles: - return {3, 0, 1}; - case InputTopology::TrianglesAdjacency: - return {3, 0, 2}; - } - return {3, 0, 1}; -} - void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const Shader::VaryingState& passthrough_mask, bool passthrough_position, - std::optional passthrough_layer_attr, - InputTopology input_topology) { - const PassthroughVertices vertices{GetPassthroughVertices(input_topology)}; - for (u32 vertex = 0; vertex < vertices.count; vertex++) { - const u32 i = vertices.first + vertex * vertices.stride; + std::optional passthrough_layer_attr) { + for (u32 i = 0; i < program.output_vertices; i++) { // Assign generics from input for (u32 j = 0; j < 32; j++) { if (!passthrough_mask.Generic(j)) { @@ -233,16 +208,25 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, ir.EndPrimitive(ir.Imm32(0)); } -void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info, - InputTopology input_topology) { +u32 GetOutputTopologyVertices(OutputTopology output_topology) { + switch (output_topology) { + case OutputTopology::PointList: + return 1; + case OutputTopology::LineStrip: + return 2; + default: + return 3; + } +} + +void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) { for (IR::Block* const block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { if (inst.GetOpcode() == IR::Opcode::Epilogue) { IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; EmitGeometryPassthrough( ir, program, program.info.passthrough, - program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}, - input_topology); + program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}); } } } @@ -251,8 +235,7 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf } // Anonymous namespace IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, - Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info, - InputTopology input_topology) { + Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { HostTranslateInfo normalized_host_info{host_info}; normalized_host_info.ApplyDescriptorLimitPolicy(); @@ -281,9 +264,8 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& inst_pool, ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology, - InputTopology input_topology) { + Shader::OutputTopology output_topology) { IR::Program program; program.stage = Stage::Geometry; program.output_topology = output_topology; - program.output_vertices = GetPassthroughVertices(input_topology).count; + program.output_vertices = GetOutputTopologyVertices(output_topology); program.is_geometry_passthrough = false; program.info.loads.mask = source_program.info.stores.mask; @@ -452,7 +433,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool& inst_pool, IR::IREmitter ir{*current_block}; EmitGeometryPassthrough(ir, program, program.info.stores, true, - source_program.info.emulated_layer, input_topology); + source_program.info.emulated_layer); IR::Block* return_block{block_pool.Create(inst_pool)}; IR::IREmitter{*return_block}.Epilogue(); diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 23c7ba1bbf..fa870258b3 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -21,8 +21,7 @@ namespace Shader::Maxwell { [[nodiscard]] IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, Environment& env, - Flow::CFG& cfg, const HostTranslateInfo& host_info, - InputTopology input_topology); + Flow::CFG& cfg, const HostTranslateInfo& host_info); [[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b); @@ -36,7 +35,6 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology, - InputTopology input_topology); + Shader::OutputTopology output_topology); } // namespace Shader::Maxwell diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 1218c8580c..cde03d5822 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -72,25 +72,6 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo } } -Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { - switch (topology) { - case Maxwell::PrimitiveTopology::Points: - return Shader::InputTopology::Points; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - return Shader::InputTopology::Lines; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - return Shader::InputTopology::LinesAdjacency; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - return Shader::InputTopology::TrianglesAdjacency; - default: - return Shader::InputTopology::Triangles; - } -} - Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -146,7 +127,33 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, default: break; } - info.input_topology = MaxwellToInputTopology(key.gs_input_topology); + switch (key.gs_input_topology) { + case Maxwell::PrimitiveTopology::Points: + info.input_topology = Shader::InputTopology::Points; + break; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + info.input_topology = Shader::InputTopology::Lines; + break; + case Maxwell::PrimitiveTopology::Triangles: + case Maxwell::PrimitiveTopology::TriangleStrip: + case Maxwell::PrimitiveTopology::TriangleFan: + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + case Maxwell::PrimitiveTopology::Patches: + info.input_topology = Shader::InputTopology::Triangles; + break; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + info.input_topology = Shader::InputTopology::LinesAdjacency; + break; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + info.input_topology = Shader::InputTopology::TrianglesAdjacency; + break; + } info.glasm_use_storage_buffers = glasm_use_storage_buffers; return info; } @@ -476,10 +483,8 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( && index == u32(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { auto topology = MaxwellToOutputTopology(key.gs_input_topology); - programs[index] = - GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology, - MaxwellToInputTopology(key.gs_input_topology)); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); continue; } if (key.unique_hashes[index] == 0) { @@ -497,15 +502,13 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.gs_input_topology)); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.gs_input_topology))}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -594,8 +597,7 @@ std::unique_ptr ShaderCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - Shader::InputTopology::Points)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; Shader::RuntimeInfo info; info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 697540981d..acb4219213 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -152,25 +152,6 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde return Shader::AttributeType::Disabled; } -Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { - switch (topology) { - case Maxwell::PrimitiveTopology::Points: - return Shader::InputTopology::Points; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - return Shader::InputTopology::Lines; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - return Shader::InputTopology::LinesAdjacency; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - return Shader::InputTopology::TrianglesAdjacency; - default: - return Shader::InputTopology::Triangles; - } -} - Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -289,7 +270,33 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program default: break; } - info.input_topology = MaxwellToInputTopology(key.state.topology); + switch (key.state.topology) { + case Maxwell::PrimitiveTopology::Points: + info.input_topology = Shader::InputTopology::Points; + break; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + info.input_topology = Shader::InputTopology::Lines; + break; + case Maxwell::PrimitiveTopology::Triangles: + case Maxwell::PrimitiveTopology::TriangleStrip: + case Maxwell::PrimitiveTopology::TriangleFan: + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + case Maxwell::PrimitiveTopology::Patches: + info.input_topology = Shader::InputTopology::Triangles; + break; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + info.input_topology = Shader::InputTopology::LinesAdjacency; + break; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + info.input_topology = Shader::InputTopology::TrianglesAdjacency; + break; + } info.force_early_z = key.state.early_z != 0; info.y_negate = key.state.y_negate != 0; return info; @@ -786,10 +793,8 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( index == static_cast(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { auto topology = MaxwellToOutputTopology(key.state.topology); - programs[index] = - GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology, - MaxwellToInputTopology(key.state.topology)); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); continue; } if (key.unique_hashes[index] == 0) { @@ -802,13 +807,11 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.state.topology)); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.state.topology))}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -954,8 +957,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - Shader::InputTopology::Points)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; const VkDriverIdKHR driver_id = device.GetDriverID(); const bool needs_shared_mem_clamp = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || From 1f0605c35d7118a140d30e3351397188367c4766 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 18 Aug 2026 20:07:28 -0400 Subject: [PATCH 28/72] [TEST] Remove the whole geometry shader changes --- src/video_core/renderer_vulkan/vk_scheduler.cpp | 3 +-- src/video_core/vulkan_common/vulkan_device.h | 15 --------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index f1f04d0b8c..36ca122daa 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -410,7 +410,6 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, - consumer_stages = device.AttachmentConsumerStages(), has_transform_feedback = device.IsExtTransformFeedbackSupported()]( vk::CommandBuffer cmdbuf) { static constexpr VkAccessFlags SHADER_ACCESS = @@ -459,7 +458,7 @@ void Scheduler::EndRenderPass() } cmdbuf.EndRenderPass(); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 02b4767095..6f8c3243b5 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -1023,21 +1023,6 @@ FN_MAX_LIMIT_LIST return features2.features.multiViewport; } - 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.geometryShader) { - stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; - } - return stages; - } - /// Returns true if the device supports VK_KHR_maintenance1. bool IsKhrMaintenance1Supported() const { return extensions.maintenance1; From 69cd5ee0d1fd19c286f26190b8cc3e22f53369a6 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 18 Aug 2026 20:51:29 -0400 Subject: [PATCH 29/72] [TEST] Improve the geometry shader handling --- .../frontend/maxwell/translate_program.cpp | 61 ++++++++++++------ .../frontend/maxwell/translate_program.h | 6 +- .../renderer_opengl/gl_shader_cache.cpp | 64 +++++++++---------- .../renderer_vulkan/vk_pipeline_cache.cpp | 64 +++++++++---------- .../renderer_vulkan/vk_rasterizer.cpp | 11 +++- .../renderer_vulkan/vk_scheduler.cpp | 3 +- src/video_core/vulkan_common/vulkan_device.h | 25 ++++++++ 7 files changed, 141 insertions(+), 93 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index ebc5a825dd..728d405674 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -169,11 +169,36 @@ std::map GenerateLegacyToGenericMappings( return mapping; } +struct PassthroughVertices { + u32 count; + u32 first; + u32 stride; +}; + +PassthroughVertices GetPassthroughVertices(InputTopology input_topology) { + switch (input_topology) { + case InputTopology::Points: + return {1, 0, 1}; + case InputTopology::Lines: + return {2, 0, 1}; + case InputTopology::LinesAdjacency: + return {2, 1, 1}; + case InputTopology::Triangles: + return {3, 0, 1}; + case InputTopology::TrianglesAdjacency: + return {3, 0, 2}; + } + return {3, 0, 1}; +} + void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const Shader::VaryingState& passthrough_mask, bool passthrough_position, - std::optional passthrough_layer_attr) { - for (u32 i = 0; i < program.output_vertices; i++) { + std::optional passthrough_layer_attr, + InputTopology input_topology) { + const PassthroughVertices vertices{GetPassthroughVertices(input_topology)}; + for (u32 vertex = 0; vertex < vertices.count; vertex++) { + const u32 i = vertices.first + vertex * vertices.stride; // Assign generics from input for (u32 j = 0; j < 32; j++) { if (!passthrough_mask.Generic(j)) { @@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, ir.EndPrimitive(ir.Imm32(0)); } -u32 GetOutputTopologyVertices(OutputTopology output_topology) { - switch (output_topology) { - case OutputTopology::PointList: - return 1; - case OutputTopology::LineStrip: - return 2; - default: - return 3; - } -} - -void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) { +void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info, + InputTopology input_topology) { for (IR::Block* const block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { if (inst.GetOpcode() == IR::Opcode::Epilogue) { IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; EmitGeometryPassthrough( ir, program, program.info.passthrough, - program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}); + program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}, + input_topology); } } } @@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf } // Anonymous namespace IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, - Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { + Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info, + InputTopology input_topology) { HostTranslateInfo normalized_host_info{host_info}; normalized_host_info.ApplyDescriptorLimitPolicy(); @@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& inst_pool, ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology) { + Shader::OutputTopology output_topology, + InputTopology input_topology) { IR::Program program; program.stage = Stage::Geometry; program.output_topology = output_topology; - program.output_vertices = GetOutputTopologyVertices(output_topology); + program.output_vertices = GetPassthroughVertices(input_topology).count; program.is_geometry_passthrough = false; program.info.loads.mask = source_program.info.stores.mask; @@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool& inst_pool, IR::IREmitter ir{*current_block}; EmitGeometryPassthrough(ir, program, program.info.stores, true, - source_program.info.emulated_layer); + source_program.info.emulated_layer, input_topology); IR::Block* return_block{block_pool.Create(inst_pool)}; IR::IREmitter{*return_block}.Epilogue(); diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index fa870258b3..23c7ba1bbf 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -21,7 +21,8 @@ namespace Shader::Maxwell { [[nodiscard]] IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, Environment& env, - Flow::CFG& cfg, const HostTranslateInfo& host_info); + Flow::CFG& cfg, const HostTranslateInfo& host_info, + InputTopology input_topology); [[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b); @@ -35,6 +36,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology); + Shader::OutputTopology output_topology, + InputTopology input_topology); } // namespace Shader::Maxwell diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index cde03d5822..1377ac4674 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -54,7 +54,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 15; +constexpr u32 CACHE_VERSION = 16; template auto MakeSpan(Container& container) { @@ -72,6 +72,25 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo } } +Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::InputTopology::Points; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::InputTopology::Lines; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Shader::InputTopology::LinesAdjacency; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Shader::InputTopology::TrianglesAdjacency; + default: + return Shader::InputTopology::Triangles; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -127,33 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, default: break; } - switch (key.gs_input_topology) { - case Maxwell::PrimitiveTopology::Points: - info.input_topology = Shader::InputTopology::Points; - break; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - info.input_topology = Shader::InputTopology::Lines; - break; - case Maxwell::PrimitiveTopology::Triangles: - case Maxwell::PrimitiveTopology::TriangleStrip: - case Maxwell::PrimitiveTopology::TriangleFan: - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - case Maxwell::PrimitiveTopology::Polygon: - case Maxwell::PrimitiveTopology::Patches: - info.input_topology = Shader::InputTopology::Triangles; - break; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - info.input_topology = Shader::InputTopology::LinesAdjacency; - break; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - info.input_topology = Shader::InputTopology::TrianglesAdjacency; - break; - } + info.input_topology = MaxwellToInputTopology(key.gs_input_topology); info.glasm_use_storage_buffers = glasm_use_storage_buffers; return info; } @@ -483,8 +476,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( && index == u32(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { auto topology = MaxwellToOutputTopology(key.gs_input_topology); - programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology); + programs[index] = + GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology, + MaxwellToInputTopology(key.gs_input_topology)); continue; } if (key.unique_hashes[index] == 0) { @@ -502,13 +497,15 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.gs_input_topology)); total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.gs_input_topology))}; total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -597,7 +594,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + Shader::InputTopology::Points)}; const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; Shader::RuntimeInfo info; info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index acb4219213..ed18587a15 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -62,7 +62,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 18; +constexpr u32 CACHE_VERSION = 19; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -152,6 +152,25 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde return Shader::AttributeType::Disabled; } +Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::InputTopology::Points; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::InputTopology::Lines; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Shader::InputTopology::LinesAdjacency; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Shader::InputTopology::TrianglesAdjacency; + default: + return Shader::InputTopology::Triangles; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -270,33 +289,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program default: break; } - switch (key.state.topology) { - case Maxwell::PrimitiveTopology::Points: - info.input_topology = Shader::InputTopology::Points; - break; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - info.input_topology = Shader::InputTopology::Lines; - break; - case Maxwell::PrimitiveTopology::Triangles: - case Maxwell::PrimitiveTopology::TriangleStrip: - case Maxwell::PrimitiveTopology::TriangleFan: - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - case Maxwell::PrimitiveTopology::Polygon: - case Maxwell::PrimitiveTopology::Patches: - info.input_topology = Shader::InputTopology::Triangles; - break; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - info.input_topology = Shader::InputTopology::LinesAdjacency; - break; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - info.input_topology = Shader::InputTopology::TrianglesAdjacency; - break; - } + info.input_topology = MaxwellToInputTopology(key.state.topology); info.force_early_z = key.state.early_z != 0; info.y_negate = key.state.y_negate != 0; return info; @@ -793,8 +786,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( index == static_cast(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { auto topology = MaxwellToOutputTopology(key.state.topology); - programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology); + programs[index] = + GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology, + MaxwellToInputTopology(key.state.topology)); continue; } if (key.unique_hashes[index] == 0) { @@ -807,11 +802,13 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.state.topology)); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.state.topology))}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -957,7 +954,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + Shader::InputTopology::Points)}; const VkDriverIdKHR driver_id = device.GetDriverID(); const bool needs_shared_mem_clamp = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 10522958cf..05535370b3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -850,10 +850,15 @@ 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_TESSELLATION_CONTROL_SHADER_BIT | - VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | - VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_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; + } + if (device.SupportsGeometryShader()) { + flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + } if (device.IsExtTransformFeedbackSupported()) { flags |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 36ca122daa..f1f04d0b8c 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -410,6 +410,7 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, + consumer_stages = device.AttachmentConsumerStages(), has_transform_feedback = device.IsExtTransformFeedbackSupported()]( vk::CommandBuffer cmdbuf) { static constexpr VkAccessFlags SHADER_ACCESS = @@ -458,7 +459,7 @@ void Scheduler::EndRenderPass() } cmdbuf.EndRenderPass(); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 6f8c3243b5..6f27e74e28 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -1023,6 +1023,31 @@ 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_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; + } + return stages; + } + /// Returns true if the device supports VK_KHR_maintenance1. bool IsKhrMaintenance1Supported() const { return extensions.maintenance1; From 2e52ca57539aa91031e97d93cdb2428768edc451 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 18 Aug 2026 21:22:25 -0400 Subject: [PATCH 30/72] [TEST] Corroborating the dowload/copy on MSAA path --- src/video_core/texture_cache/image_base.cpp | 9 ++++++++- src/video_core/texture_cache/image_base.h | 2 ++ src/video_core/texture_cache/texture_cache.h | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index 077df28fb3..dcedaf69f0 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp @@ -121,7 +121,7 @@ void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_vie image_view_ids.push_back(image_view_id); } -bool ImageBase::IsSafeDownload() const noexcept { +bool ImageBase::IsSafeGpuCopy() const noexcept { // Skip images that were not modified from the GPU if (False(flags & ImageFlagBits::GpuModified)) { return false; @@ -131,6 +131,13 @@ bool ImageBase::IsSafeDownload() const noexcept { if (True(flags & ImageFlagBits::CpuModified)) { return false; } + return true; +} + +bool ImageBase::IsSafeDownload() const noexcept { + if (!IsSafeGpuCopy()) { + return false; + } if (info.num_samples > 1) { LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented"); return false; diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 0587d7b724..e85e91fd78 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -64,6 +64,8 @@ struct ImageBase { void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id); + [[nodiscard]] bool IsSafeGpuCopy() const noexcept; + [[nodiscard]] bool IsSafeDownload() const noexcept; [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 2eb94275a8..6e0dd6c319 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1691,7 +1691,7 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA for (const auto& copy_object : join_copies_to_do) { Image& overlap = slot_images[copy_object.id]; if (copy_object.is_alias) { - if (!overlap.IsSafeDownload()) { + if (!overlap.IsSafeGpuCopy()) { continue; } const auto alias_pointer = join_alias_indices.find(copy_object.id); From 2f833997983022a4edada4a5b46e0255ee0c866e Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 18 Aug 2026 22:35:04 -0400 Subject: [PATCH 31/72] [TEST] Removal of IsSafeDownload death path --- .../renderer_opengl/gl_texture_cache.h | 1 + .../renderer_vulkan/vk_texture_cache.cpp | 18 +++++++++++++--- .../renderer_vulkan/vk_texture_cache.h | 3 +++ src/video_core/texture_cache/image_base.cpp | 13 +----------- src/video_core/texture_cache/image_base.h | 5 +++-- src/video_core/texture_cache/texture_cache.h | 21 ++++++++++++++++--- .../texture_cache/texture_cache_base.h | 2 ++ 7 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index dfcef4b0b6..5da06668de 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -370,6 +370,7 @@ struct TextureCacheParams { static constexpr bool HAS_EMULATED_COPIES = true; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; + static constexpr bool HAS_MSAA_DOWNLOADS = false; using Runtime = OpenGL::TextureCacheRuntime; using Image = OpenGL::Image; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index c88d3c85e4..b12fab0482 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1765,6 +1765,16 @@ u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { return device.GetDeviceMemoryUsage(); } +bool TextureCacheRuntime::CanDownloadMsaa(const VideoCommon::ImageInfo& info) const { + if (ImageAspectMask(info.format) != VK_IMAGE_ASPECT_COLOR_BIT) { + return false; + } + if (VideoCore::Surface::IsPixelFormatInteger(info.format)) { + return false; + } + return info.resources.layers == 1; +} + bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } @@ -2012,8 +2022,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o } if (info.num_samples > 1) { - if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT && - !VideoCore::Surface::IsPixelFormatInteger(info.format)) { + if (runtime->CanDownloadMsaa(info)) { ImageInfo temp_info = info; temp_info.num_samples = 1; @@ -2133,8 +2142,11 @@ void Image::DownloadMemory(std::span buffers_span, std::span o }); runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image)); - return; } + if (is_rescaled) { + ScaleUp(true); + } + return; } else { boost::container::small_vector buffers_vector{}; boost::container::small_vector, 8> diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 073b852663..40088c4b74 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -66,6 +66,8 @@ public: bool CanReportMemoryUsage() const; + bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const; + void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, @@ -585,6 +587,7 @@ struct TextureCacheParams { static constexpr bool HAS_EMULATED_COPIES = false; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; + static constexpr bool HAS_MSAA_DOWNLOADS = true; using Runtime = Vulkan::TextureCacheRuntime; using Image = Vulkan::Image; diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index dcedaf69f0..9b0f33220d 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -134,17 +134,6 @@ bool ImageBase::IsSafeGpuCopy() const noexcept { return true; } -bool ImageBase::IsSafeDownload() const noexcept { - if (!IsSafeGpuCopy()) { - return false; - } - if (info.num_samples > 1) { - LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented"); - return false; - } - return true; -} - void ImageBase::CheckBadOverlapState() { if (False(flags & ImageFlagBits::BadOverlap)) { return; diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index e85e91fd78..79fc8505d6 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -66,8 +69,6 @@ struct ImageBase { [[nodiscard]] bool IsSafeGpuCopy() const noexcept; - [[nodiscard]] bool IsSafeDownload() const noexcept; - [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { const VAddr overlap_end = overlap_cpu_addr + overlap_size; return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 6e0dd6c319..de56ba52ed 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -134,7 +134,7 @@ void TextureCache

::RunGarbageCollector() { if (True(image.flags & ImageFlagBits::IsDecoding)) { return false; } - const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap); + const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap); if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) { return false; } @@ -594,11 +594,26 @@ void TextureCache

::WriteMemory(DAddr cpu_addr, size_t size) { }); } +template +bool TextureCache

::IsDownloadable(const ImageBase& image) const noexcept { + if (!image.IsSafeGpuCopy()) { + return false; + } + if (image.info.num_samples == 1) { + return true; + } + if constexpr (P::HAS_MSAA_DOWNLOADS) { + return runtime.CanDownloadMsaa(image.info); + } else { + return false; + } +} + template void TextureCache

::DownloadMemory(DAddr cpu_addr, size_t size) { boost::container::small_vector images; - ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { - if (!image.IsSafeDownload()) { + ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { + if (!IsDownloadable(image)) { return; } image.flags &= ~ImageFlagBits::GpuModified; diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 66f64ff8b3..d143c1ef72 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -303,6 +303,8 @@ private: FramebufferId GetFramebufferId(const RenderTargets& key); /// Refresh the contents (pixel data) of an image + [[nodiscard]] bool IsDownloadable(const ImageBase& image) const noexcept; + void RefreshContents(Image& image, ImageId image_id); /// Upload data from guest to an image From 842b5e0b3ab99b50117269923f25305d6aa7a677 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 18 Aug 2026 23:03:37 -0400 Subject: [PATCH 32/72] [TEST] New MSAA shader for UINT/SINT color --- src/video_core/host_shaders/CMakeLists.txt | 4 + .../convert_msaa_to_non_msaa_sint.frag | 22 + .../convert_msaa_to_non_msaa_uint.frag | 22 + .../convert_non_msaa_to_msaa_sint.frag | 21 + .../convert_non_msaa_to_msaa_uint.frag | 21 + src/video_core/renderer_vulkan/blit_image.cpp | 574 ++++++++++-------- src/video_core/renderer_vulkan/blit_image.h | 11 + .../renderer_vulkan/vk_texture_cache.cpp | 11 +- 8 files changed, 408 insertions(+), 278 deletions(-) create mode 100644 src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag create mode 100644 src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag create mode 100644 src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag create mode 100644 src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 35d092c3bc..4ee2c86418 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -33,8 +33,12 @@ 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_sint.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_uint.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_sint.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_uint.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag new file mode 100644 index 0000000000..fedd064cb0 --- /dev/null +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform isampler2DMS msaa_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +layout(location = 0) out ivec4 frag_color; + +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; + frag_color = texelFetch(msaa_in, msaa_coord, sample_id); +} diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag new file mode 100644 index 0000000000..ec692752a0 --- /dev/null +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform usampler2DMS msaa_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +layout(location = 0) out uvec4 frag_color; + +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; + frag_color = texelFetch(msaa_in, msaa_coord, sample_id); +} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag new file mode 100644 index 0000000000..491c1bf5cc --- /dev/null +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform isampler2D img_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +layout(location = 0) out ivec4 frag_color; + +void main() { + const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; + const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); + const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; + frag_color = texelFetch(img_in, coord, 0); +} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag new file mode 100644 index 0000000000..08eb0cc843 --- /dev/null +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform usampler2D img_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +layout(location = 0) out uvec4 frag_color; + +void main() { + const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; + const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); + const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; + frag_color = texelFetch(img_in, coord, 0); +} diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 482c5d7255..b6d395ebc4 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -22,7 +22,11 @@ #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_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" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_sint_frag_spv.h" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_uint_frag_spv.h" #include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h" #include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h" #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" @@ -521,8 +525,18 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) } } +[[nodiscard]] MSAACopyFormatClass FormatClass(VideoCore::Surface::PixelFormat format) { + if (!VideoCore::Surface::IsPixelFormatInteger(format)) { + return MSAACopyFormatClass::Float; + } + if (VideoCore::Surface::IsPixelFormatSignedInteger(format)) { + return MSAACopyFormatClass::SignedInteger; + } + return MSAACopyFormatClass::UnsignedInteger; +} + [[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image, - VkFormat format, u32 base_level, + VkFormat format, u32 base_level, u32 base_layer, VkImageAspectFlags aspect_mask) { return device.CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -541,7 +555,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) .aspectMask = aspect_mask, .baseMipLevel = base_level, .levelCount = 1, - .baseArrayLayer = 0, + .baseArrayLayer = base_layer, .layerCount = 1, }, }); @@ -618,7 +632,15 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, 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_msaa_to_non_msaa_sint_frag( + 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_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)), + convert_non_msaa_to_msaa_uint_frag( + BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_UINT_FRAG_SPV)), convert_non_msaa_to_msaa_depth_frag( BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)), convert_non_msaa_to_msaa_depth_stencil_frag( @@ -982,6 +1004,7 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .renderpass = renderpass, .samples = samples, .msaa_to_non_msaa = msaa_to_non_msaa, + .format_class = FormatClass(dst_format), }; const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key); const VkPipelineLayout layout = *msaa_copy_pipeline_layout; @@ -991,140 +1014,142 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i 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(copy.src_subresource.base_level), - VK_IMAGE_ASPECT_COLOR_BIT); - vk::ImageView dst_view = - MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, - static_cast(copy.dst_subresource.base_level), - VK_IMAGE_ASPECT_COLOR_BIT); - 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(dst_offset.x) + dst_extent.width, - .height = static_cast(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 s32 num_layers = (std::min)(copy.src_subresource.num_layers, + copy.dst_subresource.num_layers); + for (s32 layer = 0; layer < num_layers; ++layer) { + vk::ImageView src_view = + MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, + static_cast(copy.src_subresource.base_level), + static_cast(copy.src_subresource.base_layer + layer), + VK_IMAGE_ASPECT_COLOR_BIT); + vk::ImageView dst_view = + MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, + static_cast(copy.dst_subresource.base_level), + static_cast(copy.dst_subresource.base_layer + layer), + VK_IMAGE_ASPECT_COLOR_BIT); + 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, }; - const std::array pre_barriers{ - VkImageMemoryBarrier{ + 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(dst_offset.x) + dst_extent.width, + .height = static_cast(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(render_area.offset.x), + .y = static_cast(render_area.offset.y), + .width = static_cast(render_area.extent.width), + .height = static_cast(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 | - 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, + .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_COMPUTE_SHADER_BIT | + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_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(render_area.offset.x), - .y = static_cast(render_area.offset.y), - .width = static_cast(render_area.extent.width), - .height = static_cast(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), - }); + 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(); } @@ -1598,6 +1623,7 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage .renderpass = renderpass, .samples = samples, .msaa_to_non_msaa = false, + .format_class = MSAACopyFormatClass::Float, }; const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil); const VkPipelineLayout layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout @@ -1613,158 +1639,161 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; } 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(copy.src_subresource.base_level), - VK_IMAGE_ASPECT_DEPTH_BIT); - vk::ImageView src_stencil_view = - copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, - static_cast(copy.src_subresource.base_level), - VK_IMAGE_ASPECT_STENCIL_BIT) - : vk::ImageView{}; - vk::ImageView dst_view = - MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, - static_cast(copy.dst_subresource.base_level), - attachment_aspect); - 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(dst_offset.x) + dst_extent.width, - .height = static_cast(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(); - const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE; - scheduler.Record([this, pipeline, layout, sampler, renderpass, - framebuffer_handle = *framebuffer, src_view_handle = *src_view, - src_stencil_handle, src = src_image, dst = dst_image, render_area, - attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) { - const VkImageSubresourceRange src_range{ - .aspectMask = attachment_aspect, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, + const s32 num_layers = (std::min)(copy.src_subresource.num_layers, + copy.dst_subresource.num_layers); + for (s32 layer = 0; layer < num_layers; ++layer) { + vk::ImageView src_view = + MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, + static_cast(copy.src_subresource.base_level), + static_cast(copy.src_subresource.base_layer + layer), + VK_IMAGE_ASPECT_DEPTH_BIT); + vk::ImageView src_stencil_view = + copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, + static_cast(copy.src_subresource.base_level), + static_cast(copy.src_subresource.base_layer + layer), + VK_IMAGE_ASPECT_STENCIL_BIT) + : vk::ImageView{}; + vk::ImageView dst_view = + MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, + static_cast(copy.dst_subresource.base_level), + static_cast(copy.dst_subresource.base_layer + layer), + attachment_aspect); + 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, }; - const std::array pre_barriers{ - VkImageMemoryBarrier{ + 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(dst_offset.x) + dst_extent.width, + .height = static_cast(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(); + const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE; + scheduler.Record([this, pipeline, layout, sampler, renderpass, + framebuffer_handle = *framebuffer, src_view_handle = *src_view, + src_stencil_handle, src = src_image, dst = dst_image, render_area, + attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) { + const VkImageSubresourceRange src_range{ + .aspectMask = attachment_aspect, + .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_DEPTH_STENCIL_ATTACHMENT_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 = src_range, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_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 = src_range, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_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 = + src_stencil_handle != VK_NULL_HANDLE + ? two_textures_descriptor_allocator.Commit() + : one_texture_descriptor_allocator.Commit(); + if (src_stencil_handle != VK_NULL_HANDLE) { + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle, + src_stencil_handle); + } else { + 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(render_area.offset.x), + .y = static_cast(render_area.offset.y), + .width = static_cast(render_area.extent.width), + .height = static_cast(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_DEPTH_STENCIL_ATTACHMENT_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 = src_range, - }, - VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_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 = src_range, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_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 = - src_stencil_handle != VK_NULL_HANDLE - ? two_textures_descriptor_allocator.Commit() - : one_texture_descriptor_allocator.Commit(); - if (src_stencil_handle != VK_NULL_HANDLE) { - UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle, - src_stencil_handle); - } else { - 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(render_area.offset.x), - .y = static_cast(render_area.offset.y), - .width = static_cast(render_area.extent.width), - .height = static_cast(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_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_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 = src_range, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 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), - }); - if (copy_stencil) { + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, post_barrier); + }); msaa_copy_resources.push_back(MSAACopyResources{ .tick = scheduler.CurrentTick(), - .src_view = std::move(src_stencil_view), - .dst_view = vk::ImageView{}, - .framebuffer = vk::Framebuffer{}, + .src_view = std::move(src_view), + .dst_view = std::move(dst_view), + .framebuffer = std::move(framebuffer), }); + if (copy_stencil) { + msaa_copy_resources.push_back(MSAACopyResources{ + .tick = scheduler.CurrentTick(), + .src_view = std::move(src_stencil_view), + .dst_view = vk::ImageView{}, + .framebuffer = vk::Framebuffer{}, + }); + } } } } @@ -1775,9 +1804,16 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)]; } 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); + VkShaderModule frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_frag + : *convert_non_msaa_to_msaa_frag; + if (key.format_class == MSAACopyFormatClass::SignedInteger) { + frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_sint_frag + : *convert_non_msaa_to_msaa_sint_frag; + } else if (key.format_class == MSAACopyFormatClass::UnsignedInteger) { + frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_uint_frag + : *convert_non_msaa_to_msaa_uint_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, diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 3b073f2825..66a70c6215 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -45,12 +45,19 @@ struct BlitDepthStencilPipelineKey { u32 stencil_ref; }; +enum class MSAACopyFormatClass : u32 { + Float, + SignedInteger, + UnsignedInteger, +}; + struct MSAACopyPipelineKey { constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default; VkRenderPass renderpass; VkSampleCountFlagBits samples; bool msaa_to_non_msaa; + MSAACopyFormatClass format_class; }; struct BlitMSAAPipelineKey { @@ -198,7 +205,11 @@ private: 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_msaa_to_non_msaa_sint_frag; + vk::ShaderModule convert_msaa_to_non_msaa_uint_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; vk::ShaderModule convert_non_msaa_to_msaa_depth_frag; vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag; vk::Sampler linear_sampler; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index b12fab0482..796039be38 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1766,13 +1766,7 @@ u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { } bool TextureCacheRuntime::CanDownloadMsaa(const VideoCommon::ImageInfo& info) const { - if (ImageAspectMask(info.format) != VK_IMAGE_ASPECT_COLOR_BIT) { - return false; - } - if (VideoCore::Surface::IsPixelFormatInteger(info.format)) { - return false; - } - return info.resources.layers == 1; + return ImageAspectMask(info.format) == VK_IMAGE_ASPECT_COLOR_BIT; } bool TextureCacheRuntime::CanReportMemoryUsage() const { @@ -1905,8 +1899,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, const bool msaa_upload_is_depth = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; const bool wants_msaa_upload = info.num_samples > 1 - && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth) - && !VideoCore::Surface::IsPixelFormatInteger(info.format); + && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth); if (wants_msaa_upload) { const bool msaa_upload_copies_stencil = From 2a1b9278b98d00e33d8885bf2cb1220fd044ae07 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 00:41:14 -0400 Subject: [PATCH 33/72] [TEST] MSAA depth stencil discard again --- .../renderer_vulkan/vk_render_pass_cache.cpp | 5 ++++- .../renderer_vulkan/vk_render_pass_cache.h | 4 +++- src/video_core/renderer_vulkan/vk_scheduler.cpp | 4 +++- src/video_core/renderer_vulkan/vk_texture_cache.cpp | 13 ++++++++++--- src/video_core/renderer_vulkan/vk_texture_cache.h | 8 +++++++- 5 files changed, 27 insertions(+), 7 deletions(-) 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 efb37bc787..a514ac9022 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -175,8 +175,11 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard + ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE; descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples, - depth_load_op, VK_ATTACHMENT_STORE_OP_STORE)); + depth_load_op, depth_store_op)); } std::array resolve_references{}; const bool do_resolve_color = 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 0b01fc0194..f9923be75a 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -26,6 +26,7 @@ struct RenderPassKey { u32 color_clear_mask; bool depth_stencil_clear; u32 color_discard_mask; + bool depth_stencil_discard; }; } // namespace Vulkan @@ -49,7 +50,8 @@ struct hash { (static_cast(key.color_discard_mask) << 24) | (static_cast(key.resolve_color) << 32) | (static_cast(key.depth_stencil_clear) << 33) | - (static_cast(key.resolve_depth_stencil) << 34); + (static_cast(key.resolve_depth_stencil) << 34) | + (static_cast(key.depth_stencil_discard) << 35); size_t seed = 0; Common::HashCombine(seed, formats); Common::HashCombine(seed, state); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index f1f04d0b8c..68852a0e82 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -155,8 +155,10 @@ void Scheduler::RealizeDeferredClear() { } const u32 color_discard_mask = dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u; + const bool depth_stencil_discard = + dc.depth_stencil && dc.framebuffer->DiscardsMsaaDepthStencil(); const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant( - dc.color_clear_mask, dc.depth_stencil, color_discard_mask); + dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard); EndRenderPass(); BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 796039be38..432117ab0f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -55,6 +55,7 @@ using VideoCore::Surface::SurfaceType; namespace { constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; +constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true; [[nodiscard]] constexpr bool NeedsExplicitBorderColorFormat(VkFormat format) { switch (format) { @@ -3000,6 +3001,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, discard_msaa_color = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; + discard_msaa_depth_stencil = ENABLE_MSAA_RESOLVE_CONSUME && + ENABLE_MSAA_DEPTH_STENCIL_DISCARD && do_resolve_depth_stencil; renderpass = runtime.render_pass_cache.Get(renderpass_key); render_pass_key = renderpass_key; @@ -3089,13 +3092,16 @@ 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) { + u32 color_discard_mask, + bool depth_stencil_discard) const { + if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0 && + !depth_stencil_discard) { return renderpass; } static_assert(NUM_RT <= 8); const u32 variant_key = color_clear_mask | (color_discard_mask << 8) | - (static_cast(depth_stencil_clear) << 16); + (static_cast(depth_stencil_clear) << 16) | + (static_cast(depth_stencil_discard) << 17); for (u32 index = 0; index < num_memoized_variants; ++index) { if (variant_keys[index] == variant_key) { return variant_render_passes[index]; @@ -3105,6 +3111,7 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste key.color_clear_mask = color_clear_mask; key.depth_stencil_clear = depth_stencil_clear; key.color_discard_mask = color_discard_mask; + key.depth_stencil_discard = depth_stencil_discard; const VkRenderPass variant = render_pass_cache->Get(key); if (num_memoized_variants < variant_keys.size()) { variant_keys[num_memoized_variants] = variant_key; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 40088c4b74..681866d7e3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -196,7 +196,8 @@ public: } [[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, - u32 color_discard_mask) const; + u32 color_discard_mask, + bool depth_stencil_discard) const; [[nodiscard]] VkExtent2D RenderArea() const noexcept { return render_area; @@ -250,6 +251,10 @@ public: return discard_msaa_color; } + [[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept { + return discard_msaa_depth_stencil; + } + private: static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8; @@ -270,6 +275,7 @@ private: RenderPassKey render_pass_key{}; RenderPassCache* render_pass_cache{nullptr}; bool discard_msaa_color{}; + bool discard_msaa_depth_stencil{}; mutable std::array variant_keys{}; mutable std::array variant_render_passes{}; mutable u32 num_memoized_variants{}; From 3ce788b09c75d7cb349257d36998d4623d01a78a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 01:55:02 -0400 Subject: [PATCH 34/72] Forcing off rescale hack --- .../yuzu_emu/features/settings/model/BooleanSetting.kt | 1 - .../yuzu_emu/features/settings/model/view/SettingsItem.kt | 7 ------- src/android/app/src/main/jni/android_config.cpp | 1 + 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 39a76a8608..6d47e4d69d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -18,7 +18,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"), FIX_BLOOM_EFFECTS("fix_bloom_effects"), EMULATE_BGR565("emulate_bgr565"), - RESCALE_HACK("rescale_hack"), CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"), USE_DOCKED_MODE("use_docked_mode"), USE_AUTO_STUB("use_auto_stub"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index c05539c306..45e2cd6ad8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -877,13 +877,6 @@ abstract class SettingsItem( descriptionId = R.string.fix_bloom_effects_description ) ) - put( - SwitchSetting( - BooleanSetting.RESCALE_HACK, - titleId = R.string.rescale_hack, - descriptionId = R.string.rescale_hack_description - ) - ) put( SwitchSetting( BooleanSetting.EMULATE_BGR565, diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp index 6548667de4..febebd57a4 100644 --- a/src/android/app/src/main/jni/android_config.cpp +++ b/src/android/app/src/main/jni/android_config.cpp @@ -33,6 +33,7 @@ void AndroidConfig::SaveAllValues() { } void AndroidConfig::ReadAndroidValues() { + Settings::values.rescale_hack.SetValue(false); ReadAndroidUIValues(); if (global) { ReadUIValues(); From ff3de66bab5c2d0b678bb1471f857074bee5b881 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 02:33:19 -0400 Subject: [PATCH 35/72] Quick adjustment on deferred clears order --- .../renderer_vulkan/vk_rasterizer.cpp | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 05535370b3..29fe7db424 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -424,24 +424,12 @@ void RasterizerVulkan::Clear(u32 layer_count) { 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); u32 up_scale = 1; u32 down_shift = 0; if (texture_cache.IsRescaling()) { up_scale = Settings::values.resolution_info.up_scale; down_shift = Settings::values.resolution_info.down_shift; } - UpdateViewportsState(regs); - VkRect2D default_scissor{}; default_scissor.offset.x = 0; default_scissor.offset.y = 0; @@ -497,6 +485,22 @@ void RasterizerVulkan::Clear(u32 layer_count) { return; } + const bool clear_covers_render_area = + clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 && + clear_rect.rect.extent.width >= render_area.width && + clear_rect.rect.extent.height >= render_area.height; + const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) && + 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); + UpdateViewportsState(regs); + const u32 color_attachment = regs.clear_surface.RT; if (use_color && framebuffer->HasAspectColorBit(color_attachment)) { const auto format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format); From cb04b133ea2649b305fbd7c412b99818d651a0a8 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 02:54:25 -0400 Subject: [PATCH 36/72] [TEST] Ported fix from tiled-gpu-v2 for ZBCTable --- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 87 +++++++++++-------- .../service/nvdrv/devices/nvhost_ctrl_gpu.h | 12 ++- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 14cdd8fda4..b5a4e6a670 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/assert.h" #include "common/logging.h" @@ -264,7 +265,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) { } NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { - if (params.type > supported_types) { + if (params.type == 0 || params.type > supported_types) { LOG_ERROR(Service_NVDRV, "ZBCSetTable: invalid type {:#x}", params.type); return NvResult::BadParameter; } @@ -279,42 +280,61 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { color_entry.format = params.format; color_entry.ref_cnt = 1u; - auto color_it = std::ranges::find_if(zbc_colors, - [&](const ZbcColorEntry& color_in_question) { - return color_entry.format == color_in_question.format && - color_entry.color_ds == color_in_question.color_ds && - color_entry.color_l2 == color_in_question.color_l2; - }); + const auto color_end = zbc_colors.begin() + zbc_used_color_entries; + auto color_it = std::find_if(zbc_colors.begin(), color_end, + [&](const ZbcColorEntry& color_in_question) { + return color_entry.format == color_in_question.format && + color_entry.color_ds == color_in_question.color_ds && + color_entry.color_l2 == color_in_question.color_l2; + }); - if (color_it != zbc_colors.end()) { + if (color_it != color_end) { ++color_it->ref_cnt; LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused color entry fmt={:#x}, ref_cnt={:#x}", params.format, color_it->ref_cnt); - } else { - zbc_colors.push_back(color_entry); - LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added color entry fmt={:#x}, index={:#x}", - params.format, zbc_colors.size() - 1); + break; } + + if (zbc_used_color_entries >= zbc_table_size) { + LOG_WARNING(Service_NVDRV, "ZBCSetTable: color table is full, fmt={:#x}", + params.format); + return NvResult::InsufficientMemory; + } + + zbc_colors[zbc_used_color_entries] = color_entry; + LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added color entry fmt={:#x}, index={:#x}", + params.format, zbc_used_color_entries); + ++zbc_used_color_entries; break; } case ZBCTypes::depth: { ZbcDepthEntry depth_entry{params.depth, params.format, 1u}; - auto depth_it = std::ranges::find_if(zbc_depths, - [&](const ZbcDepthEntry& depth_entry_in_question) { - return depth_entry.format == depth_entry_in_question.format && - depth_entry.depth == depth_entry_in_question.depth; - }); + const auto depth_end = zbc_depths.begin() + zbc_used_depth_entries; + auto depth_it = std::find_if(zbc_depths.begin(), depth_end, + [&](const ZbcDepthEntry& depth_entry_in_question) { + return depth_entry.format == depth_entry_in_question.format && + depth_entry.depth == depth_entry_in_question.depth; + }); - if (depth_it != zbc_depths.end()) { + if (depth_it != depth_end) { ++depth_it->ref_cnt; LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused depth entry fmt={:#x}, ref_cnt={:#x}", depth_entry.format, depth_it->ref_cnt); - } else { - zbc_depths.push_back(depth_entry); - LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added depth entry fmt={:#x}, index={:#x}", - depth_entry.format, zbc_depths.size() - 1); + break; } + + if (zbc_used_depth_entries >= zbc_table_size) { + LOG_WARNING(Service_NVDRV, "ZBCSetTable: depth table is full, fmt={:#x}", + depth_entry.format); + return NvResult::InsufficientMemory; + } + + zbc_depths[zbc_used_depth_entries] = depth_entry; + LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added depth entry fmt={:#x}, index={:#x}", + depth_entry.format, zbc_used_depth_entries); + ++zbc_used_depth_entries; + break; } } @@ -329,35 +349,34 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) { std::scoped_lock lk(zbc_mutex); + if (params.type == 0) { + params.index_size = zbc_table_size; + return NvResult::Success; + } + + if (params.index_size >= zbc_table_size) { + LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid index {:#x}", params.index_size); + return NvResult::BadParameter; + } + switch (static_cast(params.type)) { case ZBCTypes::color: { - if (params.index_size >= zbc_colors.size()) { - LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid color index {:#x}", params.index_size); - return NvResult::BadParameter; - } - const auto& colors = zbc_colors[params.index_size]; std::copy_n(colors.color_ds.begin(), colors.color_ds.size(), std::begin(params.color_ds)); std::copy_n(colors.color_l2.begin(), colors.color_l2.size(), std::begin(params.color_l2)); params.depth = 0; params.ref_cnt = colors.ref_cnt; params.format = colors.format; - params.index_size = static_cast(zbc_colors.size()); break; } case ZBCTypes::depth: { - if (params.index_size >= zbc_depths.size()) { - LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid depth index {:#x}", params.index_size); - return NvResult::BadParameter; - } - const auto& depth_entry = zbc_depths[params.index_size]; std::fill(std::begin(params.color_ds), std::end(params.color_ds), 0); std::fill(std::begin(params.color_l2), std::end(params.color_l2), 0); params.depth = depth_entry.depth; params.ref_cnt = depth_entry.ref_cnt; params.format = depth_entry.format; - params.index_size = static_cast(zbc_depths.size()); + break; } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 80b7abd6a1..ede658c50f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include "common/common_funcs.h" #include "common/common_types.h" @@ -212,10 +212,14 @@ private: Kernel::KEvent* unknown_event; // ZBC Tables + static constexpr u32 zbc_table_size = 15u; + static constexpr u32 supported_types = 2u; + std::mutex zbc_mutex{}; - std::vector zbc_colors{}; - std::vector zbc_depths{}; - const u32 supported_types = 2u; + std::array zbc_colors{}; + std::array zbc_depths{}; + u32 zbc_used_color_entries{}; + u32 zbc_used_depth_entries{}; }; } // namespace Service::Nvidia::Devices From 945d5c373ba4e179361ef7685324ecb419161649 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 03:13:47 -0400 Subject: [PATCH 37/72] [TEST] Added another MSAA depth stencil shaders to convert into non msaa + sanitize MSAA UINT/SINT path --- src/video_core/host_shaders/CMakeLists.txt | 2 + .../convert_msaa_to_non_msaa_depth.frag | 20 +++++ ...onvert_msaa_to_non_msaa_depth_stencil.frag | 23 ++++++ src/video_core/renderer_vulkan/blit_image.cpp | 29 +++++-- src/video_core/renderer_vulkan/blit_image.h | 5 +- .../renderer_vulkan/vk_rasterizer.cpp | 10 +-- .../renderer_vulkan/vk_texture_cache.cpp | 76 +++++++++++++++---- src/video_core/vulkan_common/vulkan_device.h | 10 +-- 8 files changed, 138 insertions(+), 37 deletions(-) create mode 100644 src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag create mode 100644 src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag 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; } From b0a01ca6296037ed33f6cc237cfe913ba5d70438 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 15:43:19 -0400 Subject: [PATCH 38/72] [TEST] Add varyings to the legacy fallback when no gemoetry shader passthrough is available --- .../frontend/maxwell/translate_program.cpp | 96 +++++++++++++------ .../renderer_opengl/gl_shader_cache.cpp | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 728d405674..40e7f83583 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -144,12 +144,15 @@ std::map GenerateLegacyToGenericMappings( for (size_t i = 0; i < count; ++i) { mapping.insert({attr + i, previous_stage_mapping.at(attr + i)}); } - } else { - for (size_t i = 0; i < count; ++i) { - mapping.insert({attr + i, unused_generics.front() + i}); - } - unused_generics.pop(); + return; } + if (unused_generics.empty()) { + return; + } + for (size_t i = 0; i < count; ++i) { + mapping.insert({attr + i, unused_generics.front() + i}); + } + unused_generics.pop(); }; for (size_t index = 0; index < 4; ++index) { auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; @@ -199,26 +202,46 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const PassthroughVertices vertices{GetPassthroughVertices(input_topology)}; for (u32 vertex = 0; vertex < vertices.count; vertex++) { const u32 i = vertices.first + vertex * vertices.stride; - // Assign generics from input - for (u32 j = 0; j < 32; j++) { - if (!passthrough_mask.Generic(j)) { - continue; - } - - const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4); + const auto copy_vec4{[&ir, i](IR::Attribute attr) { ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0)); ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0)); ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0)); ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0)); + }}; + + // Assign generics from input + for (u32 j = 0; j < IR::NUM_GENERICS; j++) { + if (!passthrough_mask.Generic(j)) { + continue; + } + copy_vec4(IR::Attribute::Generic0X + (j * 4)); + } + + for (u32 j = 0; j < 4; j++) { + const IR::Attribute attr = IR::Attribute::ColorFrontDiffuseR + (j * 4); + if (!passthrough_mask.AnyComponent(attr)) { + continue; + } + copy_vec4(attr); + } + + if (passthrough_mask[IR::Attribute::FogCoordinate]) { + ir.SetAttribute(IR::Attribute::FogCoordinate, + ir.GetAttribute(IR::Attribute::FogCoordinate, ir.Imm32(i)), + ir.Imm32(0)); + } + + for (u32 j = 0; j < IR::NUM_FIXEDFNCTEXTURE; j++) { + const IR::Attribute attr = IR::Attribute::FixedFncTexture0S + (j * 4); + if (!passthrough_mask.AnyComponent(attr)) { + continue; + } + copy_vec4(attr); } if (passthrough_position) { // Assign position from input - const IR::Attribute attr = IR::Attribute::PositionX; - ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0)); - ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0)); - ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0)); - ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0)); + copy_vec4(IR::Attribute::PositionX); } if (passthrough_layer_attr) { @@ -380,19 +403,24 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run program.info.legacy_stores_mapping = GenerateLegacyToGenericMappings(stores, unused_output_generics, {}); for (IR::Block* const block : program.post_order_blocks) { - for (IR::Inst& inst : block->Instructions()) { - switch (inst.GetOpcode()) { - case IR::Opcode::SetAttribute: { - const auto attr = inst.Arg(0).Attribute(); - if (IsLegacyAttribute(attr)) { - stores.Set(program.info.legacy_stores_mapping[attr], true); - inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr])); - } - break; + auto it{block->begin()}; + while (it != block->end()) { + IR::Inst& inst{*it}; + if (inst.GetOpcode() != IR::Opcode::SetAttribute || + !IsLegacyAttribute(inst.Arg(0).Attribute())) { + ++it; + continue; } - default: - break; + const auto& mapping{program.info.legacy_stores_mapping}; + const auto mapped{mapping.find(inst.Arg(0).Attribute())}; + if (mapped == mapping.end()) { + inst.Invalidate(); + it = block->Instructions().erase(it); + continue; } + stores.Set(mapped->second, true); + inst.SetArg(0, Shader::IR::Value(mapped->second)); + ++it; } } } @@ -414,10 +442,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run switch (inst.GetOpcode()) { case IR::Opcode::GetAttribute: { const auto attr = inst.Arg(0).Attribute(); - if (IsLegacyAttribute(attr)) { - loads.Set(mappings[attr], true); - inst.SetArg(0, Shader::IR::Value(mappings[attr])); + if (!IsLegacyAttribute(attr)) { + break; } + const auto mapped{mappings.find(attr)}; + if (mapped == mappings.end()) { + inst.ReplaceUsesWith(IR::Value{0.0f}); + break; + } + loads.Set(mapped->second, true); + inst.SetArg(0, Shader::IR::Value(mapped->second)); break; } default: diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 1377ac4674..c07d6fbde2 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -54,7 +54,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 16; +constexpr u32 CACHE_VERSION = 17; template auto MakeSpan(Container& container) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ed18587a15..854bef3192 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -62,7 +62,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 19; +constexpr u32 CACHE_VERSION = 20; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; From faacc62b10be59cb7492df5dabce840638493db6 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 17:42:09 -0400 Subject: [PATCH 39/72] [TEST] Prune on passthrough stores --- .../frontend/maxwell/translate_program.cpp | 46 +++++++++++++++++++ .../frontend/maxwell/translate_program.h | 2 + .../renderer_opengl/gl_shader_cache.cpp | 6 ++- .../renderer_vulkan/vk_pipeline_cache.cpp | 6 ++- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 40e7f83583..ce4b67fc19 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -391,6 +391,52 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b return result; } +void PrunePassthroughStores(IR::Program& program, const VaryingState& previous_stage_stores) { + if (program.stage != Stage::Geometry || program.is_geometry_passthrough) { + return; + } + VaryingState pruned; + for (size_t index = 0; index < program.info.passthrough.mask.size(); ++index) { + if (!program.info.passthrough.mask[index] || previous_stage_stores.mask[index]) { + continue; + } + const IR::Attribute attr{static_cast(index)}; + if (attr >= IR::Attribute::PositionX && attr <= IR::Attribute::PositionW) { + continue; + } + pruned.mask[index] = true; + } + if (pruned.mask.none()) { + return; + } + const auto erase_matching{[&pruned](IR::Block* block, IR::Opcode opcode, bool skip_used) { + auto it{block->begin()}; + while (it != block->end()) { + IR::Inst& inst{*it}; + if (inst.GetOpcode() != opcode || + !pruned.mask[static_cast(inst.Arg(0).Attribute())]) { + ++it; + continue; + } + if (skip_used && inst.HasUses()) { + ++it; + continue; + } + inst.Invalidate(); + it = block->Instructions().erase(it); + } + }}; + for (IR::Block* const block : program.post_order_blocks) { + erase_matching(block, IR::Opcode::SetAttribute, false); + } + for (IR::Block* const block : program.post_order_blocks) { + erase_matching(block, IR::Opcode::GetAttribute, true); + } + program.info.stores.mask &= ~pruned.mask; + program.info.loads.mask &= ~pruned.mask; + program.info.passthrough.mask &= ~pruned.mask; +} + void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { auto& stores = program.info.stores; if (stores.Legacy()) { diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 23c7ba1bbf..d44814ecce 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -29,6 +29,8 @@ namespace Shader::Maxwell { void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); +void PrunePassthroughStores(IR::Program& program, const VaryingState& previous_stage_stores); + // Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages. // This creates a workaround by setting the layer as a generic output and creating a // passthrough geometry shader that reads the generic and sets the layer. diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index c07d6fbde2..68431c67e3 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -45,6 +45,7 @@ using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; +using Shader::Maxwell::PrunePassthroughStores; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; using VideoCommon::FileEnvironment; @@ -54,7 +55,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 17; +constexpr u32 CACHE_VERSION = 18; template auto MakeSpan(Container& container) { @@ -537,6 +538,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( const size_t stage_index{index - 1}; infos[stage_index] = &program.info; + if (previous_program) { + PrunePassthroughStores(program, previous_program->info.stores); + } const auto runtime_info = MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm); switch (::Settings::values.renderer_backend.GetValue()) { case Settings::RendererBackend::OpenGL_GLSL: diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 854bef3192..606396529b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -56,13 +56,14 @@ using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; +using Shader::Maxwell::PrunePassthroughStores; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 20; +constexpr u32 CACHE_VERSION = 21; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -838,6 +839,9 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( const size_t stage_index{index - 1}; infos[stage_index] = &program.info; + if (previous_stage) { + PrunePassthroughStores(program, previous_stage->info.stores); + } const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage, device)}; ConvertLegacyToGeneric(program, runtime_info); const std::vector code{EmitSPIRV(profile, runtime_info, program, binding)}; From 8206c7abac5029c3c439ad396aee788de74b8792 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 19:05:47 -0400 Subject: [PATCH 40/72] [TEST] Add clipping distances to GS --- .../glsl/emit_glsl_context_get_set.cpp | 16 ++++++++ .../backend/glsl/glsl_emit_context.cpp | 9 +++-- .../spirv/emit_spirv_context_get_set.cpp | 17 ++++++++ .../backend/spirv/spirv_emit_context.cpp | 6 +++ .../backend/spirv/spirv_emit_context.h | 1 + .../frontend/maxwell/translate_program.cpp | 23 +++++++++++ .../renderer_opengl/gl_shader_cache.cpp | 38 ++++++++++++------ .../renderer_vulkan/vk_pipeline_cache.cpp | 40 +++++++++++++------ 8 files changed, 121 insertions(+), 29 deletions(-) diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index ffe5cd116c..8c69aa643b 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -217,6 +217,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle); break; } + case IR::Attribute::ClipDistance0: + case IR::Attribute::ClipDistance1: + case IR::Attribute::ClipDistance2: + case IR::Attribute::ClipDistance3: + case IR::Attribute::ClipDistance4: + case IR::Attribute::ClipDistance5: + case IR::Attribute::ClipDistance6: + case IR::Attribute::ClipDistance7: { + const u32 index{static_cast(attr) - static_cast(IR::Attribute::ClipDistance0)}; + if (IsInputArray(ctx.stage)) { + ctx.AddF32("{}=gl_in[{}].gl_ClipDistance[{}];", inst, vertex, index); + } else { + ctx.AddF32("{}=gl_ClipDistance[{}];", inst, index); + } + break; + } case IR::Attribute::PointSpriteS: case IR::Attribute::PointSpriteT: ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle); diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index a2df6159fb..729e09eb4b 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -306,9 +306,12 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile } else if (program.is_geometry_passthrough && !profile.support_geometry_shader_passthrough) { LOG_WARNING(Shader_GLSL, "Passthrough geometry program used but not supported"); } - header += fmt::format( - "layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;}}gl_in[];", - OutputPrimitive(program.output_topology), program.output_vertices); + header += fmt::format("layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;", + OutputPrimitive(program.output_topology), program.output_vertices); + if (program.info.loads.ClipDistances()) { + header += "float gl_ClipDistance[];"; + } + header += "}gl_in[];"; break; case Stage::Fragment: stage_name = "fs"; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 28f9869e22..56b2c13fbd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -367,6 +367,23 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face), ctx.OpBitcast(ctx.F32[1], ctx.Const((std::numeric_limits::max)())), ctx.f32_zero_value); + case IR::Attribute::ClipDistance0: + case IR::Attribute::ClipDistance1: + case IR::Attribute::ClipDistance2: + case IR::Attribute::ClipDistance3: + case IR::Attribute::ClipDistance4: + case IR::Attribute::ClipDistance5: + case IR::Attribute::ClipDistance6: + case IR::Attribute::ClipDistance7: { + const u32 base{static_cast(IR::Attribute::ClipDistance0)}; + const u32 index{static_cast(attr) - base}; + if (!ValidId(ctx.input_clip_distances) || index >= ctx.profile.max_user_clip_distances) { + return ctx.Const(1.0f); + } + const Id pointer{ + AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_clip_distances, ctx.Const(index))}; + return ctx.OpLoad(ctx.F32[1], pointer); + } case IR::Attribute::PointSpriteS: return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value)); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b562e82983..f6ed8b7f0f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1522,6 +1522,12 @@ void EmitContext::DefineInputs(const IR::Program& program) { } } } + if (stage == Stage::Geometry && info.loads.ClipDistances() && + profile.max_user_clip_distances > 0) { + const u32 used{(std::min)(profile.max_user_clip_distances, 8u)}; + const Id type{TypeArray(F32[1], Const(used))}; + input_clip_distances = DefineInput(*this, type, true, spv::BuiltIn::ClipDistance); + } if (loads[IR::Attribute::InstanceId]) { if (profile.support_vertex_instance_id) { instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index fb20966b67..16301f2d08 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -285,6 +285,7 @@ public: Id point_coord{}; Id tess_coord{}; Id clip_distances{}; + Id input_clip_distances{}; Id layer{}; Id viewport_index{}; Id viewport_mask{}; diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index ce4b67fc19..44635a1425 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -178,6 +178,20 @@ struct PassthroughVertices { u32 stride; }; +OutputTopology GetPassthroughTopology(InputTopology input_topology) { + switch (input_topology) { + case InputTopology::Points: + return OutputTopology::PointList; + case InputTopology::Lines: + case InputTopology::LinesAdjacency: + return OutputTopology::LineStrip; + case InputTopology::Triangles: + case InputTopology::TrianglesAdjacency: + return OutputTopology::TriangleStrip; + } + return OutputTopology::TriangleStrip; +} + PassthroughVertices GetPassthroughVertices(InputTopology input_topology) { switch (input_topology) { case InputTopology::Points: @@ -239,6 +253,14 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, copy_vec4(attr); } + for (u32 j = 0; j < 8; j++) { + const IR::Attribute attr = IR::Attribute::ClipDistance0 + j; + if (!passthrough_mask[attr]) { + continue; + } + ir.SetAttribute(attr, ir.GetAttribute(attr, ir.Imm32(i)), ir.Imm32(0)); + } + if (passthrough_position) { // Assign position from input copy_vec4(IR::Attribute::PositionX); @@ -304,6 +326,7 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } -Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { +Shader::OutputTopology InputToOutputTopology(Shader::InputTopology topology) { switch (topology) { - case Maxwell::PrimitiveTopology::Points: + case Shader::InputTopology::Points: return Shader::OutputTopology::PointList; - case Maxwell::PrimitiveTopology::LineStrip: + case Shader::InputTopology::Lines: + case Shader::InputTopology::LinesAdjacency: return Shader::OutputTopology::LineStrip; - default: + case Shader::InputTopology::Triangles: + case Shader::InputTopology::TrianglesAdjacency: return Shader::OutputTopology::TriangleStrip; } + return Shader::OutputTopology::TriangleStrip; } Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { @@ -92,6 +95,16 @@ Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology } } +Shader::InputTopology GeometryInputTopology(const GraphicsPipelineKey& key) { + if (key.unique_hashes[static_cast(Maxwell::ShaderType::Tessellation)] == 0) { + return MaxwellToInputTopology(key.gs_input_topology); + } + if (key.tessellation_primitive == Maxwell::Tessellation::DomainType::Isolines) { + return Shader::InputTopology::Lines; + } + return Shader::InputTopology::Triangles; +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -147,7 +160,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, default: break; } - info.input_topology = MaxwellToInputTopology(key.gs_input_topology); + info.input_topology = GeometryInputTopology(key); info.glasm_use_storage_buffers = glasm_use_storage_buffers; return info; } @@ -476,11 +489,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( const bool is_emulated_stage = layer_source_program != nullptr && index == u32(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { - auto topology = MaxwellToOutputTopology(key.gs_input_topology); - programs[index] = - GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology, - MaxwellToInputTopology(key.gs_input_topology)); + const auto input_topology = GeometryInputTopology(key); + programs[index] = GenerateGeometryPassthrough( + pools.inst, pools.block, host_info, *layer_source_program, + InputToOutputTopology(input_topology), input_topology); continue; } if (key.unique_hashes[index] == 0) { @@ -499,14 +511,14 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.gs_input_topology)); + GeometryInputTopology(key)); total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.gs_input_topology))}; + GeometryInputTopology(key))}; total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 606396529b..7fd27a7d83 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 21; +constexpr u32 CACHE_VERSION = 22; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -73,15 +73,18 @@ auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } -Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { +Shader::OutputTopology InputToOutputTopology(Shader::InputTopology topology) { switch (topology) { - case Maxwell::PrimitiveTopology::Points: + case Shader::InputTopology::Points: return Shader::OutputTopology::PointList; - case Maxwell::PrimitiveTopology::LineStrip: + case Shader::InputTopology::Lines: + case Shader::InputTopology::LinesAdjacency: return Shader::OutputTopology::LineStrip; - default: + case Shader::InputTopology::Triangles: + case Shader::InputTopology::TrianglesAdjacency: return Shader::OutputTopology::TriangleStrip; } + return Shader::OutputTopology::TriangleStrip; } Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { @@ -172,6 +175,18 @@ Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology } } +Shader::InputTopology GeometryInputTopology(const GraphicsPipelineCacheKey& key) { + if (key.unique_hashes[static_cast(Maxwell::ShaderType::Tessellation)] == 0) { + return MaxwellToInputTopology(key.state.topology); + } + const auto domain{ + static_cast(key.state.tessellation_primitive.Value())}; + if (domain == Maxwell::Tessellation::DomainType::Isolines) { + return Shader::InputTopology::Lines; + } + return Shader::InputTopology::Triangles; +} + Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -290,7 +305,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program default: break; } - info.input_topology = MaxwellToInputTopology(key.state.topology); + info.input_topology = GeometryInputTopology(key); info.force_early_z = key.state.early_z != 0; info.y_negate = key.state.y_negate != 0; return info; @@ -786,11 +801,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( const bool is_emulated_stage = layer_source_program != nullptr && index == static_cast(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { - auto topology = MaxwellToOutputTopology(key.state.topology); - programs[index] = - GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology, - MaxwellToInputTopology(key.state.topology)); + const auto input_topology = GeometryInputTopology(key); + programs[index] = GenerateGeometryPassthrough( + pools.inst, pools.block, host_info, *layer_source_program, + InputToOutputTopology(input_topology), input_topology); continue; } if (key.unique_hashes[index] == 0) { @@ -804,12 +818,12 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.state.topology)); + GeometryInputTopology(key)); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.state.topology))}; + GeometryInputTopology(key))}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } From a4c37b5117e3b989cf8addcfada0f5ab6dcadda5 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 20:49:54 -0400 Subject: [PATCH 41/72] Flush deferred clears tied to scissor entries on framebuffer --- src/video_core/renderer_opengl/gl_texture_cache.h | 2 ++ src/video_core/renderer_vulkan/vk_scheduler.cpp | 8 ++++++++ src/video_core/renderer_vulkan/vk_scheduler.h | 3 +++ src/video_core/renderer_vulkan/vk_texture_cache.cpp | 4 ++++ src/video_core/renderer_vulkan/vk_texture_cache.h | 2 ++ src/video_core/texture_cache/texture_cache.h | 2 ++ 6 files changed, 21 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 5da06668de..8aed499bb7 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -75,6 +75,8 @@ public: void Finish(); + void FlushDeferredClear() {} + StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false); StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 68852a0e82..792377d242 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -192,6 +192,14 @@ bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkC return true; } +void Scheduler::FlushDeferredClear() { + if (deferred_clear.framebuffer == nullptr) { + return; + } + RealizeDeferredClear(); + EndRenderPass(); +} + void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { if (deferred_clear.framebuffer == framebuffer) { RealizeDeferredClear(); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index c2aa3fb996..d4bc8d0ec5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -65,6 +65,9 @@ public: /// Defers a full depth/stencil clear so it becomes the next render pass. bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value); + /// Realizes any pending deferred clear before its framebuffer can be moved or freed. + void FlushDeferredClear(); + /// Requests the current execution context to be able to execute operations only allowed outside /// of a renderpass. void RequestOutsideRenderPassOperationContext(); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 35a04bd808..47d285f297 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1796,6 +1796,10 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } +void TextureCacheRuntime::FlushDeferredClear() { + scheduler.FlushDeferredClear(); +} + void TextureCacheRuntime::TickFrame() { std::erase_if(pending_msaa_images, [this](const auto& pending) { return scheduler.IsFree(pending.first); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 681866d7e3..fd11dd2ef6 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -60,6 +60,8 @@ public: void TickFrame(); + void FlushDeferredClear(); + u64 GetDeviceLocalMemory() const; u64 GetDeviceMemoryUsage() const; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index de56ba52ed..e3e9e213a8 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -577,6 +577,7 @@ FramebufferId TextureCache

::GetFramebufferId(const RenderTargets& key) { return id ? &slot_image_views[id] : nullptr; }); ImageView* const depth_buffer = key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr; + runtime.FlushDeferredClear(); framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key); return framebuffer_id; } @@ -2419,6 +2420,7 @@ void TextureCache

::RemoveImageViewReferences(std::span rem template void TextureCache

::RemoveFramebuffers(std::span removed_views) { + runtime.FlushDeferredClear(); auto it = framebuffers.begin(); while (it != framebuffers.end()) { if (it->first.Contains(removed_views)) { From b754fcc49c3940beaf8a2f7aa8cfc16e17f2738a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 22:40:54 -0400 Subject: [PATCH 42/72] [TEST] Ensure resolve shadow reads on msaa + shader extencil fallback --- src/video_core/renderer_vulkan/blit_image.cpp | 32 +++++++++++++------ .../renderer_vulkan/vk_scheduler.cpp | 1 + .../renderer_vulkan/vk_texture_cache.cpp | 26 +++++++++++---- .../renderer_vulkan/vk_texture_cache.h | 9 ++++++ 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 5d82bfe3b8..09ffa4df12 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -844,28 +844,40 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, Tegra::Engines::Fermi2D::Operation operation) { - if (!device.IsExtShaderStencilExportSupported()) { - return; - } ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point); ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy); + const bool blit_stencil = device.IsExtShaderStencilExportSupported(); const BlitImagePipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .operation = operation, }; - const VkPipelineLayout layout = *two_textures_pipeline_layout; + VkPipelineLayout layout{}; + VkPipeline pipeline{}; + VkImageView src_stencil_view = VK_NULL_HANDLE; + if (blit_stencil) { + layout = *two_textures_pipeline_layout; + pipeline = FindOrEmplaceDepthStencilPipeline(key); + src_stencil_view = src_image_view.StencilView(); + } else { + layout = *one_texture_pipeline_layout; + pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass); + } const VkSampler sampler = *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key); const VkImageView src_depth_view = src_image_view.DepthView(); - const VkImageView src_stencil_view = src_image_view.StencilView(); RecordShaderReadBarrier(scheduler, src_image_view); scheduler.RequestRenderpass(dst_framebuffer); scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view, - src_stencil_view, this](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit(); - UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view, - src_stencil_view); + src_stencil_view, blit_stencil, this](vk::CommandBuffer cmdbuf) { + VkDescriptorSet descriptor_set{}; + if (blit_stencil) { + descriptor_set = two_textures_descriptor_allocator.Commit(); + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view, + src_stencil_view); + } else { + descriptor_set = one_texture_descriptor_allocator.Commit(); + UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view); + } cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, nullptr); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 792377d242..31e852e943 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -132,6 +132,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); + framebuffer->MarkResolveShadowsUpToDate(); } void Scheduler::RealizeDeferredClear() { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 47d285f297..0c7f8cca77 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1019,7 +1019,6 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk if (shadow.image && shadow.format == format && shadow.extent.width == extent.width && shadow.extent.height == extent.height && shadow.layers == layers && shadow.aspect_mask == aspect_mask) { - shadow.up_to_date = true; return *shadow.view; } VkImageUsageFlags shadow_usage = @@ -1069,7 +1068,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk shadow.extent = extent; shadow.layers = layers; shadow.aspect_mask = aspect_mask; - shadow.up_to_date = true; + shadow.up_to_date = false; return *shadow.view; } @@ -1082,6 +1081,13 @@ const TextureCacheRuntime::ResolveShadow* TextureCacheRuntime::GetValidResolveSh return &it->second; } +void TextureCacheRuntime::MarkResolveShadowUpToDate(VkImage msaa_image) { + const auto it = resolve_shadows.find(msaa_image); + if (it != resolve_shadows.end()) { + it->second.up_to_date = true; + } +} + void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) { const auto it = resolve_shadows.find(msaa_image); if (it != resolve_shadows.end()) { @@ -1753,10 +1759,6 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, 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); @@ -3059,6 +3061,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, renderpass = runtime.render_pass_cache.Get(renderpass_key); render_pass_key = renderpass_key; render_pass_cache = &runtime.render_pass_cache; + runtime_ptr = &runtime; render_area.width = (std::min)(render_area.width, width); render_area.height = (std::min)(render_area.height, height); @@ -3075,6 +3078,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, const VkImage msaa_image = images[rt_map[index]]; attachments.push_back(runtime.GetOrCreateResolveShadow( msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT)); + resolve_shadow_images[num_resolve_shadows++] = msaa_image; continue; } VkImageCreateInfo resolve_ci{ @@ -3127,6 +3131,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, .format; attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area, layers, depth_aspect_mask)); + resolve_shadow_images[num_resolve_shadows++] = depth_image; } num_color_buffers = static_cast(num_colors); @@ -3143,6 +3148,15 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, }); } +void Framebuffer::MarkResolveShadowsUpToDate() const { + if (runtime_ptr == nullptr) { + return; + } + for (u32 index = 0; index < num_resolve_shadows; ++index) { + runtime_ptr->MarkResolveShadowUpToDate(resolve_shadow_images[index]); + } +} + VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, u32 color_discard_mask, bool depth_stencil_discard) const { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index fd11dd2ef6..f611c0f31b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -131,6 +131,8 @@ public: void InvalidateResolveShadow(VkImage msaa_image); + void MarkResolveShadowUpToDate(VkImage msaa_image); + void EraseResolveShadow(VkImage msaa_image); std::span ViewFormats(PixelFormat format) { @@ -257,6 +259,10 @@ public: return discard_msaa_depth_stencil; } + /// Records that a render pass has begun, so its resolve attachments will hold valid contents + /// once it ends. + void MarkResolveShadowsUpToDate() const; + private: static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8; @@ -274,6 +280,9 @@ private: bool is_rescaled{}; std::vector resolve_images; std::vector resolve_image_views; + std::array resolve_shadow_images{}; + u32 num_resolve_shadows = 0; + TextureCacheRuntime* runtime_ptr{nullptr}; RenderPassKey render_pass_key{}; RenderPassCache* render_pass_cache{nullptr}; bool discard_msaa_color{}; From e3be392b8a5edf83a61676d74136c171add24f12 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 19 Aug 2026 23:58:21 -0400 Subject: [PATCH 43/72] [TEST] Wondering around cbuf live route --- .../renderer_opengl/gl_shader_cache.cpp | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 +- src/video_core/shader_environment.cpp | 22 +++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 85efdb11ec..966e804318 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -55,7 +55,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 19; +constexpr u32 CACHE_VERSION = 20; template auto MakeSpan(Container& container) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7fd27a7d83..e2cd6e1e97 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 22; +constexpr u32 CACHE_VERSION = 23; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 4d5db7e511..0ae3c4b23e 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -338,11 +338,13 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, } u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { - const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]}; - ASSERT(cbuf.enabled); u32 value{}; - if (cbuf_offset < cbuf.size) { - value = gpu_memory->Read(cbuf.address + cbuf_offset); + const auto& stage_cbufs{maxwell3d->state.shader_stages[stage_index].const_buffers}; + if (cbuf_index < stage_cbufs.size()) { + const auto& cbuf{stage_cbufs[cbuf_index]}; + if (cbuf.enabled && cbuf_offset < cbuf.size) { + value = gpu_memory->Read(cbuf.address + cbuf_offset); + } } cbuf_values.emplace(MakeCbufKey(cbuf_index, cbuf_offset), value); return value; @@ -420,12 +422,14 @@ ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_com } u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { - const auto& qmd{kepler_compute->launch_description}; - ASSERT(((qmd.const_buffer_enable_mask.Value() >> cbuf_index) & 1) != 0); - const auto& cbuf{qmd.const_buffer_config[cbuf_index]}; u32 value{}; - if (cbuf_offset < cbuf.size) { - value = gpu_memory->Read(cbuf.Address() + cbuf_offset); + const auto& qmd{kepler_compute->launch_description}; + if (cbuf_index < qmd.const_buffer_config.size()) { + const bool enabled{((qmd.const_buffer_enable_mask.Value() >> cbuf_index) & 1) != 0}; + const auto& cbuf{qmd.const_buffer_config[cbuf_index]}; + if (enabled && cbuf_offset < cbuf.size) { + value = gpu_memory->Read(cbuf.Address() + cbuf_offset); + } } cbuf_values.emplace(MakeCbufKey(cbuf_index, cbuf_offset), value); return value; From 543d4dfc8ed263a5d0feeee7e177218f04e5a01b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 20 Aug 2026 01:48:16 -0400 Subject: [PATCH 44/72] [TEST] Bypass unresolved render --- src/video_core/engines/maxwell_3d.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index e7da9d9138..fb5b8ab206 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -22,6 +22,8 @@ namespace Tegra::Engines { +constexpr bool BYPASS_UNRESOLVED_RENDER_ENABLE = true; + /// First register id that is actually a Macro call. constexpr u32 MacroRegistersStart = 0xE00; @@ -517,6 +519,10 @@ void Maxwell3D::ProcessQueryCondition() { execute_on = true; return; } + if (BYPASS_UNRESOLVED_RENDER_ENABLE) { + execute_on = true; + return; + } const GPUVAddr condition_address{regs.render_enable.Address()}; switch (regs.render_enable_override) { case Regs::RenderEnable::Override::AlwaysRender: From 68ff82679fb5198a9cbba893eb4b753ae579732e Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 20 Aug 2026 02:54:02 -0400 Subject: [PATCH 45/72] GIVE ME SOMETHING --- src/video_core/engines/maxwell_3d.cpp | 6 ------ src/video_core/renderer_opengl/gl_graphics_pipeline.h | 1 + src/video_core/renderer_opengl/gl_shader_cache.cpp | 3 ++- src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | 1 + src/video_core/renderer_vulkan/fixed_pipeline_state.h | 1 + src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index fb5b8ab206..e7da9d9138 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -22,8 +22,6 @@ namespace Tegra::Engines { -constexpr bool BYPASS_UNRESOLVED_RENDER_ENABLE = true; - /// First register id that is actually a Macro call. constexpr u32 MacroRegistersStart = 0xE00; @@ -519,10 +517,6 @@ void Maxwell3D::ProcessQueryCondition() { execute_on = true; return; } - if (BYPASS_UNRESOLVED_RENDER_ENABLE) { - execute_on = true; - return; - } const GPUVAddr condition_address{regs.render_enable.Address()}; switch (regs.render_enable_override) { case Regs::RenderEnable::Override::AlwaysRender: diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 66ab677919..d536adf630 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -44,6 +44,7 @@ struct GraphicsPipelineKey { BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing; BitField<10, 1, u32> tessellation_clockwise; BitField<11, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage; + BitField<14, 1, u32> viewport_scale_offset_enabled; }; std::array padding; VideoCommon::TransformFeedbackState xfb_state; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 966e804318..f59d3c73dc 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -55,7 +55,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 20; +constexpr u32 CACHE_VERSION = 21; template auto MakeSpan(Container& container) { @@ -390,6 +390,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { regs.tessellation.params.output_primitives.Value() == Maxwell::Tessellation::OutputPrimitives::Triangles_CW); graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); + graphics_key.viewport_scale_offset_enabled.Assign(regs.viewport_scale_offset_enabled != 0); graphics_key.app_stage.Assign(maxwell3d->engine_state); if (graphics_key.xfb_enabled) { SetXfbState(graphics_key.xfb_state, regs); diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 0ced890929..3eb9bf841a 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -141,6 +141,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0); depth_format.Assign(static_cast(regs.zeta.format)); y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); + viewport_scale_offset_enabled.Assign(regs.viewport_scale_offset_enabled != 0); bool use_last_provoking_vertex = false; const bool provoking_vertex_available = features.has_provoking_vertex; diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 36a4b4b7a2..27710ab806 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -231,6 +231,7 @@ struct FixedPipelineState { BitField<15, 1, u32> alpha_to_coverage_enabled; BitField<16, 1, u32> alpha_to_one_enabled; BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage; + BitField<20, 1, u32> viewport_scale_offset_enabled; }; std::array color_formats; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index e2cd6e1e97..2bfc9937c3 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 23; +constexpr u32 CACHE_VERSION = 24; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; From f0ba93d75f7e87b9d05e47623186b2173f1b9106 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 20 Aug 2026 15:36:22 -0400 Subject: [PATCH 46/72] Here we go again --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 5 +++-- src/video_core/vulkan_common/vulkan_device.cpp | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 2bfc9937c3..9d0bf3c95a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 24; +constexpr u32 CACHE_VERSION = 25; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -454,7 +454,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .has_broken_spirv_position_input = driver_id == false, .has_broken_unsigned_image_offsets = false, .has_broken_signed_operations = false, - .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, + .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY || + driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .ignore_nan_fp_comparisons = false, .has_broken_spirv_subgroup_mask_vector_extract_dynamic = false, .has_broken_robust = diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index bd833ba784..4011656d88 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -526,8 +526,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable."); RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable, VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls."); - RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64."); RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); From 6390edb76d9515c2df330d0c5c048b0f7c298968 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 20 Aug 2026 16:33:14 -0400 Subject: [PATCH 47/72] Remove FP32 sz nan preserve on QCOM --- src/shader_recompiler/backend/spirv/emit_spirv.cpp | 3 ++- src/shader_recompiler/profile.h | 2 ++ src/video_core/renderer_opengl/gl_shader_cache.cpp | 1 + src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 53453e6ab7..2f7fd2bb42 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -381,7 +381,8 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve); ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 16U); } - if (profile.support_fp32_signed_zero_nan_preserve) { + if (profile.support_fp32_signed_zero_nan_preserve && + !profile.has_broken_fp32_signed_zero_nan_preserve) { ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve); ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U); } diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index eb7b2a02b2..98e3e0abd4 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -86,6 +86,8 @@ struct Profile { bool has_broken_signed_operations{}; /// Float controls break when fp16 is enabled bool has_broken_fp16_float_controls{}; + /// Forcing fp32 signed zero, Inf and NaN preservation miscompiles on some drivers + bool has_broken_fp32_signed_zero_nan_preserve{}; /// Dynamic vec4 indexing is broken on some OpenGL drivers bool has_gl_component_indexing_bug{}; /// The precise type qualifier is broken in the fragment stage of some drivers diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index f59d3c73dc..879e15f56e 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -238,6 +238,7 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .has_broken_unsigned_image_offsets = true, .has_broken_signed_operations = true, .has_broken_fp16_float_controls = false, + .has_broken_fp32_signed_zero_nan_preserve = false, .has_gl_component_indexing_bug = device.HasComponentIndexingBug(), .has_gl_precise_bug = device.HasPreciseBug(), .has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(), diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9d0bf3c95a..767cd45137 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -456,6 +456,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .has_broken_signed_operations = false, .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, + .has_broken_fp32_signed_zero_nan_preserve = + driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .ignore_nan_fp_comparisons = false, .has_broken_spirv_subgroup_mask_vector_extract_dynamic = false, .has_broken_robust = From 4bf30b41a6efee287cb82e1a74872c85e3ce7413 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 20 Aug 2026 19:49:12 -0400 Subject: [PATCH 48/72] =?UTF-8?q?ASD=C3=91lasfu=20mfudgh=20rq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shader_recompiler/backend/spirv/emit_spirv.cpp | 2 +- src/shader_recompiler/profile.h | 2 ++ src/video_core/renderer_opengl/gl_shader_cache.cpp | 1 + src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 2f7fd2bb42..585543df2b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -335,7 +335,7 @@ void SetupDenormControl(const Profile& profile, const IR::Program& program, Emit if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) { LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader"); } else if (info.uses_fp32_denorms_flush) { - if (profile.support_fp32_denorm_flush) { + if (profile.support_fp32_denorm_flush && !profile.has_broken_fp32_denorm_flush) { ctx.AddCapability(spv::Capability::DenormFlushToZero); ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U); } else { diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 98e3e0abd4..d0cf98d3fd 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -88,6 +88,8 @@ struct Profile { bool has_broken_fp16_float_controls{}; /// Forcing fp32 signed zero, Inf and NaN preservation miscompiles on some drivers bool has_broken_fp32_signed_zero_nan_preserve{}; + /// Declaring fp32 denorm flush to zero miscompiles on some drivers + bool has_broken_fp32_denorm_flush{}; /// Dynamic vec4 indexing is broken on some OpenGL drivers bool has_gl_component_indexing_bug{}; /// The precise type qualifier is broken in the fragment stage of some drivers diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 879e15f56e..8a375a2c7b 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -239,6 +239,7 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .has_broken_signed_operations = true, .has_broken_fp16_float_controls = false, .has_broken_fp32_signed_zero_nan_preserve = false, + .has_broken_fp32_denorm_flush = false, .has_gl_component_indexing_bug = device.HasComponentIndexingBug(), .has_gl_precise_bug = device.HasPreciseBug(), .has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(), diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 767cd45137..db0bd2dab0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -458,6 +458,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .has_broken_fp32_signed_zero_nan_preserve = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, + .has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .ignore_nan_fp_comparisons = false, .has_broken_spirv_subgroup_mask_vector_extract_dynamic = false, .has_broken_robust = From 1cd118eccc3dbda383bce90460c4de1b0e721eeb Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 21 Aug 2026 21:32:19 -0400 Subject: [PATCH 49/72] [TEST] Return FP16 + FP32 SZ nan-preserve on QCOM --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index db0bd2dab0..96007ec276 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 25; +constexpr u32 CACHE_VERSION = 26; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -454,10 +454,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .has_broken_spirv_position_input = driver_id == false, .has_broken_unsigned_image_offsets = false, .has_broken_signed_operations = false, - .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY || - driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, - .has_broken_fp32_signed_zero_nan_preserve = - driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, + .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, + .has_broken_fp32_signed_zero_nan_preserve = false, .has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .ignore_nan_fp_comparisons = false, .has_broken_spirv_subgroup_mask_vector_extract_dynamic = false, From 33ae95f4ae3269f3aba6ef811aef3f562adfe645 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 21 Aug 2026 22:30:07 -0400 Subject: [PATCH 50/72] [TEST] Add Exchange initialization on 3D pass for linear unswizzling --- .../renderer_vulkan/vk_compute_pass.cpp | 24 ++++++++++++------- .../renderer_vulkan/vk_compute_pass.h | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 54f8f9520f..83b9d55916 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -720,13 +720,15 @@ void BlockLinearUnswizzle3DPass::Unswizzle( const u32 blocks_x = (image.info.size.width + 3) / 4; const u32 blocks_y = (image.info.size.height + 3) / 4; + const bool is_initialized = image.ExchangeInitialization(); + scheduler.RequestOutsideRenderPassOperationContext(); for (u32 z_offset = 0; z_offset < z_count; z_offset += MAX_BATCH_SLICES) { const u32 current_chunk_slices = (std::min)(MAX_BATCH_SLICES, z_count - z_offset); const u32 current_z_start = z_start + z_offset; UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, - current_z_start, current_chunk_slices); + current_z_start, current_chunk_slices, is_initialized); } } @@ -736,7 +738,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const VideoCommon::SwizzleParameters& sw, const BlockLinearSwizzle3DParams& params, u32 blocks_x, u32 blocks_y, - u32 z_start, u32 z_count) + u32 z_start, u32 z_count, bool is_initialized) { BlockLinearUnswizzle3DPushConstants pc{}; pc.origin[0] = params.origin[0]; @@ -780,6 +782,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const VkDeviceSize barrier_size = output_slice_size * z_count; const bool is_first_chunk = (z_start == 0); + const bool use_undefined_layout = !is_initialized && is_first_chunk; const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkImage dst_image = image.Handle(); @@ -788,7 +791,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const u32 image_height = image.info.size.height; scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, z_start, z_count, - barrier_size, is_first_chunk, out_buffer, dst_image, aspect, + barrier_size, use_undefined_layout, out_buffer, dst_image, aspect, image_width, image_height ](vk::CommandBuffer cmdbuf) { @@ -815,15 +818,14 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .size = barrier_size, }; - // Image layout transition const VkImageMemoryBarrier pre_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = is_first_chunk ? VkAccessFlags{} : - static_cast(VK_ACCESS_TRANSFER_WRITE_BIT), + .srcAccessMask = use_undefined_layout ? VkAccessFlags{} : + static_cast(VK_ACCESS_SHADER_READ_BIT), .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = is_first_chunk ? VK_IMAGE_LAYOUT_UNDEFINED : - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .oldLayout = use_undefined_layout ? VK_IMAGE_LAYOUT_UNDEFINED : + VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -831,9 +833,13 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .subresourceRange = {aspect, 0, 1, 0, 1}, }; + VkPipelineStageFlags pre_barrier_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + pre_barrier_src_stages |= use_undefined_layout ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT + : vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; + // Single barrier handles both buffer and image cmdbuf.PipelineBarrier( - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + pre_barrier_src_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, buffer_barrier, pre_barrier diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 5150e95b90..f3dc4b7697 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -156,7 +156,7 @@ public: const VideoCommon::SwizzleParameters& sw, const BlockLinearSwizzle3DParams& params, u32 blocks_x, u32 blocks_y, - u32 z_start, u32 z_count); + u32 z_start, u32 z_count, bool is_initialized); private: Scheduler& scheduler; From 9510516e68cde8fd1d8e6f7efa04f03c481f8637 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 21 Aug 2026 22:41:03 -0400 Subject: [PATCH 51/72] Fix build --- .../renderer_vulkan/vk_compute_pass.cpp | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 83b9d55916..7992236f85 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -818,14 +818,23 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .size = barrier_size, }; + VkAccessFlags pre_barrier_src_access = VK_ACCESS_SHADER_READ_BIT; + VkImageLayout pre_barrier_old_layout = VK_IMAGE_LAYOUT_GENERAL; + VkPipelineStageFlags pre_barrier_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + if (use_undefined_layout) { + pre_barrier_src_access = VkAccessFlags{}; + pre_barrier_old_layout = VK_IMAGE_LAYOUT_UNDEFINED; + pre_barrier_src_stages |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } else { + pre_barrier_src_stages |= vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; + } + const VkImageMemoryBarrier pre_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = use_undefined_layout ? VkAccessFlags{} : - static_cast(VK_ACCESS_SHADER_READ_BIT), + .srcAccessMask = pre_barrier_src_access, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = use_undefined_layout ? VK_IMAGE_LAYOUT_UNDEFINED : - VK_IMAGE_LAYOUT_GENERAL, + .oldLayout = pre_barrier_old_layout, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -833,10 +842,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .subresourceRange = {aspect, 0, 1, 0, 1}, }; - VkPipelineStageFlags pre_barrier_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - pre_barrier_src_stages |= use_undefined_layout ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT - : vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; - // Single barrier handles both buffer and image cmdbuf.PipelineBarrier( pre_barrier_src_stages, From 78ce48e4ca54a6e5db3d91e123101261a11ca637 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 21 Aug 2026 23:00:15 -0400 Subject: [PATCH 52/72] add finish on block linear pass --- src/video_core/renderer_vulkan/vk_compute_pass.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 7992236f85..d1effe2c6c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -730,6 +730,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle( UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, current_z_start, current_chunk_slices, is_initialized); } + scheduler.Finish(); } void BlockLinearUnswizzle3DPass::UnswizzleChunk( @@ -1121,6 +1122,7 @@ void BlockLinearUnswizzle2DPass::Unswizzle( cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, post_copy); }); + scheduler.Finish(); } namespace { @@ -1372,6 +1374,7 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, post_copy); }); + scheduler.Finish(); } } // namespace Vulkan From 22b51d0c61c2ce6c8e3415bd198c5ee4cc5f0a15 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 21 Aug 2026 23:19:49 -0400 Subject: [PATCH 53/72] Extract pitch unswizzle pass from vk-experiments7 --- .../renderer_vulkan/vk_compute_pass.cpp | 129 ++++++++++++++++++ .../renderer_vulkan/vk_compute_pass.h | 19 +++ .../renderer_vulkan/vk_texture_cache.cpp | 40 ++++++ .../renderer_vulkan/vk_texture_cache.h | 1 + 4 files changed, 189 insertions(+) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index d1effe2c6c..ae7bab0671 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -25,6 +25,7 @@ #include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h" +#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/surface.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -197,6 +198,13 @@ struct QueriesPrefixScanPushConstants { struct ConditionalRenderingResolvePushConstants { u32 compare_to_zero; }; + +struct PitchUnswizzlePushConstants { + std::array origin; + std::array destination; + u32 bytes_per_block; + u32 pitch; +}; } // Anonymous namespace ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, DescriptorPool& descriptor_pool, @@ -1377,4 +1385,125 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( scheduler.Finish(); } +PitchUnswizzlePass::PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, + ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + PITCH_UNSWIZZLE_COMP_SPV), + scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +PitchUnswizzlePass::~PitchUnswizzlePass() = default; + +bool PitchUnswizzlePass::IsSupported(const VideoCommon::ImageInfo& info) { + if (info.type != VideoCommon::ImageType::Linear) { + return false; + } + if (VideoCore::Surface::IsPixelFormatASTC(info.format) || + VideoCore::Surface::IsPixelFormatBCn(info.format)) { + return false; + } + if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) { + return false; + } + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); + return bytes_per_block == 1 || bytes_per_block == 2 || bytes_per_block == 4 || + bytes_per_block == 8 || bytes_per_block == 16; +} + +void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& swizzled, + std::span swizzles) { + scheduler.RequestOutsideRenderPassOperationContext(); + const VkPipeline vk_pipeline = *pipeline; + const VkImageAspectFlags aspect_mask = image.AspectMask(); + const VkImage vk_image = image.Handle(); + const bool is_initialized = image.ExchangeInitialization(); + scheduler.Record([vk_pipeline, vk_image, aspect_mask, + is_initialized](vk::CommandBuffer cmdbuf) { + VkAccessFlags src_access = VK_ACCESS_NONE; + VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED; + VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + if (is_initialized) { + src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + old_layout = VK_IMAGE_LAYOUT_GENERAL; + src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER; + } + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = old_layout, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); + }); + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format); + const u32 pitch = image.info.pitch; + for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { + const size_t input_offset = swizzle.buffer_offset + swizzled.offset; + const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); + const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, input_offset, + image.guest_size_bytes - swizzle.buffer_offset); + compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); + const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; + + const PitchUnswizzlePushConstants params{ + .origin = {0, 0}, + .destination = {0, 0}, + .bytes_per_block = bytes_per_block, + .pitch = pitch, + }; + scheduler.Record([this, num_dispatches_x, num_dispatches_y, params, + descriptor_data](vk::CommandBuffer cmdbuf) { + const VkDescriptorSet set = descriptor_allocator.Commit(); + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); + cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1); + }); + } + scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .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_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier); + }); + scheduler.Finish(); +} + } // 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 f3dc4b7697..b763887d5d 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -202,4 +202,23 @@ private: ComputePassDescriptorQueue& compute_pass_descriptor_queue; }; +class PitchUnswizzlePass final : public ComputePass { +public: + explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~PitchUnswizzlePass(); + + [[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info); + + void Unswizzle(Image& image, const StagingBufferRef& swizzled, + std::span swizzles); + +private: + Scheduler& scheduler; + StagingBufferPool& staging_buffer_pool; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 0c7f8cca77..e45eb66c31 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -233,6 +233,29 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { return allocator.CreateImage(image_ci); } +[[nodiscard]] VkFormat UnswizzleStorageFormat(u32 bytes_per_block) { + switch (bytes_per_block) { + case 1: + return VK_FORMAT_R8_UINT; + case 2: + return VK_FORMAT_R16_UINT; + case 4: + return VK_FORMAT_R32_UINT; + case 8: + return VK_FORMAT_R32G32_UINT; + case 16: + return VK_FORMAT_R32G32B32A32_UINT; + default: + ASSERT_MSG(false, "Invalid bytes_per_block={} for accelerated unswizzle", bytes_per_block); + return VK_FORMAT_R32_UINT; + } +} + +[[nodiscard]] bool IsUnswizzleStorageFormatSupported(u32 bytes_per_block) { + return bytes_per_block == 1 || bytes_per_block == 2 || bytes_per_block == 4 || + bytes_per_block == 8 || bytes_per_block == 16; +} + [[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image, VkFormat format) { static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ @@ -932,6 +955,11 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched const auto image_format = static_cast(index_a); if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) { view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32); + } else if (!IsPixelFormatASTC(image_format) && !IsPixelFormatBCn(image_format)) { + const u32 bpp = VideoCore::Surface::BytesPerBlock(image_format); + if (IsUnswizzleStorageFormatSupported(bpp)) { + view_formats[index_a].push_back(UnswizzleStorageFormat(bpp)); + } } for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) { const auto view_format = static_cast(index_b); @@ -951,6 +979,8 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched compute_pass_descriptor_queue); bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue); + pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, + compute_pass_descriptor_queue); } void TextureCacheRuntime::Finish() { @@ -1840,6 +1870,9 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) { flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; flags |= VideoCommon::ImageFlagBits::CostlyLoad; + } else if (runtime->pitch_unswizzle_pass && PitchUnswizzlePass::IsSupported(info)) { + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; + flags |= VideoCommon::ImageFlagBits::CostlyLoad; } if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) { flags |= VideoCommon::ImageFlagBits::Converted; @@ -2284,6 +2317,9 @@ VkImageView Image::StorageImageView(s32 level) noexcept { MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); if (WillUseAcceleratedAstcDecode(runtime->device, info)) { format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; + } else if (info.type == ImageType::Linear) { + format_info.format = + UnswizzleStorageFormat(VideoCore::Surface::BytesPerBlock(info.format)); } view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), format_info.format); @@ -3205,6 +3241,10 @@ void TextureCacheRuntime::AccelerateImageUpload( return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles); } + if (pitch_unswizzle_pass && PitchUnswizzlePass::IsSupported(image.info)) { + return pitch_unswizzle_pass->Unswizzle(image, map, swizzles); + } + if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index f611c0f31b..1a44e1b382 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -157,6 +157,7 @@ public: std::optional bl3d_unswizzle_pass; std::optional bl2d_unswizzle_pass; std::optional bl3db_unswizzle_pass; + std::optional pitch_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; From 6a3ff818e0bb4e214a661cebb30d15be7e0143af Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 00:45:54 -0400 Subject: [PATCH 54/72] Revert "Extract pitch unswizzle pass from vk-experiments7" --- .../renderer_vulkan/vk_compute_pass.cpp | 129 ------------------ .../renderer_vulkan/vk_compute_pass.h | 19 --- .../renderer_vulkan/vk_texture_cache.cpp | 40 ------ .../renderer_vulkan/vk_texture_cache.h | 1 - 4 files changed, 189 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index ae7bab0671..d1effe2c6c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -25,7 +25,6 @@ #include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h" -#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/surface.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -198,13 +197,6 @@ struct QueriesPrefixScanPushConstants { struct ConditionalRenderingResolvePushConstants { u32 compare_to_zero; }; - -struct PitchUnswizzlePushConstants { - std::array origin; - std::array destination; - u32 bytes_per_block; - u32 pitch; -}; } // Anonymous namespace ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, DescriptorPool& descriptor_pool, @@ -1385,125 +1377,4 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( scheduler.Finish(); } -PitchUnswizzlePass::PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, - DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, - ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, - COMPUTE_PUSH_CONSTANT_RANGE, - PITCH_UNSWIZZLE_COMP_SPV), - scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, - compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} - -PitchUnswizzlePass::~PitchUnswizzlePass() = default; - -bool PitchUnswizzlePass::IsSupported(const VideoCommon::ImageInfo& info) { - if (info.type != VideoCommon::ImageType::Linear) { - return false; - } - if (VideoCore::Surface::IsPixelFormatASTC(info.format) || - VideoCore::Surface::IsPixelFormatBCn(info.format)) { - return false; - } - if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) { - return false; - } - const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); - return bytes_per_block == 1 || bytes_per_block == 2 || bytes_per_block == 4 || - bytes_per_block == 8 || bytes_per_block == 16; -} - -void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& swizzled, - std::span swizzles) { - scheduler.RequestOutsideRenderPassOperationContext(); - const VkPipeline vk_pipeline = *pipeline; - const VkImageAspectFlags aspect_mask = image.AspectMask(); - const VkImage vk_image = image.Handle(); - const bool is_initialized = image.ExchangeInitialization(); - scheduler.Record([vk_pipeline, vk_image, aspect_mask, - is_initialized](vk::CommandBuffer cmdbuf) { - VkAccessFlags src_access = VK_ACCESS_NONE; - VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - if (is_initialized) { - src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - old_layout = VK_IMAGE_LAYOUT_GENERAL; - src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER; - } - const VkImageMemoryBarrier image_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = src_access, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = old_layout, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = vk_image, - .subresourceRange{ - .aspectMask = aspect_mask, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); - }); - const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format); - const u32 pitch = image.info.pitch; - for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { - const size_t input_offset = swizzle.buffer_offset + swizzled.offset; - const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); - const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); - - compute_pass_descriptor_queue.Acquire(scheduler, 2); - compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, input_offset, - image.guest_size_bytes - swizzle.buffer_offset); - compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); - const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; - - const PitchUnswizzlePushConstants params{ - .origin = {0, 0}, - .destination = {0, 0}, - .bytes_per_block = bytes_per_block, - .pitch = pitch, - }; - scheduler.Record([this, num_dispatches_x, num_dispatches_y, params, - descriptor_data](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet set = descriptor_allocator.Commit(); - device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); - cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); - cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1); - }); - } - scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier image_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = vk_image, - .subresourceRange{ - .aspectMask = aspect_mask, - .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_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier); - }); - scheduler.Finish(); -} - } // 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 b763887d5d..f3dc4b7697 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -202,23 +202,4 @@ private: ComputePassDescriptorQueue& compute_pass_descriptor_queue; }; -class PitchUnswizzlePass final : public ComputePass { -public: - explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, - DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_); - ~PitchUnswizzlePass(); - - [[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info); - - void Unswizzle(Image& image, const StagingBufferRef& swizzled, - std::span swizzles); - -private: - Scheduler& scheduler; - StagingBufferPool& staging_buffer_pool; - ComputePassDescriptorQueue& compute_pass_descriptor_queue; -}; - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index e45eb66c31..0c7f8cca77 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -233,29 +233,6 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { return allocator.CreateImage(image_ci); } -[[nodiscard]] VkFormat UnswizzleStorageFormat(u32 bytes_per_block) { - switch (bytes_per_block) { - case 1: - return VK_FORMAT_R8_UINT; - case 2: - return VK_FORMAT_R16_UINT; - case 4: - return VK_FORMAT_R32_UINT; - case 8: - return VK_FORMAT_R32G32_UINT; - case 16: - return VK_FORMAT_R32G32B32A32_UINT; - default: - ASSERT_MSG(false, "Invalid bytes_per_block={} for accelerated unswizzle", bytes_per_block); - return VK_FORMAT_R32_UINT; - } -} - -[[nodiscard]] bool IsUnswizzleStorageFormatSupported(u32 bytes_per_block) { - return bytes_per_block == 1 || bytes_per_block == 2 || bytes_per_block == 4 || - bytes_per_block == 8 || bytes_per_block == 16; -} - [[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image, VkFormat format) { static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ @@ -955,11 +932,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched const auto image_format = static_cast(index_a); if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) { view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32); - } else if (!IsPixelFormatASTC(image_format) && !IsPixelFormatBCn(image_format)) { - const u32 bpp = VideoCore::Surface::BytesPerBlock(image_format); - if (IsUnswizzleStorageFormatSupported(bpp)) { - view_formats[index_a].push_back(UnswizzleStorageFormat(bpp)); - } } for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) { const auto view_format = static_cast(index_b); @@ -979,8 +951,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched compute_pass_descriptor_queue); bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue); - pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, - compute_pass_descriptor_queue); } void TextureCacheRuntime::Finish() { @@ -1870,9 +1840,6 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) { flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; flags |= VideoCommon::ImageFlagBits::CostlyLoad; - } else if (runtime->pitch_unswizzle_pass && PitchUnswizzlePass::IsSupported(info)) { - flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; - flags |= VideoCommon::ImageFlagBits::CostlyLoad; } if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) { flags |= VideoCommon::ImageFlagBits::Converted; @@ -2317,9 +2284,6 @@ VkImageView Image::StorageImageView(s32 level) noexcept { MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); if (WillUseAcceleratedAstcDecode(runtime->device, info)) { format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; - } else if (info.type == ImageType::Linear) { - format_info.format = - UnswizzleStorageFormat(VideoCore::Surface::BytesPerBlock(info.format)); } view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), format_info.format); @@ -3241,10 +3205,6 @@ void TextureCacheRuntime::AccelerateImageUpload( return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles); } - if (pitch_unswizzle_pass && PitchUnswizzlePass::IsSupported(image.info)) { - return pitch_unswizzle_pass->Unswizzle(image, map, swizzles); - } - if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 1a44e1b382..f611c0f31b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -157,7 +157,6 @@ public: std::optional bl3d_unswizzle_pass; std::optional bl2d_unswizzle_pass; std::optional bl3db_unswizzle_pass; - std::optional pitch_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; From 04cfc60756e206d8b6c74b4d798fde1675a78709 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 00:48:13 -0400 Subject: [PATCH 55/72] Fix licenses --- .../backend/glsl/emit_glsl_context_get_set.cpp | 3 +++ src/video_core/renderer_opengl/gl_graphics_pipeline.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 8c69aa643b..32586c0447 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index d536adf630..a5a573ca46 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project From b22a796f4b1fc316b3ffb516ceedbb399e3fe135 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 00:51:44 -0400 Subject: [PATCH 56/72] Remove strings from rescale hack --- src/android/app/src/main/res/values-ar/strings.xml | 2 -- src/android/app/src/main/res/values-es/strings.xml | 2 -- src/android/app/src/main/res/values-ru/strings.xml | 2 -- src/android/app/src/main/res/values-uk/strings.xml | 2 -- src/android/app/src/main/res/values-zh-rCN/strings.xml | 2 -- src/android/app/src/main/res/values/strings.xml | 2 -- 6 files changed, 12 deletions(-) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index bad0e25dac..6911dfaca8 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -514,8 +514,6 @@ يقلل من ضبابية التوهج في LA/EOW (Adreno A6XX - A7XX/ Turnip)، ويزيل التوهج في Burnout. تحذير: قد يسبب تشوهات رسومية في ألعاب أخرى. محاكاة BGR565 يُصلح مشاكل انعكاس الألوان في الألعاب أو ظهور تشوهات غريبة أو ظلال غريبة. - تفعيل ميزة إعادة التحجيم القديمة - يُمكّن هذا الخيار من التعامل مع عملية إعادة تحجيم الألعاب بطريقة تقليدية باستخدام مسار إعادة التحجيم السريع استخدم تظليل غير متزامن يقوم بتجميع التظليل بشكل غير متزامن. قد يقلل ذلك من التقطعات ولكنه قد يؤدي أيضًا إلى حدوث أخطاء. إعدادات إلغاء ترتيب بيانات وحدة معالجة الرسومات diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index c07bbb6fe9..04c25a6d4c 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -506,8 +506,6 @@ Reduce el efecto de resplandor en LA/EOW (Adreno A6XX - A7XX/ Turnip), elimina el resplandor en Burnout. Advertencia: puede causar artefactos gráficos en otros juegos. Emular BGR565 Soluciona problemas con colores invertidos en juegos, artefactos o sombras extrañas. - Activar la pasada de reescalado heredada - Permite el manejo de versiones anteriores para el paso de configuración de reescalado para juegos mediante el uso de una ruta de reescalado rápida. Usar sombreadores asíncronos Compila los sombreadores de forma asíncrona. Esto puede reducir los tirones, pero también puede introducir errores gráficos. Ajustes de desentrelazado de la GPU diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 370c6d206e..7f65a099bb 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -510,8 +510,6 @@ Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх. Эмулировать BGR565 Исправляет проблемы с инвертированными цветами в играх, а также со странными артефактами или некорректными тенями. - Включить старый метод изменения разрешения - Включает старый метод обработки этапа перенастройки масштабирования для игр за счёт использования быстрого алгоритма перемасштабирования. Использовать асинхронные шейдеры Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты. Настройки распаковки текстур (Unswizzle) diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 0109943222..f2bfa5f7ea 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -505,8 +505,6 @@ Зменшує розмиття світіння в LA/EOW (Adreno A6XX–A7XX / Turnip), прибирає світіння в Burnout. Увага: може спричинити графічні артефакти в інших іграх. Емулювати BGR565 Виправляє проблеми з інвертованими кольорами в іграх або дивними артефактами чи тінями. - Увімкнути застаріле масштабування - Вмикає застарілу обробку масштабування для ігор, використовуючи швидкий шлях масштабування Асинхронні шейдери Компілює шейдери асинхронно. Це може зменшити затримки, але також може спричинити графічні баги. Налаштування розпакування за допомогою ГП diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 7af46a99dd..801b34d780 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -504,8 +504,6 @@ 减少《智慧的再现》和《众神的三角力量2》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。 模拟 BGR565 修复游戏中的颜色反转或是异常的画面瑕疵或阴影问题 - 启用旧版缩放处理 - 启用通过使用快速缩放路径,来为游戏提供缩放配置处理的传统处理方式 使用异步着色器 以异步方式编译着色器。采用此方式或可减少卡顿,但也可能引入故障点。 GPU Unswizzle 设置 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 052a96eaf6..62532d2e09 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -581,8 +581,6 @@ Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. Emulate BGR565 Fixes problems with inverted colors in games or strange artifacts or strange shadows. - Enable Legacy Rescale Pass - Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path Use asynchronous shaders Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches. GPU Unswizzle Settings From 5b1613087c9c9a53833b4dd3db82b49de54efa12 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 01:07:34 -0400 Subject: [PATCH 57/72] Changes requested by reviewer meower --- src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | 6 +++--- .../frontend/maxwell/translate_program.cpp | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index ede658c50f..6d6294e06d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -212,12 +212,12 @@ private: Kernel::KEvent* unknown_event; // ZBC Tables - static constexpr u32 zbc_table_size = 15u; + static constexpr u32 ZBC_TABLE_SIZE = 15u; static constexpr u32 supported_types = 2u; std::mutex zbc_mutex{}; - std::array zbc_colors{}; - std::array zbc_depths{}; + std::array zbc_colors{}; + std::array zbc_depths{}; u32 zbc_used_color_entries{}; u32 zbc_used_depth_entries{}; }; diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 44635a1425..0bb404b78b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -472,8 +472,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run program.info.legacy_stores_mapping = GenerateLegacyToGenericMappings(stores, unused_output_generics, {}); for (IR::Block* const block : program.post_order_blocks) { - auto it{block->begin()}; - while (it != block->end()) { + for (auto it = block->begin(); it != block->end();) { IR::Inst& inst{*it}; if (inst.GetOpcode() != IR::Opcode::SetAttribute || !IsLegacyAttribute(inst.Arg(0).Attribute())) { From 9f776cfca7f44a2666eccca958e8a3053d2ea1a9 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 01:22:01 -0400 Subject: [PATCH 58/72] [TEST] Adjustments on color write enable --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 10 ++++++++-- src/video_core/vulkan_common/vulkan_device.cpp | 3 --- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index eeb58b9919..f22d1e1ae8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1877,13 +1877,19 @@ void RasterizerVulkan::UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& r if (!state_tracker.TouchColorMask()) { return; } + size_t num_attachments{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) { + num_attachments = index + 1; + } + } std::array setup_enables{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE; } - scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorWriteEnableEXT(setup_enables); + scheduler.Record([setup_enables, num_attachments](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteEnableEXT(vk::Span(setup_enables.data(), num_attachments)); }); } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 4011656d88..f398fbccbc 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -523,9 +523,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation."); has_broken_descriptor_aliasing = true; LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing."); - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable."); - RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable, - VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64."); RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); From b0b481716270a04c2d76c4515f4e830a9fbc58c7 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 02:49:06 -0400 Subject: [PATCH 59/72] [TEST] Adjustments on MSAA --- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 25 +++++++---- src/video_core/host_shaders/CMakeLists.txt | 42 +++++++++++++++++-- .../convert_msaa_to_non_msaa.frag | 11 ++++- .../convert_msaa_to_non_msaa_sint.frag | 22 ---------- .../convert_msaa_to_non_msaa_uint.frag | 22 ---------- .../convert_non_msaa_to_msaa.frag | 11 ++++- .../convert_non_msaa_to_msaa_sint.frag | 21 ---------- .../convert_non_msaa_to_msaa_uint.frag | 21 ---------- src/video_core/renderer_vulkan/blit_image.cpp | 1 + .../renderer_vulkan/vk_compute_pass.cpp | 32 ++++---------- .../renderer_vulkan/vk_compute_pass.h | 2 +- .../renderer_vulkan/vk_render_pass_cache.cpp | 41 ++++++++++++++++-- src/video_core/texture_cache/texture_cache.h | 12 ++++-- 13 files changed, 128 insertions(+), 135 deletions(-) delete mode 100644 src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag delete mode 100644 src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag delete mode 100644 src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag delete mode 100644 src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index b5a4e6a670..11f73d5130 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -295,7 +295,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { break; } - if (zbc_used_color_entries >= zbc_table_size) { + if (zbc_used_color_entries >= ZBC_TABLE_SIZE) { LOG_WARNING(Service_NVDRV, "ZBCSetTable: color table is full, fmt={:#x}", params.format); return NvResult::InsufficientMemory; @@ -324,7 +324,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { break; } - if (zbc_used_depth_entries >= zbc_table_size) { + if (zbc_used_depth_entries >= ZBC_TABLE_SIZE) { LOG_WARNING(Service_NVDRV, "ZBCSetTable: depth table is full, fmt={:#x}", depth_entry.format); return NvResult::InsufficientMemory; @@ -350,32 +350,41 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) { std::scoped_lock lk(zbc_mutex); if (params.type == 0) { - params.index_size = zbc_table_size; + params.index_size = ZBC_TABLE_SIZE; return NvResult::Success; } - if (params.index_size >= zbc_table_size) { - LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid index {:#x}", params.index_size); - return NvResult::BadParameter; - } - switch (static_cast(params.type)) { case ZBCTypes::color: { + if (params.index_size >= zbc_used_color_entries) { + LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid color index {:#x}", + params.index_size); + return NvResult::BadParameter; + } + const auto& colors = zbc_colors[params.index_size]; std::copy_n(colors.color_ds.begin(), colors.color_ds.size(), std::begin(params.color_ds)); std::copy_n(colors.color_l2.begin(), colors.color_l2.size(), std::begin(params.color_l2)); params.depth = 0; params.ref_cnt = colors.ref_cnt; params.format = colors.format; + params.index_size = zbc_used_color_entries; break; } case ZBCTypes::depth: { + if (params.index_size >= zbc_used_depth_entries) { + LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid depth index {:#x}", + params.index_size); + return NvResult::BadParameter; + } + const auto& depth_entry = zbc_depths[params.index_size]; std::fill(std::begin(params.color_ds), std::end(params.color_ds), 0); std::fill(std::begin(params.color_l2), std::end(params.color_l2), 0); params.depth = depth_entry.depth; params.ref_cnt = depth_entry.ref_cnt; params.format = depth_entry.format; + params.index_size = zbc_used_depth_entries; break; } } diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 2cfdbf5d97..f8b64c57e8 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -35,12 +35,8 @@ set(SHADER_FILES ${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 ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag - ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_sint.frag - ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_uint.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag @@ -174,6 +170,44 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES}) endif() endforeach() +# Integer variants of the MSAA conversion shaders. They only differ from the float +# source in the sampler and output types, so they are generated from it via defines. +set(SHADER_TYPE_VARIANTS + "convert_msaa_to_non_msaa.frag|sint|isampler2DMS|ivec4" + "convert_msaa_to_non_msaa.frag|uint|usampler2DMS|uvec4" + "convert_non_msaa_to_msaa.frag|sint|isampler2D|ivec4" + "convert_non_msaa_to_msaa.frag|uint|usampler2D|uvec4" +) + +foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS}) + string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT}) + list(GET VARIANT_PARTS 0 VARIANT_FILENAME) + list(GET VARIANT_PARTS 1 VARIANT_SUFFIX) + list(GET VARIANT_PARTS 2 VARIANT_SAMPLER) + list(GET VARIANT_PARTS 3 VARIANT_TEXEL) + + set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME}) + get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE) + get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT) + string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT}) + set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT}) + + string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME) + set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h) + add_custom_command( + OUTPUT + ${VARIANT_HEADER_FILE} + COMMAND + ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} + -DSAMPLER_TYPE=${VARIANT_SAMPLER} -DTEXEL_TYPE=${VARIANT_TEXEL} + --variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE} + --target-env ${SPIR_V_VERSION} + MAIN_DEPENDENCY + ${VARIANT_SOURCE} + ) + set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE}) +endforeach() + foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES}) get_filename_component(FILENAME ${FILEPATH} NAME) string(REPLACE "." "_" HEADER_NAME ${FILENAME}) diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag index 6c4aac3c82..8f7ec34d1a 100644 --- a/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag @@ -3,7 +3,14 @@ #version 450 core -layout(binding = 0) uniform sampler2DMS msaa_in; +#ifndef SAMPLER_TYPE +#define SAMPLER_TYPE sampler2DMS +#endif +#ifndef TEXEL_TYPE +#define TEXEL_TYPE vec4 +#endif + +layout(binding = 0) uniform SAMPLER_TYPE msaa_in; layout(push_constant) uniform PushConstants { ivec2 dst_offset; @@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants { ivec2 scale; }; -layout(location = 0) out vec4 frag_color; +layout(location = 0) out TEXEL_TYPE frag_color; void main() { const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset; diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag deleted file mode 100644 index fedd064cb0..0000000000 --- a/src/video_core/host_shaders/convert_msaa_to_non_msaa_sint.frag +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#version 450 core - -layout(binding = 0) uniform isampler2DMS msaa_in; - -layout(push_constant) uniform PushConstants { - ivec2 dst_offset; - ivec2 src_offset; - ivec2 scale; -}; - -layout(location = 0) out ivec4 frag_color; - -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; - frag_color = texelFetch(msaa_in, msaa_coord, sample_id); -} diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag deleted file mode 100644 index ec692752a0..0000000000 --- a/src/video_core/host_shaders/convert_msaa_to_non_msaa_uint.frag +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#version 450 core - -layout(binding = 0) uniform usampler2DMS msaa_in; - -layout(push_constant) uniform PushConstants { - ivec2 dst_offset; - ivec2 src_offset; - ivec2 scale; -}; - -layout(location = 0) out uvec4 frag_color; - -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; - frag_color = texelFetch(msaa_in, msaa_coord, sample_id); -} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag index 19e23849d2..b5a93068a9 100644 --- a/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag @@ -3,7 +3,14 @@ #version 450 core -layout(binding = 0) uniform sampler2D img_in; +#ifndef SAMPLER_TYPE +#define SAMPLER_TYPE sampler2D +#endif +#ifndef TEXEL_TYPE +#define TEXEL_TYPE vec4 +#endif + +layout(binding = 0) uniform SAMPLER_TYPE img_in; layout(push_constant) uniform PushConstants { ivec2 dst_offset; @@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants { ivec2 scale; }; -layout(location = 0) out vec4 frag_color; +layout(location = 0) out TEXEL_TYPE frag_color; void main() { const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag deleted file mode 100644 index 491c1bf5cc..0000000000 --- a/src/video_core/host_shaders/convert_non_msaa_to_msaa_sint.frag +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#version 450 core - -layout(binding = 0) uniform isampler2D img_in; - -layout(push_constant) uniform PushConstants { - ivec2 dst_offset; - ivec2 src_offset; - ivec2 scale; -}; - -layout(location = 0) out ivec4 frag_color; - -void main() { - const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; - const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); - const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; - frag_color = texelFetch(img_in, coord, 0); -} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag deleted file mode 100644 index 08eb0cc843..0000000000 --- a/src/video_core/host_shaders/convert_non_msaa_to_msaa_uint.frag +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#version 450 core - -layout(binding = 0) uniform usampler2D img_in; - -layout(push_constant) uniform PushConstants { - ivec2 dst_offset; - ivec2 src_offset; - ivec2 scale; -}; - -layout(location = 0) out uvec4 frag_color; - -void main() { - const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; - const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); - const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; - frag_color = texelFetch(img_in, coord, 0); -} diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 09ffa4df12..b4500f34f8 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1815,6 +1815,7 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage } } } + scheduler.InvalidateState(); } VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) { diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index d1effe2c6c..f5c0c50768 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -615,7 +615,6 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier); }); - scheduler.Finish(); } constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0; @@ -720,17 +719,14 @@ void BlockLinearUnswizzle3DPass::Unswizzle( const u32 blocks_x = (image.info.size.width + 3) / 4; const u32 blocks_y = (image.info.size.height + 3) / 4; - const bool is_initialized = image.ExchangeInitialization(); - scheduler.RequestOutsideRenderPassOperationContext(); for (u32 z_offset = 0; z_offset < z_count; z_offset += MAX_BATCH_SLICES) { const u32 current_chunk_slices = (std::min)(MAX_BATCH_SLICES, z_count - z_offset); const u32 current_z_start = z_start + z_offset; UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, - current_z_start, current_chunk_slices, is_initialized); + current_z_start, current_chunk_slices); } - scheduler.Finish(); } void BlockLinearUnswizzle3DPass::UnswizzleChunk( @@ -739,7 +735,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const VideoCommon::SwizzleParameters& sw, const BlockLinearSwizzle3DParams& params, u32 blocks_x, u32 blocks_y, - u32 z_start, u32 z_count, bool is_initialized) + u32 z_start, u32 z_count) { BlockLinearUnswizzle3DPushConstants pc{}; pc.origin[0] = params.origin[0]; @@ -783,7 +779,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const VkDeviceSize barrier_size = output_slice_size * z_count; const bool is_first_chunk = (z_start == 0); - const bool use_undefined_layout = !is_initialized && is_first_chunk; const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkImage dst_image = image.Handle(); @@ -792,7 +787,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const u32 image_height = image.info.size.height; scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, z_start, z_count, - barrier_size, use_undefined_layout, out_buffer, dst_image, aspect, + barrier_size, is_first_chunk, out_buffer, dst_image, aspect, image_width, image_height ](vk::CommandBuffer cmdbuf) { @@ -819,23 +814,14 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .size = barrier_size, }; - VkAccessFlags pre_barrier_src_access = VK_ACCESS_SHADER_READ_BIT; - VkImageLayout pre_barrier_old_layout = VK_IMAGE_LAYOUT_GENERAL; - VkPipelineStageFlags pre_barrier_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - if (use_undefined_layout) { - pre_barrier_src_access = VkAccessFlags{}; - pre_barrier_old_layout = VK_IMAGE_LAYOUT_UNDEFINED; - pre_barrier_src_stages |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } else { - pre_barrier_src_stages |= vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; - } - const VkImageMemoryBarrier pre_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = pre_barrier_src_access, + .srcAccessMask = is_first_chunk ? VkAccessFlags{} : + static_cast(VK_ACCESS_TRANSFER_WRITE_BIT), .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = pre_barrier_old_layout, + .oldLayout = is_first_chunk ? VK_IMAGE_LAYOUT_UNDEFINED : + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -845,7 +831,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( // Single barrier handles both buffer and image cmdbuf.PipelineBarrier( - pre_barrier_src_stages, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, buffer_barrier, pre_barrier @@ -1122,7 +1108,6 @@ void BlockLinearUnswizzle2DPass::Unswizzle( cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, post_copy); }); - scheduler.Finish(); } namespace { @@ -1374,7 +1359,6 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, post_copy); }); - scheduler.Finish(); } } // 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 f3dc4b7697..5150e95b90 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -156,7 +156,7 @@ public: const VideoCommon::SwizzleParameters& sw, const BlockLinearSwizzle3DParams& params, u32 blocks_x, u32 blocks_y, - u32 z_start, u32 z_count, bool is_initialized); + u32 z_start, u32 z_count); private: Scheduler& scheduler; 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 a514ac9022..a00d147db5 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -229,6 +229,18 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .preserveAttachmentCount = 0, .pPreserveAttachments = nullptr, }; + const VkSubpassDependency feedback_loop_dependency{ + .srcSubpass = 0, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + }; const VkSubpassDependency counter_resume_dependency{ .srcSubpass = 0, .dstSubpass = 0, @@ -324,6 +336,23 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { counter_resume_dependency2.srcAccessMask = 0; counter_resume_dependency2.dstAccessMask = 0; } + const VkSubpassDependency2 feedback_loop_dependency2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + .pNext = nullptr, + .srcSubpass = feedback_loop_dependency.srcSubpass, + .dstSubpass = feedback_loop_dependency.dstSubpass, + .srcStageMask = feedback_loop_dependency.srcStageMask, + .dstStageMask = feedback_loop_dependency.dstStageMask, + .srcAccessMask = feedback_loop_dependency.srcAccessMask, + .dstAccessMask = feedback_loop_dependency.dstAccessMask, + .dependencyFlags = feedback_loop_dependency.dependencyFlags, + .viewOffset = 0, + }; + boost::container::static_vector dependencies2{ + feedback_loop_dependency2}; + if (can_resume_transform_feedback) { + dependencies2.push_back(counter_resume_dependency2); + } pair->second = device->GetLogical().CreateRenderPass2({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, .pNext = nullptr, @@ -332,14 +361,18 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(), .subpassCount = 1, .pSubpasses = &subpass2, - .dependencyCount = can_resume_transform_feedback ? 1u : 0u, - .pDependencies = can_resume_transform_feedback ? &counter_resume_dependency2 : nullptr, + .dependencyCount = static_cast(dependencies2.size()), + .pDependencies = dependencies2.data(), .correlatedViewMaskCount = 0, .pCorrelatedViewMasks = nullptr, }); return *pair->second; } + boost::container::static_vector dependencies{feedback_loop_dependency}; + if (can_resume_transform_feedback) { + dependencies.push_back(counter_resume_dependency); + } pair->second = device->GetLogical().CreateRenderPass({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = nullptr, @@ -348,8 +381,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .pAttachments = descriptions.empty() ? nullptr : descriptions.data(), .subpassCount = 1, .pSubpasses = &subpass, - .dependencyCount = can_resume_transform_feedback ? 1u : 0u, - .pDependencies = can_resume_transform_feedback ? &counter_resume_dependency : nullptr, + .dependencyCount = static_cast(dependencies.size()), + .pDependencies = dependencies.data(), }); return *pair->second; } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index e3e9e213a8..92c034d4cd 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -279,11 +279,12 @@ void TextureCache

::CheckFeedbackLoop(std::span views) { const ImageId view_image_id = slot_image_views[view.id].image_id; { - bool is_continue = false; + bool aliases_color_rt = false; for (size_t i = 0; i < 8; ++i) - is_continue |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i]; - if (is_continue) - continue; + aliases_color_rt |= + (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i]; + if (aliases_color_rt) + return true; } if (depth_active && view_image_id == rt_depth_image_id) { return true; @@ -1710,6 +1711,9 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA if (!overlap.IsSafeGpuCopy()) { continue; } + if (overlap.info.num_samples != new_image.info.num_samples) { + continue; + } const auto alias_pointer = join_alias_indices.find(copy_object.id); if (alias_pointer == join_alias_indices.end()) { continue; From a6d49872a14e7dd44f0c94528abec14becfe4832 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 03:34:01 -0400 Subject: [PATCH 60/72] [TEST] Adjustments on the pipeline cache + optimized vertex buffer refinement --- src/video_core/buffer_cache/buffer_cache.h | 12 +++++----- .../renderer_vulkan/vk_compute_pipeline.cpp | 5 ++++- .../renderer_vulkan/vk_compute_pipeline.h | 3 +++ .../renderer_vulkan/vk_graphics_pipeline.cpp | 7 ++++-- .../renderer_vulkan/vk_graphics_pipeline.h | 5 ++++- .../renderer_vulkan/vk_pipeline_cache.cpp | 22 +++++++++++++------ .../renderer_vulkan/vk_pipeline_cache.h | 3 +++ 7 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 95c67f865a..127a23637f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -811,7 +811,7 @@ void BufferCache

::BindHostVertexBuffers() { if (use_optimized_vertex_buffers) { auto& flags = maxwell3d->dirty.flags; const u32 enabled_mask = enabled_vertex_buffers_mask; - bool any_dirty = false; + u32 dirty_mask = 0; u32 pending_mask = enabled_mask; while (pending_mask != 0) { const u32 index = std::countr_zero(pending_mask); @@ -820,13 +820,15 @@ void BufferCache

::BindHostVertexBuffers() { Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); SynchronizeBuffer(buffer, binding.device_addr, binding.size); - any_dirty |= flags[Dirty::VertexBuffer0 + index]; + if (flags[Dirty::VertexBuffer0 + index]) { + dirty_mask |= 1u << index; + } } - if (enabled_mask == 0 || !any_dirty) { + if (dirty_mask == 0) { return; } - const u32 min_index = static_cast(std::countr_zero(enabled_mask)); - const u32 max_index = 32u - static_cast(std::countl_zero(enabled_mask)); + const u32 min_index = static_cast(std::countr_zero(dirty_mask)); + const u32 max_index = 32u - static_cast(std::countl_zero(dirty_mask)); HostBindings bindings{}; bindings.min_index = min_index; bindings.max_index = max_index; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 29356cfcec..6c2ba89026 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -32,6 +32,7 @@ using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk::PipelineCache& pipeline_cache_, + std::shared_mutex& pipeline_cache_mutex_, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue_, DescriptorBufferRing& descriptor_buffer_ring_, @@ -40,7 +41,8 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, vk::ShaderModule spv_module_, u64 shader_hash_) : device{device_}, - pipeline_cache(pipeline_cache_), guest_descriptor_queue{guest_descriptor_queue_}, + pipeline_cache(pipeline_cache_), pipeline_cache_mutex(pipeline_cache_mutex_), + guest_descriptor_queue{guest_descriptor_queue_}, descriptor_buffer_ring{descriptor_buffer_ring_}, info{info_}, shader_hash{shader_hash_}, spv_module(std::move(spv_module_)) { if (shader_notify) { @@ -111,6 +113,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk .basePipelineIndex = 0, }; try { + std::shared_lock cache_lock{pipeline_cache_mutex}; pipeline = device.GetLogical().CreateComputePipeline(compute_ci, *pipeline_cache); } catch (const vk::Exception& exception) { LOG_CRITICAL(Render_Vulkan, "Adreno rejected compute shader {:016X}: {}", shader_hash, diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index fba0519b58..65d4bb1382 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/common_types.h" #include "common/thread_worker.h" @@ -34,6 +35,7 @@ class Scheduler; class ComputePipeline { public: explicit ComputePipeline(const Device& device, Scheduler& scheduler, vk::PipelineCache& pipeline_cache, + std::shared_mutex& pipeline_cache_mutex, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, DescriptorBufferRing& descriptor_buffer_ring, @@ -59,6 +61,7 @@ public: private: const Device& device; vk::PipelineCache& pipeline_cache; + std::shared_mutex& pipeline_cache_mutex; GuestDescriptorQueue& guest_descriptor_queue; DescriptorBufferRing& descriptor_buffer_ring; Shader::Info info; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index a2eee7f16d..554cc038ef 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -251,7 +251,8 @@ ConfigureFuncPtr ConfigureFunc(const std::array& m // TODO(crueter): This is the worst-formatted code I have EVER seen GraphicsPipeline::GraphicsPipeline( Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, - vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify, + vk::PipelineCache& pipeline_cache_, std::shared_mutex& pipeline_cache_mutex_, + VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue_, DescriptorBufferRing& descriptor_buffer_ring_, Common::ThreadWorker* worker_thread, @@ -259,7 +260,8 @@ GraphicsPipeline::GraphicsPipeline( const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, - pipeline_cache(pipeline_cache_), scheduler{scheduler_}, + pipeline_cache(pipeline_cache_), pipeline_cache_mutex(pipeline_cache_mutex_), + scheduler{scheduler_}, guest_descriptor_queue{guest_descriptor_queue_}, descriptor_buffer_ring{descriptor_buffer_ring_}, spv_modules{std::move(stages)} { if (shader_notify) { @@ -1081,6 +1083,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; } + std::shared_lock cache_lock{pipeline_cache_mutex}; pipeline = device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index b1ac5a3fcc..ebb92a8f16 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -78,7 +79,8 @@ class GraphicsPipeline { public: explicit GraphicsPipeline( Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, - vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify, + vk::PipelineCache& pipeline_cache, std::shared_mutex& pipeline_cache_mutex, + VideoCore::ShaderNotify* shader_notify, const Device& device, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, DescriptorBufferRing& descriptor_buffer_ring, Common::ThreadWorker* worker_thread, @@ -151,6 +153,7 @@ private: TextureCache& texture_cache; BufferCache& buffer_cache; vk::PipelineCache& pipeline_cache; + std::shared_mutex& pipeline_cache_mutex; Scheduler& scheduler; GuestDescriptorQueue& guest_descriptor_queue; DescriptorBufferRing& descriptor_buffer_ring; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 96007ec276..d1948fa59b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -553,6 +553,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, PipelineCache::~PipelineCache() { if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) { + std::unique_lock lock{vulkan_pipeline_cache_mutex}; SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); } @@ -612,6 +613,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading if (use_vulkan_pipeline_cache) { vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin"; + std::unique_lock lock{vulkan_pipeline_cache_mutex}; vulkan_pipeline_cache = LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION); } @@ -710,6 +712,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.WaitForRequests(stop_loading); if (use_vulkan_pipeline_cache) { + std::unique_lock lock{vulkan_pipeline_cache_mutex}; SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); size_t size = 0; @@ -743,11 +746,14 @@ void PipelineCache::QueueVulkanPipelineCacheFlush() { pipelines_since_flush = 0; last_flush = now; serialization_thread.QueueWork([this] { - SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, - CACHE_VERSION); - size_t size = 0; - vulkan_pipeline_cache.Read(&size, nullptr); - last_cache_size.store(size, std::memory_order_relaxed); + { + std::unique_lock lock{vulkan_pipeline_cache_mutex}; + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); + size_t size = 0; + vulkan_pipeline_cache.Read(&size, nullptr); + last_cache_size.store(size, std::memory_order_relaxed); + } flush_in_flight.store(false, std::memory_order_release); }); } @@ -890,7 +896,8 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique( - scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, + scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, + vulkan_pipeline_cache_mutex, &shader_notify, device, descriptor_pool, guest_descriptor_queue, descriptor_buffer_ring, thread_worker, statistics, render_pass_cache, key, std::move(modules), infos); @@ -1014,7 +1021,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( spv_module.SetObjectNameEXT(name.c_str()); } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique(device, scheduler, vulkan_pipeline_cache, descriptor_pool, + return std::make_unique(device, scheduler, vulkan_pipeline_cache, + vulkan_pipeline_cache_mutex, descriptor_pool, guest_descriptor_queue, descriptor_buffer_ring, thread_worker, statistics, &shader_notify, program.info, std::move(spv_module), diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 9d66b663f5..94cf733890 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -175,6 +177,7 @@ private: std::filesystem::path vulkan_pipeline_cache_filename; vk::PipelineCache vulkan_pipeline_cache; + std::shared_mutex vulkan_pipeline_cache_mutex; size_t pipelines_since_flush{}; std::chrono::steady_clock::time_point last_flush{}; std::atomic last_cache_size{}; From 06ac01adb01bb2da2fbb7f1610e0e9ca53632d61 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 03:41:54 -0400 Subject: [PATCH 61/72] [TEST] MSAA unification paths on MSAA and DepthMSAA --- src/video_core/renderer_vulkan/blit_image.cpp | 376 +++++++----------- src/video_core/renderer_vulkan/blit_image.h | 21 + 2 files changed, 163 insertions(+), 234 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index b4500f34f8..99add8735b 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -999,52 +999,36 @@ 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 copies, - bool msaa_to_non_msaa) { +void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, + VkPipelineLayout layout, VkImage dst_image, + VkFormat dst_vk_format, VkImage src_image, + VkFormat src_vk_format, s32 scale_x, s32 scale_y, + std::span copies, + const MSAACopyAspectInfo& aspect_info, bool copy_stencil) { 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 = - 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, - .format_class = FormatClass(dst_format), - }; - 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) { const s32 num_layers = (std::min)(copy.src_subresource.num_layers, copy.dst_subresource.num_layers); for (s32 layer = 0; layer < num_layers; ++layer) { + const u32 src_level = static_cast(copy.src_subresource.base_level); + const u32 src_layer = static_cast(copy.src_subresource.base_layer + layer); vk::ImageView src_view = - MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, - static_cast(copy.src_subresource.base_level), - static_cast(copy.src_subresource.base_layer + layer), - VK_IMAGE_ASPECT_COLOR_BIT); + MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level, + src_layer, aspect_info.src_view_aspect); + vk::ImageView src_stencil_view; + if (copy_stencil) { + src_stencil_view = + MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level, + src_layer, VK_IMAGE_ASPECT_STENCIL_BIT); + } vk::ImageView dst_view = MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, static_cast(copy.dst_subresource.base_level), static_cast(copy.dst_subresource.base_layer + layer), - VK_IMAGE_ASPECT_COLOR_BIT); + aspect_info.attachment_aspect); 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{ @@ -1067,13 +1051,17 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .src_offset = {copy.src_offset.x, copy.src_offset.y}, .scale = {scale_x, scale_y}, }; + VkImageView src_stencil_handle = VK_NULL_HANDLE; + if (copy_stencil) { + src_stencil_handle = *src_stencil_view; + } 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, + src_stencil_handle, src = src_image, dst = dst_image, render_area, + aspect_info, push_constants](vk::CommandBuffer cmdbuf) { + const VkImageSubresourceRange barrier_range{ + .aspectMask = aspect_info.barrier_aspect, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, @@ -1083,38 +1071,30 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i 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, + .srcAccessMask = aspect_info.pre_src_access, + .dstAccessMask = aspect_info.pre_src_dst_access, .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, + .subresourceRange = barrier_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, + .srcAccessMask = aspect_info.pre_src_access, + .dstAccessMask = aspect_info.pre_dst_dst_access, .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, + .subresourceRange = barrier_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); + cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0, + nullptr, nullptr, pre_barriers); const VkRenderPassBeginInfo renderpass_bi{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .pNext = nullptr, @@ -1125,8 +1105,15 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .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); + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; + if (src_stencil_handle != VK_NULL_HANDLE) { + descriptor_set = two_textures_descriptor_allocator.Commit(); + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle, + src_stencil_handle); + } else { + 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); @@ -1146,20 +1133,17 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i 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, + .srcAccessMask = aspect_info.post_src_access, + .dstAccessMask = aspect_info.post_dst_access, .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, + .subresourceRange = barrier_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); + cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0, + post_barrier); }); msaa_copy_resources.push_back(MSAACopyResources{ .tick = scheduler.CurrentTick(), @@ -1167,11 +1151,72 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .dst_view = std::move(dst_view), .framebuffer = std::move(framebuffer), }); + if (copy_stencil) { + msaa_copy_resources.push_back(MSAACopyResources{ + .tick = scheduler.CurrentTick(), + .src_view = std::move(src_stencil_view), + .dst_view = vk::ImageView{}, + .framebuffer = vk::Framebuffer{}, + }); + } } } 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 copies, + bool msaa_to_non_msaa) { + 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; + VkSampleCountFlagBits samples = SampleCountFlag(num_samples); + if (msaa_to_non_msaa) { + samples = VK_SAMPLE_COUNT_1_BIT; + } + 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, + .format_class = FormatClass(dst_format), + }; + const MSAACopyAspectInfo aspect_info{ + .src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_dst_dst_access = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + .pre_dst_stages = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + }; + 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; + CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout, + dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies, + aspect_info, false); +} + void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { const VkPipelineLayout layout = *one_texture_pipeline_layout; @@ -1625,14 +1670,13 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage VideoCore::Surface::PixelFormat src_format, u32 num_samples, std::span copies, 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 = - msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples); + VkSampleCountFlagBits samples = SampleCountFlag(num_samples); + if (msaa_to_non_msaa) { + samples = VK_SAMPLE_COUNT_1_BIT; + } RenderPassKey renderpass_key{}; renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid); renderpass_key.depth_format = dst_format; @@ -1644,178 +1688,42 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage .msaa_to_non_msaa = msaa_to_non_msaa, .format_class = MSAACopyFormatClass::Float, }; - const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil); - const VkPipelineLayout layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_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; VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT; if (VideoCore::Surface::GetFormatType(dst_format) == VideoCore::Surface::SurfaceType::DepthStencil) { attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; } - for (const VideoCommon::ImageCopy& copy : copies) { - const s32 num_layers = (std::min)(copy.src_subresource.num_layers, - copy.dst_subresource.num_layers); - for (s32 layer = 0; layer < num_layers; ++layer) { - vk::ImageView src_view = - MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, - static_cast(copy.src_subresource.base_level), - static_cast(copy.src_subresource.base_layer + layer), - VK_IMAGE_ASPECT_DEPTH_BIT); - vk::ImageView src_stencil_view = - copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, - static_cast(copy.src_subresource.base_level), - static_cast(copy.src_subresource.base_layer + layer), - VK_IMAGE_ASPECT_STENCIL_BIT) - : vk::ImageView{}; - vk::ImageView dst_view = - MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, - static_cast(copy.dst_subresource.base_level), - static_cast(copy.dst_subresource.base_layer + layer), - attachment_aspect); - 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(dst_offset.x) + dst_extent.width, - .height = static_cast(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(); - const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE; - scheduler.Record([this, pipeline, layout, sampler, renderpass, - framebuffer_handle = *framebuffer, src_view_handle = *src_view, - src_stencil_handle, src = src_image, dst = dst_image, render_area, - attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) { - const VkImageSubresourceRange src_range{ - .aspectMask = attachment_aspect, - .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_DEPTH_STENCIL_ATTACHMENT_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 = src_range, - }, - VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_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 = src_range, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_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 = - src_stencil_handle != VK_NULL_HANDLE - ? two_textures_descriptor_allocator.Commit() - : one_texture_descriptor_allocator.Commit(); - if (src_stencil_handle != VK_NULL_HANDLE) { - UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle, - src_stencil_handle); - } else { - 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(render_area.offset.x), - .y = static_cast(render_area.offset.y), - .width = static_cast(render_area.extent.width), - .height = static_cast(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_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_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 = src_range, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 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), - }); - if (copy_stencil) { - msaa_copy_resources.push_back(MSAACopyResources{ - .tick = scheduler.CurrentTick(), - .src_view = std::move(src_stencil_view), - .dst_view = vk::ImageView{}, - .framebuffer = vk::Framebuffer{}, - }); - } - } + VkPipelineLayout layout = *msaa_copy_pipeline_layout; + if (copy_stencil) { + layout = *msaa_copy_depth_stencil_pipeline_layout; } - scheduler.InvalidateState(); + const MSAACopyAspectInfo aspect_info{ + .src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT, + .attachment_aspect = attachment_aspect, + .barrier_aspect = attachment_aspect, + .pre_src_access = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + .pre_dst_stages = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + }; + 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; + CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout, + dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies, + aspect_info, copy_stencil); } VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) { diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index d4fdf9bc13..cc6dc421ed 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -136,6 +136,27 @@ public: bool msaa_to_non_msaa); private: + struct MSAACopyAspectInfo { + VkImageAspectFlags src_view_aspect; + VkImageAspectFlags attachment_aspect; + VkImageAspectFlags barrier_aspect; + VkAccessFlags pre_src_access; + VkAccessFlags pre_src_dst_access; + VkAccessFlags pre_dst_dst_access; + VkPipelineStageFlags pre_src_stages; + VkPipelineStageFlags pre_dst_stages; + VkAccessFlags post_src_access; + VkAccessFlags post_dst_access; + VkPipelineStageFlags post_src_stages; + VkPipelineStageFlags post_dst_stages; + }; + + void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, + VkImage dst_image, VkFormat dst_vk_format, VkImage src_image, + VkFormat src_vk_format, s32 scale_x, s32 scale_y, + std::span copies, + const MSAACopyAspectInfo& aspect_info, bool copy_stencil); + void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view); From e0d43180420becfb53aabfa3099a29097626941b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 03:51:12 -0400 Subject: [PATCH 62/72] Fix build --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d1948fa59b..07e4b4d378 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -712,7 +712,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.WaitForRequests(stop_loading); if (use_vulkan_pipeline_cache) { - std::unique_lock lock{vulkan_pipeline_cache_mutex}; + std::unique_lock cache_lock{vulkan_pipeline_cache_mutex}; SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); size_t size = 0; From 518f571fcf0f87bb97e93b29dfba2e204187e218 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 04:42:36 -0400 Subject: [PATCH 63/72] [TEST] Save space with Sync2 --- src/video_core/renderer_vulkan/blit_image.cpp | 80 +++++----- src/video_core/renderer_vulkan/blit_image.h | 19 +-- .../renderer_vulkan/vk_rasterizer.cpp | 16 +- .../renderer_vulkan/vk_render_pass_cache.cpp | 4 - .../renderer_vulkan/vk_scheduler.cpp | 69 +++++---- src/video_core/vulkan_common/vulkan_device.h | 11 +- .../vulkan_common/vulkan_wrapper.cpp | 72 +++++++++ src/video_core/vulkan_common/vulkan_wrapper.h | 140 +++++++++--------- 8 files changed, 252 insertions(+), 159 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 99add8735b..6b94dfa70b 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1068,10 +1068,12 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .layerCount = VK_REMAINING_ARRAY_LAYERS, }; const std::array pre_barriers{ - VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, + .srcStageMask = aspect_info.pre_src_stages, .srcAccessMask = aspect_info.pre_src_access, + .dstStageMask = aspect_info.pre_src_dst_stages, .dstAccessMask = aspect_info.pre_src_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1080,10 +1082,12 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .image = src, .subresourceRange = barrier_range, }, - VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, + .srcStageMask = aspect_info.pre_src_stages, .srcAccessMask = aspect_info.pre_src_access, + .dstStageMask = aspect_info.pre_dst_dst_stages, .dstAccessMask = aspect_info.pre_dst_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1093,8 +1097,7 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .subresourceRange = barrier_range, }, }; - cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0, - nullptr, nullptr, pre_barriers); + cmdbuf.PipelineBarrier2Images(0, pre_barriers); const VkRenderPassBeginInfo renderpass_bi{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .pNext = nullptr, @@ -1130,10 +1133,12 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, 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, + const VkImageMemoryBarrier2 post_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, + .srcStageMask = aspect_info.post_src_stages, .srcAccessMask = aspect_info.post_src_access, + .dstStageMask = aspect_info.post_dst_stages, .dstAccessMask = aspect_info.post_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1142,8 +1147,7 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .image = dst, .subresourceRange = barrier_range, }; - cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0, - post_barrier); + cmdbuf.PipelineBarrier2Images(0, post_barrier); }); msaa_copy_resources.push_back(MSAACopyResources{ .tick = scheduler.CurrentTick(), @@ -1192,21 +1196,23 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT, .attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT, .barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT, - .pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT, - .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_src_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_SHADER_WRITE_BIT | + VK_ACCESS_2_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, .pre_dst_dst_access = - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, - .pre_dst_stages = - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, - .post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .pre_src_dst_stages = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + .pre_dst_dst_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_src_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_dst_stages = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_2_TRANSFER_BIT, }; const VkFormat src_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; @@ -1702,19 +1708,19 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage .attachment_aspect = attachment_aspect, .barrier_aspect = attachment_aspect, .pre_src_access = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, - .pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, - .pre_dst_stages = - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, - .post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, + .pre_dst_dst_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .pre_src_dst_stages = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + .pre_dst_dst_stages = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, + .post_src_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT | + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, .post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, }; const VkFormat src_vk_format = diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index cc6dc421ed..08cab788aa 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -140,15 +140,16 @@ private: VkImageAspectFlags src_view_aspect; VkImageAspectFlags attachment_aspect; VkImageAspectFlags barrier_aspect; - VkAccessFlags pre_src_access; - VkAccessFlags pre_src_dst_access; - VkAccessFlags pre_dst_dst_access; - VkPipelineStageFlags pre_src_stages; - VkPipelineStageFlags pre_dst_stages; - VkAccessFlags post_src_access; - VkAccessFlags post_dst_access; - VkPipelineStageFlags post_src_stages; - VkPipelineStageFlags post_dst_stages; + VkAccessFlags2 pre_src_access; + VkAccessFlags2 pre_src_dst_access; + VkAccessFlags2 pre_dst_dst_access; + VkPipelineStageFlags2 pre_src_stages; + VkPipelineStageFlags2 pre_src_dst_stages; + VkPipelineStageFlags2 pre_dst_dst_stages; + VkAccessFlags2 post_src_access; + VkAccessFlags2 post_dst_access; + VkPipelineStageFlags2 post_src_stages; + VkPipelineStageFlags2 post_dst_stages; }; void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f22d1e1ae8..04bce948f1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -363,8 +363,6 @@ void RasterizerVulkan::DrawTexture() { UpdateDynamicStates(); - query_cache.NotifySegment(true); - query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state; const auto& sampler = texture_cache.GetSampler(draw_texture_state.src_sampler, false); const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); @@ -495,10 +493,10 @@ void RasterizerVulkan::Clear(u32 layer_count) { (!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); } - query_cache.NotifySegment(true); - query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); UpdateViewportsState(regs); const u32 color_attachment = regs.clear_surface.RT; @@ -1877,19 +1875,13 @@ void RasterizerVulkan::UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& r if (!state_tracker.TouchColorMask()) { return; } - size_t num_attachments{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) { - num_attachments = index + 1; - } - } std::array setup_enables{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE; } - scheduler.Record([setup_enables, num_attachments](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorWriteEnableEXT(vk::Span(setup_enables.data(), num_attachments)); + scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteEnableEXT(setup_enables); }); } 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 a00d147db5..b992f6361c 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -331,10 +331,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { }; if (device->HasSynchronization2()) { counter_resume_dependency2.pNext = &counter_resume_barrier; - counter_resume_dependency2.srcStageMask = 0; - counter_resume_dependency2.dstStageMask = 0; - counter_resume_dependency2.srcAccessMask = 0; - counter_resume_dependency2.dstAccessMask = 0; } const VkSubpassDependency2 feedback_loop_dependency2{ .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 31e852e943..1310476c6a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -421,18 +421,21 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, - consumer_stages = device.AttachmentConsumerStages(), + shader_stages = device.ShaderConsumerStages(), has_transform_feedback = device.IsExtTransformFeedbackSupported()]( vk::CommandBuffer cmdbuf) { - static constexpr VkAccessFlags SHADER_ACCESS = - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - static constexpr VkAccessFlags COLOR_ACCESS = - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + static constexpr VkAccessFlags2 SHADER_ACCESS = + VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT; + static constexpr VkAccessFlags2 COLOR_ACCESS = + VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + static constexpr VkAccessFlags2 DEPTH_STENCIL_ACCESS = + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + static constexpr VkPipelineStageFlags2 DEPTH_STENCIL_STAGES = + VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; - std::array barriers; + std::array barriers; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; @@ -440,25 +443,37 @@ void Scheduler::EndRenderPass() & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) !=0; - VkAccessFlags src_access = 0; - VkAccessFlags dst_access = SHADER_ACCESS; + VkPipelineStageFlags2 src_stages = 0; + VkAccessFlags2 src_access = 0; + VkPipelineStageFlags2 dst_stages = shader_stages; + VkAccessFlags2 dst_access = SHADER_ACCESS; if (is_color) { - src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + src_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + src_access |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + dst_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; dst_access |= COLOR_ACCESS; } else if (is_depth_stencil) { - src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + src_stages |= DEPTH_STENCIL_STAGES; + src_access |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dst_stages |= DEPTH_STENCIL_STAGES; dst_access |= DEPTH_STENCIL_ACCESS; } else { - src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + src_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT + | DEPTH_STENCIL_STAGES; + src_access |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dst_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT + | DEPTH_STENCIL_STAGES; dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS; } - barriers[i] = VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + barriers[i] = VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, + .srcStageMask = src_stages, .srcAccessMask = src_access, + .dstStageMask = dst_stages, .dstAccessMask = dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -469,19 +484,19 @@ void Scheduler::EndRenderPass() }; } cmdbuf.EndRenderPass(); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages, - 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); + cmdbuf.PipelineBarrier2Images(0, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { - static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + static constexpr VkMemoryBarrier2 XFB_OUTPUT_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, .pNext = nullptr, - .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, - .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, + .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, + .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT | + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_2_TRANSFER_READ_BIT, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, - VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, XFB_OUTPUT_BARRIER); + cmdbuf.PipelineBarrier2Memory(0, XFB_OUTPUT_BARRIER); } }); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d32aa95668..8083e376fe 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -1027,14 +1027,11 @@ FN_MAX_LIMIT_LIST return features2.features.geometryShader; } - VkPipelineStageFlags AttachmentConsumerStages() const { + VkPipelineStageFlags ShaderConsumerStages() 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.geometryShader) { stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; @@ -1042,6 +1039,12 @@ FN_MAX_LIMIT_LIST return stages; } + VkPipelineStageFlags AttachmentConsumerStages() const { + return ShaderConsumerStages() | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + /// Returns true if the device supports VK_KHR_maintenance1. bool IsKhrMaintenance1Supported() const { return extensions.maintenance1; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 33173e956d..9013bb3c49 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "common/common_types.h" #include "common/logging.h" #include "video_core/vulkan_common/vk_enum_string_helper.h" @@ -296,6 +298,76 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec } // Anonymous namespace +void PipelineBarrierDowngrade(const DeviceDispatch& dld, VkCommandBuffer handle, + VkDependencyFlags dependency_flags, + Span memory_barriers, + Span buffer_barriers, + Span image_barriers) { + VkPipelineStageFlags2 src_stages = 0; + VkPipelineStageFlags2 dst_stages = 0; + for (const VkMemoryBarrier2& barrier : memory_barriers) { + src_stages |= barrier.srcStageMask; + dst_stages |= barrier.dstStageMask; + } + for (const VkBufferMemoryBarrier2& barrier : buffer_barriers) { + src_stages |= barrier.srcStageMask; + dst_stages |= barrier.dstStageMask; + } + for (const VkImageMemoryBarrier2& barrier : image_barriers) { + src_stages |= barrier.srcStageMask; + dst_stages |= barrier.dstStageMask; + } + + boost::container::small_vector memory; + memory.reserve(memory_barriers.size()); + for (const VkMemoryBarrier2& barrier : memory_barriers) { + memory.push_back(VkMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = DowngradeAccessMask(barrier.srcAccessMask), + .dstAccessMask = DowngradeAccessMask(barrier.dstAccessMask), + }); + } + boost::container::small_vector buffers; + buffers.reserve(buffer_barriers.size()); + for (const VkBufferMemoryBarrier2& barrier : buffer_barriers) { + buffers.push_back(VkBufferMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = DowngradeAccessMask(barrier.srcAccessMask), + .dstAccessMask = DowngradeAccessMask(barrier.dstAccessMask), + .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, + .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, + .buffer = barrier.buffer, + .offset = barrier.offset, + .size = barrier.size, + }); + } + boost::container::small_vector images; + images.reserve(image_barriers.size()); + for (const VkImageMemoryBarrier2& barrier : image_barriers) { + images.push_back(VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = DowngradeAccessMask(barrier.srcAccessMask), + .dstAccessMask = DowngradeAccessMask(barrier.dstAccessMask), + .oldLayout = barrier.oldLayout, + .newLayout = barrier.newLayout, + .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, + .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, + .image = barrier.image, + .subresourceRange = barrier.subresourceRange, + }); + } + + dld.vkCmdPipelineBarrier(handle, + DowngradeStageMask(src_stages, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + DowngradeStageMask(dst_stages, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT), + dependency_flags, static_cast(memory.size()), memory.data(), + static_cast(buffers.size()), buffers.data(), + static_cast(images.size()), images.data()); +} + bool Load(InstanceDispatch& dld) noexcept { #define X(name) Proc(dld.name, dld, #name) return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) && diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index d4e8e2aebc..f29eb176f6 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -158,6 +158,41 @@ inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_H inline constexpr VkPipelineStageFlags PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_HOST_BIT; +constexpr VkPipelineStageFlags DowngradeStageMask(VkPipelineStageFlags2 mask, + VkPipelineStageFlags empty) noexcept { + VkPipelineStageFlags result = static_cast(mask & 0xFFFFFFFFULL); + if ((mask & (VK_PIPELINE_STAGE_2_COPY_BIT | VK_PIPELINE_STAGE_2_RESOLVE_BIT | + VK_PIPELINE_STAGE_2_BLIT_BIT | VK_PIPELINE_STAGE_2_CLEAR_BIT)) != 0) { + result |= VK_PIPELINE_STAGE_TRANSFER_BIT; + } + if ((mask & VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT) != 0) { + result |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; + } + if ((mask & VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT) != 0) { + result |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + } + if ((mask & VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT) != 0) { + result |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + } + if (result == 0) { + return empty; + } + return result; +} + +constexpr VkAccessFlags DowngradeAccessMask(VkAccessFlags2 mask) noexcept { + VkAccessFlags result = static_cast(mask & 0xFFFFFFFFULL); + if ((mask & (VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT)) != 0) { + result |= VK_ACCESS_SHADER_READ_BIT; + } + if ((mask & VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT) != 0) { + result |= VK_ACCESS_SHADER_WRITE_BIT; + } + return result; +} + /// Table holding Vulkan instance function pointers. struct InstanceDispatch { @@ -1197,6 +1232,12 @@ private: const InstanceDispatch* dld = nullptr; }; +void PipelineBarrierDowngrade(const DeviceDispatch& dld, VkCommandBuffer handle, + VkDependencyFlags dependency_flags, + Span memory_barriers, + Span buffer_barriers, + Span image_barriers); + class CommandBuffer { public: CommandBuffer() noexcept = default; @@ -1347,78 +1388,45 @@ public: VkDependencyFlags dependency_flags, Span memory_barriers, Span buffer_barriers, Span image_barriers) const noexcept { - static constexpr u32 MaxBarriers = 16; - if (dld->vkCmdPipelineBarrier2 && memory_barriers.size() <= MaxBarriers && - buffer_barriers.size() <= MaxBarriers && image_barriers.size() <= MaxBarriers) { - const auto src_stage_mask2 = static_cast(src_stage_mask); - const auto dst_stage_mask2 = static_cast(dst_stage_mask); - - std::array memory_barriers2; - for (u32 i = 0; i < memory_barriers.size(); ++i) { - memory_barriers2[i] = VkMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = src_stage_mask2, - .srcAccessMask = static_cast(memory_barriers[i].srcAccessMask), - .dstStageMask = dst_stage_mask2, - .dstAccessMask = static_cast(memory_barriers[i].dstAccessMask), - }; - } - std::array buffer_barriers2; - for (u32 i = 0; i < buffer_barriers.size(); ++i) { - const auto& barrier = buffer_barriers[i]; - buffer_barriers2[i] = VkBufferMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = src_stage_mask2, - .srcAccessMask = static_cast(barrier.srcAccessMask), - .dstStageMask = dst_stage_mask2, - .dstAccessMask = static_cast(barrier.dstAccessMask), - .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, - .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, - .buffer = barrier.buffer, - .offset = barrier.offset, - .size = barrier.size, - }; - } - std::array image_barriers2; - for (u32 i = 0; i < image_barriers.size(); ++i) { - const auto& barrier = image_barriers[i]; - image_barriers2[i] = VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = src_stage_mask2, - .srcAccessMask = static_cast(barrier.srcAccessMask), - .dstStageMask = dst_stage_mask2, - .dstAccessMask = static_cast(barrier.dstAccessMask), - .oldLayout = barrier.oldLayout, - .newLayout = barrier.newLayout, - .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, - .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, - .image = barrier.image, - .subresourceRange = barrier.subresourceRange, - }; - } - const VkDependencyInfo dependency_info{ - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = dependency_flags, - .memoryBarrierCount = memory_barriers.size(), - .pMemoryBarriers = memory_barriers2.data(), - .bufferMemoryBarrierCount = buffer_barriers.size(), - .pBufferMemoryBarriers = buffer_barriers2.data(), - .imageMemoryBarrierCount = image_barriers.size(), - .pImageMemoryBarriers = image_barriers2.data(), - }; - dld->vkCmdPipelineBarrier2(handle, &dependency_info); - return; - } dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags, memory_barriers.size(), memory_barriers.data(), buffer_barriers.size(), buffer_barriers.data(), image_barriers.size(), image_barriers.data()); } + void PipelineBarrier2(VkDependencyFlags dependency_flags, + Span memory_barriers, + Span buffer_barriers, + Span image_barriers) const noexcept { + if (!dld->vkCmdPipelineBarrier2) { + PipelineBarrierDowngrade(*dld, handle, dependency_flags, memory_barriers, + buffer_barriers, image_barriers); + return; + } + const VkDependencyInfo dependency_info{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = dependency_flags, + .memoryBarrierCount = memory_barriers.size(), + .pMemoryBarriers = memory_barriers.data(), + .bufferMemoryBarrierCount = buffer_barriers.size(), + .pBufferMemoryBarriers = buffer_barriers.data(), + .imageMemoryBarrierCount = image_barriers.size(), + .pImageMemoryBarriers = image_barriers.data(), + }; + dld->vkCmdPipelineBarrier2(handle, &dependency_info); + } + + void PipelineBarrier2Images(VkDependencyFlags dependency_flags, + Span image_barriers) const noexcept { + PipelineBarrier2(dependency_flags, {}, {}, image_barriers); + } + + void PipelineBarrier2Memory(VkDependencyFlags dependency_flags, + Span memory_barriers) const noexcept { + PipelineBarrier2(dependency_flags, memory_barriers, {}, {}); + } + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkDependencyFlags dependency_flags = 0) const noexcept { PipelineBarrier(src_stage_mask, dst_stage_mask, dependency_flags, {}, {}, {}); From 385b844e2aeb3016116453a30c80432bbb288faa Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 14:58:23 -0400 Subject: [PATCH 64/72] Remove additional feedback loop checks --- .../renderer_vulkan/vk_render_pass_cache.cpp | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) 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 b992f6361c..6be0adc229 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -229,18 +229,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .preserveAttachmentCount = 0, .pPreserveAttachments = nullptr, }; - const VkSubpassDependency feedback_loop_dependency{ - .srcSubpass = 0, - .dstSubpass = 0, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, - }; const VkSubpassDependency counter_resume_dependency{ .srcSubpass = 0, .dstSubpass = 0, @@ -332,20 +320,7 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { if (device->HasSynchronization2()) { counter_resume_dependency2.pNext = &counter_resume_barrier; } - const VkSubpassDependency2 feedback_loop_dependency2{ - .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, - .pNext = nullptr, - .srcSubpass = feedback_loop_dependency.srcSubpass, - .dstSubpass = feedback_loop_dependency.dstSubpass, - .srcStageMask = feedback_loop_dependency.srcStageMask, - .dstStageMask = feedback_loop_dependency.dstStageMask, - .srcAccessMask = feedback_loop_dependency.srcAccessMask, - .dstAccessMask = feedback_loop_dependency.dstAccessMask, - .dependencyFlags = feedback_loop_dependency.dependencyFlags, - .viewOffset = 0, - }; - boost::container::static_vector dependencies2{ - feedback_loop_dependency2}; + boost::container::static_vector dependencies2; if (can_resume_transform_feedback) { dependencies2.push_back(counter_resume_dependency2); } @@ -365,7 +340,7 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { return *pair->second; } - boost::container::static_vector dependencies{feedback_loop_dependency}; + boost::container::static_vector dependencies; if (can_resume_transform_feedback) { dependencies.push_back(counter_resume_dependency); } From 7102ff8ecb51c4ed0e4db2313409b514872c6836 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 15:27:03 -0400 Subject: [PATCH 65/72] Disable msaa tiler resolve + removal dead code from branch --- .../renderer_vulkan/vk_texture_cache.cpp | 57 +++---------------- .../renderer_vulkan/vk_texture_cache.h | 10 ---- .../vulkan_common/vulkan_device.cpp | 41 ------------- src/video_core/vulkan_common/vulkan_device.h | 20 ------- 4 files changed, 9 insertions(+), 119 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 0c7f8cca77..2789dbee3e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -53,6 +53,9 @@ using VideoCore::Surface::IsPixelFormatInteger; using VideoCore::Surface::SurfaceType; namespace { +// Master switch for the tiler MSAA resolve path: resolve attachments, resolve shadows and the +// MSAA store discard that pays for them. Turning it off restores plain MSAA store behaviour. +constexpr bool ENABLE_MSAA_TILER_RESOLVE = false; constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true; @@ -3043,11 +3046,11 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, renderpass_key.depth_format = PixelFormat::Invalid; } renderpass_key.samples = samples; - const bool do_resolve_color = + const bool do_resolve_color = ENABLE_MSAA_TILER_RESOLVE && samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); renderpass_key.resolve_color = do_resolve_color; - const bool do_resolve_depth_stencil = + const bool do_resolve_depth_stencil = ENABLE_MSAA_TILER_RESOLVE && samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE && runtime.device.IsTiler() && SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format); @@ -3074,52 +3077,10 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } 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, VK_IMAGE_ASPECT_COLOR_BIT)); - resolve_shadow_images[num_resolve_shadows++] = msaa_image; - 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)); + const VkImage msaa_image = images[rt_map[index]]; + attachments.push_back(runtime.GetOrCreateResolveShadow( + msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT)); + resolve_shadow_images[num_resolve_shadows++] = msaa_image; } } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index f611c0f31b..e17a0653f6 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -243,14 +243,6 @@ 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; } @@ -278,8 +270,6 @@ private: bool has_depth{}; bool has_stencil{}; bool is_rescaled{}; - std::vector resolve_images; - std::vector resolve_image_views; std::array resolve_shadow_images{}; u32 num_resolve_shadows = 0; TextureCacheRuntime* runtime_ptr{nullptr}; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index f398fbccbc..a7c93c161c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1572,47 +1572,6 @@ void Device::SetupFamilies(VkSurfaceKHR surface) { } } -VkSampleCountFlags Device::GetSupportedSampleCounts(VkImageUsageFlags usage, - VkImageAspectFlags aspect, - bool is_integer) const { - const VkPhysicalDeviceLimits& limits = properties.properties.limits; - const bool has_color = (aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0; - const bool has_depth = (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; - const bool has_stencil = (aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; - - VkSampleCountFlags counts = ~VkSampleCountFlags{0}; - if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) { - if (has_color) { - if (is_integer) { - counts &= limits.sampledImageIntegerSampleCounts; - } else { - counts &= limits.sampledImageColorSampleCounts; - } - } - if (has_depth) { - counts &= limits.sampledImageDepthSampleCounts; - } - if (has_stencil) { - counts &= limits.sampledImageStencilSampleCounts; - } - } - if ((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) { - counts &= limits.framebufferColorSampleCounts; - } - if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) { - if (has_depth) { - counts &= limits.framebufferDepthSampleCounts; - } - if (has_stencil) { - counts &= limits.framebufferStencilSampleCounts; - } - } - if ((usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) { - counts &= limits.storageImageSampleCounts; - } - return counts; -} - bool Device::TryReserveCustomBorderColorSamplers(size_t count) const { const size_t limit = properties.custom_border_color.maxCustomBorderColorSamplers; if (limit == 0) { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 8083e376fe..51bae46409 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -459,10 +459,6 @@ FN_MAX_LIMIT_LIST return features.features.shaderStorageImageMultisample; } - /// Returns the sample counts an image with the given usage and aspect can be created with. - VkSampleCountFlags GetSupportedSampleCounts(VkImageUsageFlags usage, VkImageAspectFlags aspect, - bool is_integer) const; - /// Returns true if the device warp size can potentially be bigger than guest's warp size. bool IsWarpSizePotentiallyBiggerThanGuest() const { return is_warp_potentially_bigger; @@ -645,11 +641,6 @@ FN_MAX_LIMIT_LIST return properties.depth_stencil_resolve.supportedStencilResolveModes; } - /// Returns true if the depth and stencil aspects may resolve with different modes. - bool SupportsIndependentResolve() const { - return properties.depth_stencil_resolve.independentResolve == VK_TRUE; - } - /// Returns true if only one of the depth and stencil aspects may be resolved. bool SupportsIndependentResolveNone() const { return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE; @@ -719,11 +710,6 @@ FN_MAX_LIMIT_LIST features.custom_border_color.customBorderColorWithoutFormat; } - /// Returns how many live samplers may carry a custom border color, 0 when unknown. - u32 GetMaxCustomBorderColorSamplers() const { - return properties.custom_border_color.maxCustomBorderColorSamplers; - } - /// Takes budget for samplers carrying a custom border color, false when exhausted. bool TryReserveCustomBorderColorSamplers(size_t count) const; @@ -1039,12 +1025,6 @@ FN_MAX_LIMIT_LIST return stages; } - VkPipelineStageFlags AttachmentConsumerStages() const { - return ShaderConsumerStages() | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } - /// Returns true if the device supports VK_KHR_maintenance1. bool IsKhrMaintenance1Supported() const { return extensions.maintenance1; From 8f18e82931f364707a24cf5cfe5ce6d7c08c7f88 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 16:03:44 -0400 Subject: [PATCH 66/72] [TEST] Unify blit helpers of msaa and depth stencil msaa --- src/video_core/renderer_vulkan/blit_image.cpp | 82 +++++++++---------- src/video_core/renderer_vulkan/blit_image.h | 4 + 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 6b94dfa70b..f931a6bcd9 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -713,24 +713,30 @@ 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; +void BlitImageHelper::BlitMSAAImpl(const Framebuffer* dst_framebuffer, + const ImageView& src_image_view, const Region2D& dst_region, + const Region2D& src_region, VkPipeline pipeline, + VkImageView src_view, VkImageView src_stencil_view, + bool blit_stencil) { + VkPipelineLayout layout = *one_texture_pipeline_layout; + if (blit_stencil) { + layout = *two_textures_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); + scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view, + src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) { + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; + if (blit_stencil) { + descriptor_set = two_textures_descriptor_allocator.Commit(); + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view, + src_stencil_view); + } else { + 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); @@ -740,6 +746,18 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, scheduler.InvalidateState(); } +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(), + }; + BlitMSAAImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitColorMSAAPipeline(key), + src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false); +} + void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region) { @@ -749,35 +767,13 @@ void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, .renderpass = dst_framebuffer->RenderPass(), .samples = dst_framebuffer->Samples(), }; - const VkPipeline pipeline = FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil); - const VkPipelineLayout layout = - blit_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 = - blit_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, blit_stencil](vk::CommandBuffer cmdbuf) { - if (blit_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(); + VkImageView src_stencil_view = VK_NULL_HANDLE; + if (blit_stencil) { + src_stencil_view = src_image_view.StencilView(); + } + BlitMSAAImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), + src_image_view.DepthView(), src_stencil_view, blit_stencil); } void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view, diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 08cab788aa..32a4cd5ad2 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -152,6 +152,10 @@ private: VkPipelineStageFlags2 post_dst_stages; }; + void BlitMSAAImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline, + VkImageView src_view, VkImageView src_stencil_view, bool blit_stencil); + void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, VkImage dst_image, VkFormat dst_vk_format, VkImage src_image, VkFormat src_vk_format, s32 scale_x, s32 scale_y, From faf82ccdd72f37d2cc1b15d0c3750bf7d29690d9 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 20:13:10 -0400 Subject: [PATCH 67/72] [TEST] Unify paths on MSAA part 2 --- .../renderer_vulkan/vk_texture_cache.cpp | 102 ++++++++++++------ .../renderer_vulkan/vk_texture_cache.h | 27 ++++- 2 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2789dbee3e..59bcdc8c2a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include #include #include #include @@ -53,9 +54,7 @@ using VideoCore::Surface::IsPixelFormatInteger; using VideoCore::Surface::SurfaceType; namespace { -// Master switch for the tiler MSAA resolve path: resolve attachments, resolve shadows and the -// MSAA store discard that pays for them. Turning it off restores plain MSAA store behaviour. -constexpr bool ENABLE_MSAA_TILER_RESOLVE = false; +constexpr bool ENABLE_MSAA_TILER_RESOLVE = true; constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true; @@ -203,6 +202,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { }; } +[[nodiscard]] VkImageCreateInfo MakeMsaaScratchImageCreateInfo(const Device& device, + const ImageInfo& info, + VkImageUsageFlags usage) { + ImageInfo temp_info = info; + temp_info.num_samples = 1; + VkImageCreateInfo image_ci = MakeImageCreateInfo(device, temp_info); + image_ci.format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, info.format).format; + image_ci.usage = usage; + return image_ci; +} + [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, const ImageInfo& info, std::span view_formats, std::optional format_override = {}) { @@ -1805,9 +1816,54 @@ void TextureCacheRuntime::FlushDeferredClear() { scheduler.FlushDeferredClear(); } +VkImage TextureCacheRuntime::AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci) { + const MsaaScratchKey key{ + .format = image_ci.format, + .type = image_ci.imageType, + .width = image_ci.extent.width, + .height = image_ci.extent.height, + .depth = image_ci.extent.depth, + .levels = image_ci.mipLevels, + .layers = image_ci.arrayLayers, + .usage = image_ci.usage, + .flags = image_ci.flags, + }; + for (MsaaScratchImage& scratch : msaa_scratch_images) { + if (scratch.key != key || !scheduler.IsFree(scratch.tick)) { + continue; + } + scratch.tick = (std::numeric_limits::max)(); + scratch.unused_frames = 0; + return *scratch.image; + } + MsaaScratchImage scratch{ + .key = key, + .image = memory_allocator.CreateImage(image_ci), + .tick = (std::numeric_limits::max)(), + .unused_frames = 0, + }; + const VkImage handle = *scratch.image; + msaa_scratch_images.push_back(std::move(scratch)); + return handle; +} + +void TextureCacheRuntime::ReleaseMsaaScratchImage(VkImage image) { + for (MsaaScratchImage& scratch : msaa_scratch_images) { + if (*scratch.image == image) { + scratch.tick = scheduler.CurrentTick(); + return; + } + } +} + void TextureCacheRuntime::TickFrame() { - std::erase_if(pending_msaa_images, [this](const auto& pending) { - return scheduler.IsFree(pending.first); + static constexpr u32 MAX_UNUSED_SCRATCH_FRAMES = 60; + std::erase_if(msaa_scratch_images, [this](MsaaScratchImage& scratch) { + if (!scheduler.IsFree(scratch.tick)) { + scratch.unused_frames = 0; + return false; + } + return ++scratch.unused_frames > MAX_UNUSED_SCRATCH_FRAMES; }); std::erase_if(pending_resolve_shadows, [this](const auto& pending) { return scheduler.IsFree(pending.first); @@ -1944,20 +2000,13 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, upload_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; } } - ImageInfo temp_info = info; - temp_info.num_samples = 1; - - VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info); - 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); + const VkImageCreateInfo image_ci = MakeMsaaScratchImageCreateInfo( + runtime->device, info, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + const VkImage temp_vk_image = runtime->AcquireMsaaScratchImage(image_ci); scheduler->RequestOutsideRenderPassOperationContext(); auto vk_copies = TransformBufferImageCopies(copies, offset, upload_aspect_mask); const VkBuffer src_buffer = buffer; - const VkImage temp_vk_image = *temp_image; const VkImageAspectFlags vk_aspect_mask = upload_aspect_mask; scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, @@ -1991,7 +2040,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, image_copies, false); } initialized = true; - runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image)); + runtime->ReleaseMsaaScratchImage(temp_vk_image); if (is_rescaled) { ScaleUp(); @@ -2048,26 +2097,20 @@ void Image::DownloadMemory(std::span buffers_span, std::span o if (info.num_samples > 1) { if (runtime->CanDownloadMsaa(info)) { - ImageInfo temp_info = info; - temp_info.num_samples = 1; - - VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info); - image_ci.format = - MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format) - .format; 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; + VkImageUsageFlags scratch_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; if (msaa_download_is_depth) { - image_ci.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + scratch_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } else { - image_ci.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + scratch_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 VkImageCreateInfo image_ci = + MakeMsaaScratchImageCreateInfo(runtime->device, info, scratch_usage); + const VkImage temp_vk_image = runtime->AcquireMsaaScratchImage(image_ci); const VkImageAspectFlags temp_aspect_mask = aspect_mask; const VkAccessFlags attachment_access = @@ -2191,8 +2234,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span 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)); + runtime->ReleaseMsaaScratchImage(temp_vk_image); } if (is_rescaled) { ScaleUp(true); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index e17a0653f6..dfa3333d5b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -70,6 +70,10 @@ public: bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const; + [[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci); + + void ReleaseMsaaScratchImage(VkImage image); + void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, @@ -162,7 +166,28 @@ public: static constexpr size_t indexing_slots = 8 * sizeof(size_t); std::array buffers{}; - std::vector> pending_msaa_images; + struct MsaaScratchKey { + VkFormat format; + VkImageType type; + u32 width; + u32 height; + u32 depth; + u32 levels; + u32 layers; + VkImageUsageFlags usage; + VkImageCreateFlags flags; + + bool operator==(const MsaaScratchKey&) const noexcept = default; + }; + + struct MsaaScratchImage { + MsaaScratchKey key; + vk::Image image; + u64 tick; + u32 unused_frames; + }; + + std::vector msaa_scratch_images; ankerl::unordered_dense::map resolve_shadows; std::vector> pending_resolve_shadows; }; From 2df4744b53a4fc64889038623d9a4e2c6bffb20f Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 21:40:47 -0400 Subject: [PATCH 68/72] Some meows --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 4 ++-- src/video_core/vulkan_common/vulkan_device.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 07e4b4d378..358695bcd6 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -712,7 +712,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.WaitForRequests(stop_loading); if (use_vulkan_pipeline_cache) { - std::unique_lock cache_lock{vulkan_pipeline_cache_mutex}; + std::shared_lock cache_lock{vulkan_pipeline_cache_mutex}; SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); size_t size = 0; @@ -747,7 +747,7 @@ void PipelineCache::QueueVulkanPipelineCacheFlush() { last_flush = now; serialization_thread.QueueWork([this] { { - std::unique_lock lock{vulkan_pipeline_cache_mutex}; + std::shared_lock lock{vulkan_pipeline_cache_mutex}; SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); size_t size = 0; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index a7c93c161c..11ec738c99 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -729,6 +729,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); + if (!extensions.synchronization2) { + dld.vkCmdPipelineBarrier2 = nullptr; + dld.vkQueueSubmit2 = nullptr; + } graphics_queue = logical.GetQueue(graphics_family); present_queue = logical.GetQueue(present_family); From 16b9b5aa0c84d1026e68f7b87e81db93aa7a30f1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 21:41:04 -0400 Subject: [PATCH 69/72] Revert "[TEST] Save space with Sync2" --- src/video_core/renderer_vulkan/blit_image.cpp | 80 +++++----- src/video_core/renderer_vulkan/blit_image.h | 19 ++- .../renderer_vulkan/vk_rasterizer.cpp | 16 +- .../renderer_vulkan/vk_render_pass_cache.cpp | 4 + .../renderer_vulkan/vk_scheduler.cpp | 69 ++++----- src/video_core/vulkan_common/vulkan_device.h | 5 +- .../vulkan_common/vulkan_wrapper.cpp | 72 --------- src/video_core/vulkan_common/vulkan_wrapper.h | 140 +++++++++--------- 8 files changed, 159 insertions(+), 246 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index f931a6bcd9..caf51c71f3 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -1064,12 +1064,10 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .layerCount = VK_REMAINING_ARRAY_LAYERS, }; const std::array pre_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = aspect_info.pre_src_stages, .srcAccessMask = aspect_info.pre_src_access, - .dstStageMask = aspect_info.pre_src_dst_stages, .dstAccessMask = aspect_info.pre_src_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1078,12 +1076,10 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .image = src, .subresourceRange = barrier_range, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = aspect_info.pre_src_stages, .srcAccessMask = aspect_info.pre_src_access, - .dstStageMask = aspect_info.pre_dst_dst_stages, .dstAccessMask = aspect_info.pre_dst_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1093,7 +1089,8 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .subresourceRange = barrier_range, }, }; - cmdbuf.PipelineBarrier2Images(0, pre_barriers); + cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0, + nullptr, nullptr, pre_barriers); const VkRenderPassBeginInfo renderpass_bi{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .pNext = nullptr, @@ -1129,12 +1126,10 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants); cmdbuf.Draw(3, 1, 0, 0); cmdbuf.EndRenderPass(); - const VkImageMemoryBarrier2 post_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier post_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = aspect_info.post_src_stages, .srcAccessMask = aspect_info.post_src_access, - .dstStageMask = aspect_info.post_dst_stages, .dstAccessMask = aspect_info.post_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1143,7 +1138,8 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, .image = dst, .subresourceRange = barrier_range, }; - cmdbuf.PipelineBarrier2Images(0, post_barrier); + cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0, + post_barrier); }); msaa_copy_resources.push_back(MSAACopyResources{ .tick = scheduler.CurrentTick(), @@ -1192,23 +1188,21 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT, .attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT, .barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT, - .pre_src_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_SHADER_WRITE_BIT | - VK_ACCESS_2_TRANSFER_WRITE_BIT, - .pre_src_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, + .pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, .pre_dst_dst_access = - VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - .pre_src_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .pre_src_dst_stages = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, - .pre_dst_dst_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .post_src_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - .post_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT, - .post_src_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .post_dst_stages = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_2_TRANSFER_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + .pre_dst_stages = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, }; const VkFormat src_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; @@ -1704,19 +1698,19 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage .attachment_aspect = attachment_aspect, .barrier_aspect = attachment_aspect, .pre_src_access = - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, - .pre_src_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, - .pre_dst_dst_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .pre_src_stages = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .pre_src_dst_stages = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, - .pre_dst_dst_stages = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, - .post_src_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .post_dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT, - .post_src_stages = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + .pre_dst_stages = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, .post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, }; const VkFormat src_vk_format = diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 32a4cd5ad2..162e894c3e 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -140,16 +140,15 @@ private: VkImageAspectFlags src_view_aspect; VkImageAspectFlags attachment_aspect; VkImageAspectFlags barrier_aspect; - VkAccessFlags2 pre_src_access; - VkAccessFlags2 pre_src_dst_access; - VkAccessFlags2 pre_dst_dst_access; - VkPipelineStageFlags2 pre_src_stages; - VkPipelineStageFlags2 pre_src_dst_stages; - VkPipelineStageFlags2 pre_dst_dst_stages; - VkAccessFlags2 post_src_access; - VkAccessFlags2 post_dst_access; - VkPipelineStageFlags2 post_src_stages; - VkPipelineStageFlags2 post_dst_stages; + VkAccessFlags pre_src_access; + VkAccessFlags pre_src_dst_access; + VkAccessFlags pre_dst_dst_access; + VkPipelineStageFlags pre_src_stages; + VkPipelineStageFlags pre_dst_stages; + VkAccessFlags post_src_access; + VkAccessFlags post_dst_access; + VkPipelineStageFlags post_src_stages; + VkPipelineStageFlags post_dst_stages; }; void BlitMSAAImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 04bce948f1..f22d1e1ae8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -363,6 +363,8 @@ void RasterizerVulkan::DrawTexture() { UpdateDynamicStates(); + query_cache.NotifySegment(true); + query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state; const auto& sampler = texture_cache.GetSampler(draw_texture_state.src_sampler, false); const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); @@ -493,10 +495,10 @@ void RasterizerVulkan::Clear(u32 layer_count) { (!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); } + query_cache.NotifySegment(true); + query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); UpdateViewportsState(regs); const u32 color_attachment = regs.clear_surface.RT; @@ -1875,13 +1877,19 @@ void RasterizerVulkan::UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& r if (!state_tracker.TouchColorMask()) { return; } + size_t num_attachments{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) { + num_attachments = index + 1; + } + } std::array setup_enables{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE; } - scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorWriteEnableEXT(setup_enables); + scheduler.Record([setup_enables, num_attachments](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteEnableEXT(vk::Span(setup_enables.data(), num_attachments)); }); } 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 6be0adc229..023771832b 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -319,6 +319,10 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { }; if (device->HasSynchronization2()) { counter_resume_dependency2.pNext = &counter_resume_barrier; + counter_resume_dependency2.srcStageMask = 0; + counter_resume_dependency2.dstStageMask = 0; + counter_resume_dependency2.srcAccessMask = 0; + counter_resume_dependency2.dstAccessMask = 0; } boost::container::static_vector dependencies2; if (can_resume_transform_feedback) { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 1310476c6a..31e852e943 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -421,21 +421,18 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, - shader_stages = device.ShaderConsumerStages(), + consumer_stages = device.AttachmentConsumerStages(), has_transform_feedback = device.IsExtTransformFeedbackSupported()]( vk::CommandBuffer cmdbuf) { - static constexpr VkAccessFlags2 SHADER_ACCESS = - VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT; - static constexpr VkAccessFlags2 COLOR_ACCESS = - VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; - static constexpr VkAccessFlags2 DEPTH_STENCIL_ACCESS = - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - static constexpr VkPipelineStageFlags2 DEPTH_STENCIL_STAGES = - VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; + static constexpr VkAccessFlags SHADER_ACCESS = + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + static constexpr VkAccessFlags COLOR_ACCESS = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - std::array barriers; + std::array barriers; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; @@ -443,37 +440,25 @@ void Scheduler::EndRenderPass() & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) !=0; - VkPipelineStageFlags2 src_stages = 0; - VkAccessFlags2 src_access = 0; - VkPipelineStageFlags2 dst_stages = shader_stages; - VkAccessFlags2 dst_access = SHADER_ACCESS; + VkAccessFlags src_access = 0; + VkAccessFlags dst_access = SHADER_ACCESS; if (is_color) { - src_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; - src_access |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; - dst_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dst_access |= COLOR_ACCESS; } else if (is_depth_stencil) { - src_stages |= DEPTH_STENCIL_STAGES; - src_access |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dst_stages |= DEPTH_STENCIL_STAGES; + src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dst_access |= DEPTH_STENCIL_ACCESS; } else { - src_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT - | DEPTH_STENCIL_STAGES; - src_access |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT - | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dst_stages |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT - | DEPTH_STENCIL_STAGES; + src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS; } - barriers[i] = VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + barriers[i] = VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = src_stages, .srcAccessMask = src_access, - .dstStageMask = dst_stages, .dstAccessMask = dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -484,19 +469,19 @@ void Scheduler::EndRenderPass() }; } cmdbuf.EndRenderPass(); - cmdbuf.PipelineBarrier2Images(0, vk::Span(barriers.data(), num_images)); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages, + 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { - static constexpr VkMemoryBarrier2 XFB_OUTPUT_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, - .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, - .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT | - VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, }; - cmdbuf.PipelineBarrier2Memory(0, XFB_OUTPUT_BARRIER); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, XFB_OUTPUT_BARRIER); } }); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 51bae46409..9d7f023e20 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -1013,11 +1013,14 @@ FN_MAX_LIMIT_LIST return features2.features.geometryShader; } - VkPipelineStageFlags ShaderConsumerStages() const { + 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.geometryShader) { stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 9013bb3c49..33173e956d 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -10,8 +10,6 @@ #include #include -#include - #include "common/common_types.h" #include "common/logging.h" #include "video_core/vulkan_common/vk_enum_string_helper.h" @@ -298,76 +296,6 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec } // Anonymous namespace -void PipelineBarrierDowngrade(const DeviceDispatch& dld, VkCommandBuffer handle, - VkDependencyFlags dependency_flags, - Span memory_barriers, - Span buffer_barriers, - Span image_barriers) { - VkPipelineStageFlags2 src_stages = 0; - VkPipelineStageFlags2 dst_stages = 0; - for (const VkMemoryBarrier2& barrier : memory_barriers) { - src_stages |= barrier.srcStageMask; - dst_stages |= barrier.dstStageMask; - } - for (const VkBufferMemoryBarrier2& barrier : buffer_barriers) { - src_stages |= barrier.srcStageMask; - dst_stages |= barrier.dstStageMask; - } - for (const VkImageMemoryBarrier2& barrier : image_barriers) { - src_stages |= barrier.srcStageMask; - dst_stages |= barrier.dstStageMask; - } - - boost::container::small_vector memory; - memory.reserve(memory_barriers.size()); - for (const VkMemoryBarrier2& barrier : memory_barriers) { - memory.push_back(VkMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = DowngradeAccessMask(barrier.srcAccessMask), - .dstAccessMask = DowngradeAccessMask(barrier.dstAccessMask), - }); - } - boost::container::small_vector buffers; - buffers.reserve(buffer_barriers.size()); - for (const VkBufferMemoryBarrier2& barrier : buffer_barriers) { - buffers.push_back(VkBufferMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = DowngradeAccessMask(barrier.srcAccessMask), - .dstAccessMask = DowngradeAccessMask(barrier.dstAccessMask), - .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, - .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, - .buffer = barrier.buffer, - .offset = barrier.offset, - .size = barrier.size, - }); - } - boost::container::small_vector images; - images.reserve(image_barriers.size()); - for (const VkImageMemoryBarrier2& barrier : image_barriers) { - images.push_back(VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = DowngradeAccessMask(barrier.srcAccessMask), - .dstAccessMask = DowngradeAccessMask(barrier.dstAccessMask), - .oldLayout = barrier.oldLayout, - .newLayout = barrier.newLayout, - .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, - .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, - .image = barrier.image, - .subresourceRange = barrier.subresourceRange, - }); - } - - dld.vkCmdPipelineBarrier(handle, - DowngradeStageMask(src_stages, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), - DowngradeStageMask(dst_stages, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT), - dependency_flags, static_cast(memory.size()), memory.data(), - static_cast(buffers.size()), buffers.data(), - static_cast(images.size()), images.data()); -} - bool Load(InstanceDispatch& dld) noexcept { #define X(name) Proc(dld.name, dld, #name) return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) && diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index f29eb176f6..d4e8e2aebc 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -158,41 +158,6 @@ inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_H inline constexpr VkPipelineStageFlags PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_HOST_BIT; -constexpr VkPipelineStageFlags DowngradeStageMask(VkPipelineStageFlags2 mask, - VkPipelineStageFlags empty) noexcept { - VkPipelineStageFlags result = static_cast(mask & 0xFFFFFFFFULL); - if ((mask & (VK_PIPELINE_STAGE_2_COPY_BIT | VK_PIPELINE_STAGE_2_RESOLVE_BIT | - VK_PIPELINE_STAGE_2_BLIT_BIT | VK_PIPELINE_STAGE_2_CLEAR_BIT)) != 0) { - result |= VK_PIPELINE_STAGE_TRANSFER_BIT; - } - if ((mask & VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT) != 0) { - result |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | - VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | - VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; - } - if ((mask & VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT) != 0) { - result |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; - } - if ((mask & VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT) != 0) { - result |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; - } - if (result == 0) { - return empty; - } - return result; -} - -constexpr VkAccessFlags DowngradeAccessMask(VkAccessFlags2 mask) noexcept { - VkAccessFlags result = static_cast(mask & 0xFFFFFFFFULL); - if ((mask & (VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT)) != 0) { - result |= VK_ACCESS_SHADER_READ_BIT; - } - if ((mask & VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT) != 0) { - result |= VK_ACCESS_SHADER_WRITE_BIT; - } - return result; -} - /// Table holding Vulkan instance function pointers. struct InstanceDispatch { @@ -1232,12 +1197,6 @@ private: const InstanceDispatch* dld = nullptr; }; -void PipelineBarrierDowngrade(const DeviceDispatch& dld, VkCommandBuffer handle, - VkDependencyFlags dependency_flags, - Span memory_barriers, - Span buffer_barriers, - Span image_barriers); - class CommandBuffer { public: CommandBuffer() noexcept = default; @@ -1388,45 +1347,78 @@ public: VkDependencyFlags dependency_flags, Span memory_barriers, Span buffer_barriers, Span image_barriers) const noexcept { + static constexpr u32 MaxBarriers = 16; + if (dld->vkCmdPipelineBarrier2 && memory_barriers.size() <= MaxBarriers && + buffer_barriers.size() <= MaxBarriers && image_barriers.size() <= MaxBarriers) { + const auto src_stage_mask2 = static_cast(src_stage_mask); + const auto dst_stage_mask2 = static_cast(dst_stage_mask); + + std::array memory_barriers2; + for (u32 i = 0; i < memory_barriers.size(); ++i) { + memory_barriers2[i] = VkMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = src_stage_mask2, + .srcAccessMask = static_cast(memory_barriers[i].srcAccessMask), + .dstStageMask = dst_stage_mask2, + .dstAccessMask = static_cast(memory_barriers[i].dstAccessMask), + }; + } + std::array buffer_barriers2; + for (u32 i = 0; i < buffer_barriers.size(); ++i) { + const auto& barrier = buffer_barriers[i]; + buffer_barriers2[i] = VkBufferMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = src_stage_mask2, + .srcAccessMask = static_cast(barrier.srcAccessMask), + .dstStageMask = dst_stage_mask2, + .dstAccessMask = static_cast(barrier.dstAccessMask), + .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, + .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, + .buffer = barrier.buffer, + .offset = barrier.offset, + .size = barrier.size, + }; + } + std::array image_barriers2; + for (u32 i = 0; i < image_barriers.size(); ++i) { + const auto& barrier = image_barriers[i]; + image_barriers2[i] = VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = src_stage_mask2, + .srcAccessMask = static_cast(barrier.srcAccessMask), + .dstStageMask = dst_stage_mask2, + .dstAccessMask = static_cast(barrier.dstAccessMask), + .oldLayout = barrier.oldLayout, + .newLayout = barrier.newLayout, + .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, + .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, + .image = barrier.image, + .subresourceRange = barrier.subresourceRange, + }; + } + const VkDependencyInfo dependency_info{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = dependency_flags, + .memoryBarrierCount = memory_barriers.size(), + .pMemoryBarriers = memory_barriers2.data(), + .bufferMemoryBarrierCount = buffer_barriers.size(), + .pBufferMemoryBarriers = buffer_barriers2.data(), + .imageMemoryBarrierCount = image_barriers.size(), + .pImageMemoryBarriers = image_barriers2.data(), + }; + dld->vkCmdPipelineBarrier2(handle, &dependency_info); + return; + } dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags, memory_barriers.size(), memory_barriers.data(), buffer_barriers.size(), buffer_barriers.data(), image_barriers.size(), image_barriers.data()); } - void PipelineBarrier2(VkDependencyFlags dependency_flags, - Span memory_barriers, - Span buffer_barriers, - Span image_barriers) const noexcept { - if (!dld->vkCmdPipelineBarrier2) { - PipelineBarrierDowngrade(*dld, handle, dependency_flags, memory_barriers, - buffer_barriers, image_barriers); - return; - } - const VkDependencyInfo dependency_info{ - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = dependency_flags, - .memoryBarrierCount = memory_barriers.size(), - .pMemoryBarriers = memory_barriers.data(), - .bufferMemoryBarrierCount = buffer_barriers.size(), - .pBufferMemoryBarriers = buffer_barriers.data(), - .imageMemoryBarrierCount = image_barriers.size(), - .pImageMemoryBarriers = image_barriers.data(), - }; - dld->vkCmdPipelineBarrier2(handle, &dependency_info); - } - - void PipelineBarrier2Images(VkDependencyFlags dependency_flags, - Span image_barriers) const noexcept { - PipelineBarrier2(dependency_flags, {}, {}, image_barriers); - } - - void PipelineBarrier2Memory(VkDependencyFlags dependency_flags, - Span memory_barriers) const noexcept { - PipelineBarrier2(dependency_flags, memory_barriers, {}, {}); - } - void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkDependencyFlags dependency_flags = 0) const noexcept { PipelineBarrier(src_stage_mask, dst_stage_mask, dependency_flags, {}, {}, {}); From 9392ccf1247a5eb165310f55d21db9ef9023dd54 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 21:55:02 -0400 Subject: [PATCH 70/72] color write meows --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f22d1e1ae8..eeb58b9919 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1877,19 +1877,13 @@ void RasterizerVulkan::UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& r if (!state_tracker.TouchColorMask()) { return; } - size_t num_attachments{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) { - num_attachments = index + 1; - } - } std::array setup_enables{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE; } - scheduler.Record([setup_enables, num_attachments](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorWriteEnableEXT(vk::Span(setup_enables.data(), num_attachments)); + scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteEnableEXT(setup_enables); }); } From 3d453e623622f97d3f3d55e1721a3fcae5120c5a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 23:13:20 -0400 Subject: [PATCH 71/72] [TEST] Unify another section of the blits on MSAA path --- src/video_core/renderer_vulkan/blit_image.cpp | 135 ++++-------------- src/video_core/renderer_vulkan/blit_image.h | 7 +- 2 files changed, 35 insertions(+), 107 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index caf51c71f3..f93145fa67 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -667,24 +667,13 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV .renderpass = dst_framebuffer->RenderPass(), .operation = operation, }; - const VkPipelineLayout layout = *one_texture_pipeline_layout; - const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceColorPipeline(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(); + VkSampler sampler = *nearest_sampler; + if (is_linear) { + sampler = *linear_sampler; + } + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceColorPipeline(key), sampler, + src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false); } void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, @@ -713,16 +702,15 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView }); } -void BlitImageHelper::BlitMSAAImpl(const Framebuffer* dst_framebuffer, - const ImageView& src_image_view, const Region2D& dst_region, - const Region2D& src_region, VkPipeline pipeline, - VkImageView src_view, VkImageView src_stencil_view, - bool blit_stencil) { +void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer, + const ImageView& src_image_view, const Region2D& dst_region, + const Region2D& src_region, VkPipeline pipeline, VkSampler sampler, + VkImageView src_view, VkImageView src_stencil_view, + bool blit_stencil) { VkPipelineLayout layout = *one_texture_pipeline_layout; if (blit_stencil) { layout = *two_textures_pipeline_layout; } - const VkSampler sampler = *nearest_sampler; RecordShaderReadBarrier(scheduler, src_image_view); scheduler.RequestRenderpass(dst_framebuffer); @@ -753,9 +741,9 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, .renderpass = dst_framebuffer->RenderPass(), .samples = dst_framebuffer->Samples(), }; - BlitMSAAImpl(dst_framebuffer, src_image_view, dst_region, src_region, - FindOrEmplaceBlitColorMSAAPipeline(key), - src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false); + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitColorMSAAPipeline(key), *nearest_sampler, + src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false); } void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, @@ -771,31 +759,16 @@ void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, if (blit_stencil) { src_stencil_view = src_image_view.StencilView(); } - BlitMSAAImpl(dst_framebuffer, src_image_view, dst_region, src_region, - FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), - src_image_view.DepthView(), src_stencil_view, blit_stencil); + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), *nearest_sampler, + src_image_view.DepthView(), src_stencil_view, blit_stencil); } void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region) { - const VkPipeline pipeline = FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()); - const VkPipelineLayout layout = *one_texture_pipeline_layout; - const VkSampler sampler = *nearest_sampler; - const VkImageView src_depth_view = src_image_view.DepthView(); - - RecordShaderReadBarrier(scheduler, src_image_view); - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, - src_depth_view](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_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(); + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler, + src_image_view.DepthView(), VK_NULL_HANDLE, false); } void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer, @@ -803,36 +776,14 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer, 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(); + VkImageView src_stencil_view = VK_NULL_HANDLE; + if (resolve_stencil) { + src_stencil_view = src_image_view.StencilView(); + } + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), + resolve_stencil), + *nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil); } void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, @@ -847,40 +798,16 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, .renderpass = dst_framebuffer->RenderPass(), .operation = operation, }; - VkPipelineLayout layout{}; VkPipeline pipeline{}; VkImageView src_stencil_view = VK_NULL_HANDLE; if (blit_stencil) { - layout = *two_textures_pipeline_layout; pipeline = FindOrEmplaceDepthStencilPipeline(key); src_stencil_view = src_image_view.StencilView(); } else { - layout = *one_texture_pipeline_layout; pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass); } - const VkSampler sampler = *nearest_sampler; - const VkImageView src_depth_view = src_image_view.DepthView(); - - RecordShaderReadBarrier(scheduler, src_image_view); - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view, - src_stencil_view, blit_stencil, this](vk::CommandBuffer cmdbuf) { - VkDescriptorSet descriptor_set{}; - if (blit_stencil) { - descriptor_set = two_textures_descriptor_allocator.Commit(); - UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view, - src_stencil_view); - } else { - descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_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(); + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler, + src_image_view.DepthView(), src_stencil_view, blit_stencil); } void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer, diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 162e894c3e..62e6743a23 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -151,9 +151,10 @@ private: VkPipelineStageFlags post_dst_stages; }; - void BlitMSAAImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, - const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline, - VkImageView src_view, VkImageView src_stencil_view, bool blit_stencil); + void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline, + VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view, + bool blit_stencil); void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, VkImage dst_image, VkFormat dst_vk_format, VkImage src_image, From e46c19391dee3c717c778a3a75a7ca873420ea62 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 22 Aug 2026 23:18:10 -0400 Subject: [PATCH 72/72] Add small_vector to CopyMSAA with reserve image on upload path --- .../renderer_vulkan/vk_texture_cache.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 59bcdc8c2a..d62ca66e00 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2015,7 +2015,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, }); const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); - std::vector image_copies; + boost::container::small_vector image_copies; image_copies.reserve(copies.size()); for (const auto& copy : copies) { VideoCommon::ImageCopy image_copy{}; @@ -2032,12 +2032,13 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, if (msaa_upload_is_depth) { runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(), info.format, temp_vk_image, info.format, - info.num_samples, image_copies, + info.num_samples, + {image_copies.data(), image_copies.size()}, 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, - image_copies, false); + {image_copies.data(), image_copies.size()}, false); } initialized = true; runtime->ReleaseMsaaScratchImage(temp_vk_image); @@ -2148,7 +2149,8 @@ void Image::DownloadMemory(std::span buffers_span, std::span o init_barrier); }); - std::vector image_copies; + boost::container::small_vector image_copies; + image_copies.reserve(copies.size()); for (const auto& copy : copies) { VideoCommon::ImageCopy image_copy; image_copy.src_offset = copy.image_offset; @@ -2162,11 +2164,14 @@ void Image::DownloadMemory(std::span buffers_span, std::span o 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); + info.num_samples, {image_copies.data(), image_copies.size()}, + 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); + info.num_samples, + {image_copies.data(), image_copies.size()}, + true); } boost::container::small_vector buffers_vector{};