|
|
|
@ -26,6 +26,7 @@ |
|
|
|
#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"
|
|
|
|
@ -34,6 +35,7 @@ |
|
|
|
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
|
|
|
#include "video_core/texture_cache/accelerated_swizzle.h"
|
|
|
|
#include "video_core/texture_cache/types.h"
|
|
|
|
#include "video_core/texture_cache/util.h"
|
|
|
|
#include "video_core/textures/decoders.h"
|
|
|
|
#include "video_core/vulkan_common/vulkan_device.h"
|
|
|
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
|
|
|
@ -1181,4 +1183,321 @@ void BlockLinearUnswizzle2DPass::VerifyAgainstCpu(const StagingBufferRef& swizzl |
|
|
|
num_diff, size, reference[first_diff], gpu_data[first_diff]); |
|
|
|
} |
|
|
|
|
|
|
|
namespace { |
|
|
|
constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0; |
|
|
|
constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1; |
|
|
|
|
|
|
|
struct alignas(16) BlockLinearUnswizzle3DPushConstants { |
|
|
|
std::array<u32, 3> dim; |
|
|
|
u32 bytes_per_block_log2; |
|
|
|
std::array<u32, 3> 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(BlockLinearUnswizzle3DPushConstants) <= 128); |
|
|
|
|
|
|
|
constexpr std::array<VkDescriptorSetLayoutBinding, 2> 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<VkDescriptorUpdateTemplateEntry, 2> 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, |
|
|
|
}; |
|
|
|
|
|
|
|
constexpr bool BL3DB_VERIFY_AGAINST_CPU = false; |
|
|
|
} // 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<sizeof(BlockLinearUnswizzle3DPushConstants)>, |
|
|
|
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<const VideoCommon::SwizzleParameters> swizzles) { |
|
|
|
using namespace VideoCommon::Accelerated; |
|
|
|
|
|
|
|
if (swizzles.empty()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
const VideoCommon::SwizzleParameters& sw = swizzles.front(); |
|
|
|
const auto params = 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<VkDeviceSize>(blocks_x) * blocks_y * blocks_z * bytes_per_block; |
|
|
|
|
|
|
|
const StagingBufferRef output = |
|
|
|
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal); |
|
|
|
|
|
|
|
BlockLinearUnswizzle3DPushConstants 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<VkAccessFlags>( |
|
|
|
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_ALL_COMMANDS_BIT |
|
|
|
: 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_ALL_COMMANDS_BIT, |
|
|
|
0, {}, {}, post_copy); |
|
|
|
}); |
|
|
|
|
|
|
|
if constexpr (BL3DB_VERIFY_AGAINST_CPU) { |
|
|
|
VerifyAgainstCpu(swizzled, sw, image.info, output, output_size, blocks_x, blocks_y, |
|
|
|
blocks_z, bytes_per_block); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void BlockLinearUnswizzle3DBufferPass::VerifyAgainstCpu( |
|
|
|
const StagingBufferRef& swizzled, const VideoCommon::SwizzleParameters& sw, |
|
|
|
const VideoCommon::ImageInfo& info, const StagingBufferRef& gpu_output, |
|
|
|
VkDeviceSize output_size, u32 blocks_x, u32 blocks_y, u32 blocks_z, u32 bytes_per_block) { |
|
|
|
const StagingBufferRef readback = |
|
|
|
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::Download); |
|
|
|
|
|
|
|
const VkBuffer src = gpu_output.buffer; |
|
|
|
const VkDeviceSize src_offset = gpu_output.offset; |
|
|
|
const VkBuffer dst = readback.buffer; |
|
|
|
const VkDeviceSize dst_offset = readback.offset; |
|
|
|
scheduler.Record([src, src_offset, dst, dst_offset, output_size](vk::CommandBuffer cmdbuf) { |
|
|
|
const VkBufferMemoryBarrier barrier{ |
|
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, |
|
|
|
.pNext = nullptr, |
|
|
|
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, |
|
|
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, |
|
|
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
|
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, |
|
|
|
.buffer = src, |
|
|
|
.offset = src_offset, |
|
|
|
.size = output_size, |
|
|
|
}; |
|
|
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, |
|
|
|
0, {}, barrier, {}); |
|
|
|
const VkBufferCopy copy{ |
|
|
|
.srcOffset = src_offset, |
|
|
|
.dstOffset = dst_offset, |
|
|
|
.size = output_size, |
|
|
|
}; |
|
|
|
cmdbuf.CopyBuffer(src, dst, copy); |
|
|
|
}); |
|
|
|
scheduler.Finish(); |
|
|
|
|
|
|
|
const size_t size = static_cast<size_t>(output_size); |
|
|
|
std::vector<u8> reference(size); |
|
|
|
const std::span<const u8> input{swizzled.mapped_span.data() + sw.buffer_offset, |
|
|
|
swizzled.mapped_span.size() - sw.buffer_offset}; |
|
|
|
const u32 stride_alignment = VideoCommon::CalculateLevelStrideAlignment(info, sw.level); |
|
|
|
Tegra::Texture::UnswizzleTexture(reference, input, bytes_per_block, blocks_x, blocks_y, blocks_z, |
|
|
|
sw.block.height, sw.block.depth, stride_alignment); |
|
|
|
|
|
|
|
const u8* gpu_data = readback.mapped_span.data(); |
|
|
|
if (std::memcmp(reference.data(), gpu_data, size) == 0) { |
|
|
|
LOG_INFO(Render_Vulkan, "BL3D verify OK: {}x{}x{} bpb={} ({} bytes)", blocks_x, blocks_y, |
|
|
|
blocks_z, bytes_per_block, size); |
|
|
|
return; |
|
|
|
} |
|
|
|
size_t first_diff = size; |
|
|
|
size_t num_diff = 0; |
|
|
|
for (size_t i = 0; i < size; ++i) { |
|
|
|
if (reference[i] != gpu_data[i]) { |
|
|
|
if (first_diff == size) { |
|
|
|
first_diff = i; |
|
|
|
} |
|
|
|
++num_diff; |
|
|
|
} |
|
|
|
} |
|
|
|
LOG_CRITICAL(Render_Vulkan, |
|
|
|
"BL3D verify FAILED: {}x{}x{} bpb={} block_height={} block_depth={} " |
|
|
|
"first_diff={} num_diff={}/{} cpu=0x{:02x} gpu=0x{:02x}", |
|
|
|
blocks_x, blocks_y, blocks_z, bytes_per_block, sw.block.height, sw.block.depth, |
|
|
|
first_diff, num_diff, size, reference[first_diff], gpu_data[first_diff]); |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace Vulkan
|