Some cleanups

This commit is contained in:
CamilleLaVey
2026-09-15 21:10:36 -04:00
parent fa4e7c6992
commit b1e512c152
37 changed files with 509 additions and 1590 deletions
+238 -245
View File
@@ -16,13 +16,16 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/vector_math.h"
#include <bit>
#include "video_core/host_shaders/astc_decoder_comp_spv.h"
#include "video_core/host_shaders/queries_prefix_scan_sum_comp_spv.h"
#include "video_core/host_shaders/queries_prefix_scan_sum_nosubgroups_comp_spv.h"
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
#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_3d_bcn_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_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"
@@ -185,6 +188,26 @@ struct AstcPushConstants {
u32 block_height_mask;
};
struct BlockLinear3DImagePushConstants {
alignas(16) std::array<u32, 3> origin;
alignas(16) std::array<s32, 3> destination;
u32 bytes_per_block_log2;
u32 slice_size;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
u32 block_depth;
u32 block_depth_mask;
};
struct PitchUnswizzlePushConstants {
std::array<u32, 2> origin;
std::array<s32, 2> destination;
u32 bytes_per_block_log2;
u32 pitch;
};
struct QueriesPrefixScanPushConstants {
u32 min_accumulation_base;
u32 max_accumulation_base;
@@ -616,260 +639,230 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
scheduler.Finish();
}
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
namespace {
struct alignas(16) BlockLinearUnswizzle3DPushConstants {
u32 blocks_dim[3]; // Offset 0
u32 bytes_per_block_log2; // Offset 12
u32 origin[3]; // Offset 16
u32 slice_size; // Offset 28
u32 block_size; // Offset 32
u32 x_shift; // Offset 36
u32 block_height; // Offset 40
u32 block_height_mask; // Offset 44
u32 block_depth; // Offset 48
u32 block_depth_mask; // Offset 52
s32 _pad; // Offset 56
s32 destination[3]; // Offset 60
s32 _pad_end; // Offset 72
};
static_assert(sizeof(BlockLinearUnswizzle3DPushConstants) <= 128);
BlockLinearUnswizzle3DPass::BlockLinearUnswizzle3DPass(
const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_,
std::array<VkDescriptorSetLayoutBinding, 2>{{
{
.binding = BL3D_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}},
std::array<VkDescriptorUpdateTemplateEntry, 2>{{
{
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
}
}},
DescriptorBankInfo{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
},
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV),
scheduler{scheduler_},
staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle3DPass::~BlockLinearUnswizzle3DPass() = default;
// God have mercy on my soul
void BlockLinearUnswizzle3DPass::Unswizzle(
Image& image,
const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_start, u32 z_count)
{
using namespace VideoCommon::Accelerated;
const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth);
// Allocate or grow to cover this batch's slice count
image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES);
ASSERT(swizzles.size() == 1);
const auto& sw = swizzles[0];
const auto params = MakeBlockLinearSwizzle3DParams(sw, image.info);
const u32 blocks_x = (image.info.size.width + 3) / 4;
const u32 blocks_y = (image.info.size.height + 3) / 4;
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);
void RecordUnswizzleBeginBarrier(Scheduler& scheduler, VkPipeline vk_pipeline, VkImage vk_image,
VkImageAspectFlags aspect_mask, bool is_initialized) {
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;
old_layout = VK_IMAGE_LAYOUT_GENERAL;
src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
}
}
void BlockLinearUnswizzle3DPass::UnswizzleChunk(
Image& image,
const StagingBufferRef& swizzled,
const VideoCommon::SwizzleParameters& sw,
const BlockLinearSwizzle3DParams& params,
u32 blocks_x, u32 blocks_y,
u32 z_start, u32 z_count)
{
BlockLinearUnswizzle3DPushConstants pc{};
pc.origin[0] = params.origin[0];
pc.origin[1] = params.origin[1];
pc.origin[2] = z_start; // Current chunk's Z start
pc.destination[0] = params.destination[0];
pc.destination[1] = params.destination[1];
pc.destination[2] = 0; // Shader writes to start of output buffer
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
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;
pc.blocks_dim[0] = blocks_x;
pc.blocks_dim[1] = blocks_y;
pc.blocks_dim[2] = z_count; // Only process the count
compute_pass_descriptor_queue.Acquire(scheduler, 3);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer,
sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset);
compute_pass_descriptor_queue.AddBuffer(*image.compute_unswizzle_buffer, 0,
image.compute_unswizzle_buffer_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(z_count, 4u);
const u32 bytes_per_block = 1u << pc.bytes_per_block_log2;
const VkDeviceSize output_slice_size =
static_cast<VkDeviceSize>(blocks_x) * blocks_y * bytes_per_block;
const VkDeviceSize barrier_size = output_slice_size * z_count;
const bool is_first_chunk = (z_start == 0);
const VkBuffer out_buffer = *image.compute_unswizzle_buffer;
const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask();
const u32 image_width = image.info.size.width;
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,
image_width, image_height
](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);
// Single barrier for compute -> transfer (buffer ready, image transition)
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 = 0,
.size = barrier_size,
};
// Image layout transition
const VkImageMemoryBarrier pre_barrier{
scheduler.Record([vk_pipeline, vk_image, aspect_mask, src_access, old_layout,
src_stage](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = is_first_chunk ? VkAccessFlags{} :
static_cast<VkAccessFlags>(VK_ACCESS_TRANSFER_WRITE_BIT),
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.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,
.image = dst_image,
.subresourceRange = {aspect, 0, 1, 0, 1},
};
// Single barrier handles both buffer and image
cmdbuf.PipelineBarrier(
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
nullptr, buffer_barrier, pre_barrier
);
// Copy chunk to correct Z position in image
const VkBufferImageCopy copy{
.bufferOffset = 0, // Read from start of staging buffer
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = {aspect, 0, 0, 1},
.imageOffset = {0, 0, static_cast<s32>(z_start)}, // Write to correct Z
.imageExtent = {image_width, image_height, z_count},
};
cmdbuf.CopyBufferToImage(out_buffer, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
// Post-copy transition
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.srcAccessMask = src_access,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.oldLayout = old_layout,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange = {aspect, 0, 1, 0, 1},
.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_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0,
nullptr, nullptr, post_barrier
);
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
});
}
void RecordUnswizzleEndBarrier(Scheduler& scheduler, VkImage vk_image,
VkImageAspectFlags aspect_mask) {
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);
});
}
} // Anonymous namespace
BlockLinearUnswizzleImage2DPass::BlockLinearUnswizzleImage2DPass(
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<sizeof(BlockLinearSwizzle2DParams)>,
BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzleImage2DPass::~BlockLinearUnswizzleImage2DPass() = default;
void BlockLinearUnswizzleImage2DPass::Unswizzle(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
using namespace VideoCommon::Accelerated;
scheduler.RequestOutsideRenderPassOperationContext();
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
RecordUnswizzleBeginBarrier(scheduler, *pipeline, vk_image, aspect_mask,
image.ExchangeInitialization());
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_z = image.info.resources.layers;
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.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 auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, 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, num_dispatches_z);
});
}
RecordUnswizzleEndBarrier(scheduler, vk_image, aspect_mask);
scheduler.Finish();
}
BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass(
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<sizeof(BlockLinear3DImagePushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default;
void BlockLinearUnswizzleImage3DPass::Unswizzle(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
using namespace VideoCommon::Accelerated;
scheduler.RequestOutsideRenderPassOperationContext();
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
RecordUnswizzleBeginBarrier(scheduler, *pipeline, vk_image, aspect_mask,
image.ExchangeInitialization());
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 2U);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.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 auto p = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
const BlockLinear3DImagePushConstants params{
.origin = p.origin,
.destination = p.destination,
.bytes_per_block_log2 = p.bytes_per_block_log2,
.slice_size = p.slice_size,
.block_size = p.block_size,
.x_shift = p.x_shift,
.block_height = p.block_height,
.block_height_mask = p.block_height_mask,
.block_depth = p.block_depth,
.block_depth_mask = p.block_depth_mask,
};
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, 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, num_dispatches_z);
});
}
RecordUnswizzleEndBarrier(scheduler, vk_image, aspect_mask);
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<sizeof(PitchUnswizzlePushConstants)>,
PITCH_UNSWIZZLE_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
PitchUnswizzlePass::~PitchUnswizzlePass() = default;
void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
ASSERT(std::has_single_bit(bytes_per_block));
scheduler.RequestOutsideRenderPassOperationContext();
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
RecordUnswizzleBeginBarrier(scheduler, *pipeline, vk_image, aspect_mask,
image.ExchangeInitialization());
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.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_log2 = static_cast<u32>(std::countr_zero(bytes_per_block)),
.pitch = image.info.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);
});
}
RecordUnswizzleEndBarrier(scheduler, vk_image, aspect_mask);
scheduler.Finish();
}
} // namespace Vulkan
@@ -17,7 +17,6 @@
#include "video_core/texture_cache/types.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "video_core/texture_cache/accelerated_swizzle.h"
namespace VideoCommon {
struct SwizzleParameters;
@@ -25,8 +24,6 @@ struct SwizzleParameters;
namespace Vulkan {
using VideoCommon::Accelerated::BlockLinearSwizzle3DParams;
class Device;
class StagingBufferPool;
class Scheduler;
@@ -137,26 +134,50 @@ private:
MemoryAllocator& memory_allocator;
};
class BlockLinearUnswizzle3DPass final : public ComputePass {
class BlockLinearUnswizzleImage2DPass final : public ComputePass {
public:
explicit BlockLinearUnswizzle3DPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle3DPass();
explicit BlockLinearUnswizzleImage2DPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzleImage2DPass();
void Unswizzle(Image& image,
const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_start, u32 z_count);
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
void UnswizzleChunk(
Image& image,
const StagingBufferRef& swizzled,
const VideoCommon::SwizzleParameters& sw,
const BlockLinearSwizzle3DParams& params,
u32 blocks_x, u32 blocks_y,
u32 z_start, u32 z_count);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzleImage3DPass final : public ComputePass {
public:
explicit BlockLinearUnswizzleImage3DPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzleImage3DPass();
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
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();
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
@@ -160,6 +160,73 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
info.size.depth == 1;
}
[[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:
return VK_FORMAT_UNDEFINED;
}
}
[[nodiscard]] bool IsUnswizzleStorageFormatSupported(const Device& device, u32 bytes_per_block) {
switch (bytes_per_block) {
case 1:
return device.IsStorageBuffer8BitAccessSupported();
case 2:
return device.IsStorageBuffer16BitAccessSupported();
case 4:
case 8:
case 16:
return true;
default:
return false;
}
}
[[nodiscard]] bool IsUnswizzleAcceleratedFormat(const Device& device, PixelFormat format) {
if (IsPixelFormatASTC(format) || VideoCore::Surface::IsPixelFormatBCn(format)) {
return false;
}
if (VideoCore::Surface::GetFormatType(format) != SurfaceType::ColorTexture) {
return false;
}
return IsUnswizzleStorageFormatSupported(device, BytesPerBlock(format));
}
[[nodiscard]] bool WillUseAcceleratedUnswizzle(const Device& device, const ImageInfo& info) {
switch (info.type) {
case ImageType::e2D:
case ImageType::e3D:
case ImageType::Linear:
break;
default:
return false;
}
if (info.num_samples > 1 || !device.IsKhrImageFormatListSupported()) {
return false;
}
return IsUnswizzleAcceleratedFormat(device, info.format);
}
[[nodiscard]] VkImageViewType UnswizzleStorageViewType(ImageType type) {
if (type == ImageType::e3D) {
return VK_IMAGE_VIEW_TYPE_3D;
}
if (type == ImageType::Linear) {
return VK_IMAGE_VIEW_TYPE_2D;
}
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
}
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
std::optional<VkFormat> format_override = {}) {
auto format_info =
@@ -249,7 +316,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
}
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
VkFormat format) {
VkFormat format,
VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
.pNext = nullptr,
@@ -260,7 +328,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.pNext = &storage_image_view_usage_create_info,
.flags = 0,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
.viewType = view_type,
.format = format,
.components{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
@@ -946,6 +1014,14 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue, memory_allocator);
}
if (device.IsKhrImageFormatListSupported()) {
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue);
bl_unswizzle_3d_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);
}
if (!device.IsKhrImageFormatListSupported()) {
return;
}
@@ -953,6 +1029,8 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
const auto image_format = static_cast<PixelFormat>(index_a);
if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) {
view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
} else if (IsUnswizzleAcceleratedFormat(device, image_format)) {
view_formats[index_a].push_back(UnswizzleStorageFormat(BytesPerBlock(image_format)));
}
for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) {
const auto view_format = static_cast<PixelFormat>(index_b);
@@ -964,18 +1042,14 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
}
}
if (Settings::values.gpu_unswizzle_enabled.GetValue()) {
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
}
}
void TextureCacheRuntime::Finish() {
scheduler.Finish();
}
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred);
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size) {
return staging_buffer_pool.Request(size, MemoryUsage::Upload);
}
StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
@@ -1899,6 +1973,10 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
}
if (False(flags & VideoCommon::ImageFlagBits::Converted) &&
WillUseAcceleratedUnswizzle(runtime->device, info)) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
}
if (runtime->device.HasDebuggingToolAttached()) {
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
}
@@ -1911,6 +1989,14 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
storage_image_views[level] =
MakeStorageView(device, level, *original_image, storage_format);
}
} else if (True(flags & VideoCommon::ImageFlagBits::AcceleratedUpload)) {
const auto& device = runtime->device.GetLogical();
const VkFormat storage_format = UnswizzleStorageFormat(BytesPerBlock(info.format));
const VkImageViewType view_type = UnswizzleStorageViewType(info.type);
for (s32 level = 0; level < info.resources.levels; ++level) {
storage_image_views[level] =
MakeStorageView(device, level, *original_image, storage_format, view_type);
}
}
}
@@ -1927,48 +2013,6 @@ Image::~Image() {
}
}
void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) {
using VideoCore::Surface::BytesPerBlock;
const u32 block_bytes = BytesPerBlock(info.format); // 8 for BC1, 16 for BC6H
const u32 block_width = 4;
const u32 block_height = 4;
// BCn is 4x4x1 blocks
const u32 blocks_x = (info.size.width + block_width - 1) / block_width;
const u32 blocks_y = (info.size.height + block_height - 1) / block_height;
const u32 blocks_z = (std::min)(max_slices, info.size.depth);
const u64 block_count =
static_cast<u64>(blocks_x) *
static_cast<u64>(blocks_y) *
static_cast<u64>(blocks_z);
const VkDeviceSize required_size = block_count * block_bytes;
if (has_compute_unswizzle_buffer && required_size <= compute_unswizzle_buffer_size) {
return;
}
compute_unswizzle_buffer_size = required_size;
VkBufferCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = compute_unswizzle_buffer_size,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
compute_unswizzle_buffer =
runtime->memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal);
has_compute_unswizzle_buffer = true;
}
void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
std::span<const VideoCommon::BufferImageCopy> copies) {
// TODO: Move this to another API
@@ -2326,11 +2370,15 @@ VkImageView Image::StorageImageView(s32 level) noexcept {
if (!view) {
auto format_info =
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
} else if (True(flags & VideoCommon::ImageFlagBits::AcceleratedUpload)) {
format_info.format = UnswizzleStorageFormat(BytesPerBlock(info.format));
view_type = UnswizzleStorageViewType(info.type);
}
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
format_info.format);
format_info.format, view_type);
}
return *view;
}
@@ -3129,25 +3177,20 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
void TextureCacheRuntime::AccelerateImageUpload(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_start, u32 z_count) {
std::span<const VideoCommon::SwizzleParameters> swizzles) {
if (IsPixelFormatASTC(image.info.format)) {
return astc_decoder_pass->Assemble(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");
}
ASSERT(false);
return;
switch (image.info.type) {
case ImageType::e2D:
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
case ImageType::e3D:
return bl_unswizzle_3d_pass->Unswizzle(image, map, swizzles);
case ImageType::Linear:
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
default:
break;
}
if (bl3d_unswizzle_pass && IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D && image.info.resources.levels == 1 && image.info.resources.layers == 1) {
return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_start, z_count);
}
ASSERT(false);
}
@@ -52,7 +52,7 @@ public:
void Finish();
StagingBufferRef UploadStagingBuffer(size_t size, bool deferred = false);
StagingBufferRef UploadStagingBuffer(size_t size);
StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
@@ -98,8 +98,7 @@ public:
}
void AccelerateImageUpload(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_start, u32 z_count);
std::span<const VideoCommon::SwizzleParameters>);
void InsertUploadMemoryBarrier() {}
@@ -157,8 +156,9 @@ public:
BlitImageHelper& blit_image_helper;
RenderPassCache& render_pass_cache;
std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
std::optional<BlockLinearUnswizzleImage2DPass> bl_unswizzle_2d_pass;
std::optional<BlockLinearUnswizzleImage3DPass> bl_unswizzle_3d_pass;
std::optional<PitchUnswizzlePass> pitch_unswizzle_pass;
const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
@@ -334,8 +334,6 @@ public:
void DownloadMemory(const StagingBufferRef& map,
std::span<const VideoCommon::BufferImageCopy> copies);
void AllocateComputeUnswizzleImage();
[[nodiscard]] VkImage Handle() const noexcept {
return *(this->*current_image);
}
@@ -361,10 +359,6 @@ public:
bool ScaleDown(bool ignore = false);
u64 allocation_tick;
friend class BlockLinearUnswizzle3DPass;
private:
bool BlitScaleHelper(bool scale_up);
@@ -376,12 +370,6 @@ private:
vk::Image original_image;
vk::Image scaled_image;
vk::Buffer compute_unswizzle_buffer;
VkDeviceSize compute_unswizzle_buffer_size = 0;
bool has_compute_unswizzle_buffer = false;
void AllocateComputeUnswizzleBuffer(u32 max_slices);
// Use a pointer to field because it is relative, so that the object can be
// moved without breaking the reference.
vk::Image Image::*current_image{};