Browse Source

Attempt to improve the GPU unswizzle speed

pull/3737/head
Forrest Mark X 1 month ago
committed by crueter
parent
commit
8de624b632
  1. 20
      src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp
  2. 170
      src/video_core/renderer_vulkan/vk_compute_pass.cpp
  3. 16
      src/video_core/renderer_vulkan/vk_compute_pass.h

20
src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp

@ -75,6 +75,12 @@ layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[]; uint out_u32[];
}; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU64 {
uvec2 out_u64[];
};
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU128 {
uvec4 out_u128[];
};
// --- Constants --- // --- Constants ---
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in; layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
@ -153,12 +159,14 @@ void main() {
uint block_index = block_coord.x + uint block_index = block_coord.x +
(block_coord.y * pc.blocks_dim.x) + (block_coord.y * pc.blocks_dim.x) +
(block_coord.z * pc.blocks_dim.x * pc.blocks_dim.y); (block_coord.z * pc.blocks_dim.x * pc.blocks_dim.y);
uint out_idx = block_index * (bytes_per_block >> 2u);
out_u32[out_idx] = texel.x;
out_u32[out_idx + 1u] = texel.y;
if (pc.bytes_per_block_log2 == 4u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
if (pc.bytes_per_block_log2 == 4u) { // 16 bytes (BC3/BC7)
out_u128[block_index] = texel;
} else if (pc.bytes_per_block_log2 == 3u) { // 8 bytes (BC1/BC4)
out_u64[block_index] = texel.xy;
} else {
uint out_idx = block_index * (bytes_per_block >> 2u);
out_u32[out_idx] = texel.x;
if ((bytes_per_block >> 2u) > 1u) out_u32[out_idx + 1u] = texel.y;
} }
} }

170
src/video_core/renderer_vulkan/vk_compute_pass.cpp

@ -725,15 +725,38 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
: VK_IMAGE_LAYOUT_UNDEFINED; : VK_IMAGE_LAYOUT_UNDEFINED;
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask(), initial_prior_layout](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE) {
return;
}
const VkAccessFlags src_access = initial_prior_layout == VK_IMAGE_LAYOUT_GENERAL
? (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT) : 0u;
const VkPipelineStageFlags src_stage = initial_prior_layout == VK_IMAGE_LAYOUT_GENERAL
? (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT) : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const VkImageMemoryBarrier pre_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = src_access,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = initial_prior_layout,
.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},
};
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, pre_barrier);
});
for (u32 z_offset = 0; z_offset < z_count; z_offset += MAX_BATCH_SLICES) { 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_chunk_slices = (std::min)(MAX_BATCH_SLICES, z_count - z_offset);
const u32 current_z_src = z_src_start + z_offset; const u32 current_z_src = z_src_start + z_offset;
const u32 current_z_dst = z_image_start + z_offset; const u32 current_z_dst = z_image_start + z_offset;
const VkImageLayout prior_layout = (z_offset == 0)
? initial_prior_layout
: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
bool chunk_has_data = slice_has_data.empty(); bool chunk_has_data = slice_has_data.empty();
if (!chunk_has_data) { if (!chunk_has_data) {
const u32 z_src_end = current_z_src + current_chunk_slices; const u32 z_src_end = current_z_src + current_chunk_slices;
@ -747,12 +770,33 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
if (chunk_has_data) { if (chunk_has_data) {
UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y,
current_z_src, current_z_dst, current_chunk_slices, prior_layout);
current_z_src, current_z_dst, current_chunk_slices);
} else { } else {
UnswizzleZeroChunk(image, blocks_x, blocks_y, bytes_per_block, UnswizzleZeroChunk(image, blocks_x, blocks_y, bytes_per_block,
current_z_dst, current_chunk_slices, prior_layout);
current_z_dst, current_chunk_slices);
} }
} }
scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask()](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE) return;
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_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 = {aspect, 0, 1, 0, 1},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, post_barrier);
});
} }
void BlockLinearUnswizzle3DPass::UnswizzleChunk( void BlockLinearUnswizzle3DPass::UnswizzleChunk(
@ -761,10 +805,10 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
const VideoCommon::SwizzleParameters& sw, const VideoCommon::SwizzleParameters& sw,
const BlockLinearSwizzle3DParams& params, const BlockLinearSwizzle3DParams& params,
u32 blocks_x, u32 blocks_y, u32 blocks_x, u32 blocks_y,
u32 z_src, u32 z_dst, u32 z_count,
VkImageLayout prior_image_layout)
u32 z_src, u32 z_dst, u32 z_count)
{ {
BlockLinearUnswizzle3DPushConstants pc{}; BlockLinearUnswizzle3DPushConstants pc{};
pc.origin[0] = params.origin[0]; pc.origin[0] = params.origin[0];
pc.origin[1] = params.origin[1]; pc.origin[1] = params.origin[1];
pc.origin[2] = z_src; // Current chunk's Z start pc.origin[2] = z_src; // Current chunk's Z start
@ -789,7 +833,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
compute_pass_descriptor_queue.Acquire(scheduler, 3); compute_pass_descriptor_queue.Acquire(scheduler, 3);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, compute_pass_descriptor_queue.AddBuffer(swizzled.buffer,
sw.buffer_offset + swizzled.offset, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset);
VK_WHOLE_SIZE);
compute_pass_descriptor_queue.AddBuffer(*image.compute_unswizzle_buffer, 0, compute_pass_descriptor_queue.AddBuffer(*image.compute_unswizzle_buffer, 0,
image.compute_unswizzle_buffer_size); image.compute_unswizzle_buffer_size);
@ -805,13 +849,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
static_cast<VkDeviceSize>(blocks_x) * blocks_y * bytes_per_block; static_cast<VkDeviceSize>(blocks_x) * blocks_y * bytes_per_block;
const VkDeviceSize barrier_size = output_slice_size * z_count; const VkDeviceSize barrier_size = output_slice_size * z_count;
const VkAccessFlags src_access =
(prior_image_layout == VK_IMAGE_LAYOUT_UNDEFINED)
? VkAccessFlags{}
: (prior_image_layout == VK_IMAGE_LAYOUT_GENERAL)
? static_cast<VkAccessFlags>(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT)
: static_cast<VkAccessFlags>(VK_ACCESS_TRANSFER_WRITE_BIT);
const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkBuffer out_buffer = *image.compute_unswizzle_buffer;
const VkImage dst_image = image.Handle(); const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask(); const VkImageAspectFlags aspect = image.AspectMask();
@ -820,10 +857,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz,
z_dst, z_count, barrier_size, z_dst, z_count, barrier_size,
prior_image_layout, src_access,
out_buffer, dst_image, aspect, out_buffer, dst_image, aspect,
image_width, image_height
](vk::CommandBuffer cmdbuf) {
image_width, image_height](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return; return;
@ -848,26 +883,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
.size = barrier_size, .size = barrier_size,
}; };
// Image layout transition
const VkImageMemoryBarrier pre_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = src_access,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = prior_image_layout,
.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
);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, buffer_barrier);
// Copy chunk to correct Z position in image // Copy chunk to correct Z position in image
const VkBufferImageCopy copy{ const VkBufferImageCopy copy{
@ -880,27 +896,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
}; };
cmdbuf.CopyBufferToImage(out_buffer, dst_image, cmdbuf.CopyBufferToImage(out_buffer, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); 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,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_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 = {aspect, 0, 1, 0, 1},
};
cmdbuf.PipelineBarrier(
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0,
nullptr, nullptr, post_barrier
);
}); });
} }
@ -1006,8 +1001,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
Image& image, Image& image,
u32 blocks_x, u32 blocks_y, u32 blocks_x, u32 blocks_y,
u32 bytes_per_block, u32 bytes_per_block,
u32 z_dst, u32 z_count,
VkImageLayout prior_image_layout)
u32 z_dst, u32 z_count)
{ {
ASSERT(image.has_compute_unswizzle_buffer); ASSERT(image.has_compute_unswizzle_buffer);
@ -1024,17 +1018,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
static_cast<VkDeviceSize>(blocks_x) * blocks_y * bytes_per_block; static_cast<VkDeviceSize>(blocks_x) * blocks_y * bytes_per_block;
const VkDeviceSize fill_size = output_slice_bytes * z_count; const VkDeviceSize fill_size = output_slice_bytes * z_count;
const VkAccessFlags src_access =
(prior_image_layout == VK_IMAGE_LAYOUT_UNDEFINED)
? VkAccessFlags{}
: (prior_image_layout == VK_IMAGE_LAYOUT_GENERAL)
? static_cast<VkAccessFlags>(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT)
: static_cast<VkAccessFlags>(VK_ACCESS_TRANSFER_WRITE_BIT);
scheduler.Record([out_buffer, dst_image, aspect, z_dst, z_count, scheduler.Record([out_buffer, dst_image, aspect, z_dst, z_count,
fill_size, prior_image_layout, src_access,
image_width, image_height
](vk::CommandBuffer cmdbuf) {
fill_size, image_width, image_height](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return; return;
@ -1052,25 +1037,13 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer, .buffer = out_buffer,
.offset = 0, .offset = 0,
.size = fill_size,
};
const VkImageMemoryBarrier pre_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = src_access,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = prior_image_layout,
.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},
.size = fill_size,
}; };
cmdbuf.PipelineBarrier( cmdbuf.PipelineBarrier(
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, nullptr, buffer_barrier, pre_barrier
);
0, buffer_barrier);
// Copy the zeroed buffer region into the correct Z position of the image. // Copy the zeroed buffer region into the correct Z position of the image.
const VkBufferImageCopy copy{ const VkBufferImageCopy copy{
@ -1082,26 +1055,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
.imageExtent = {image_width, image_height, z_count}, .imageExtent = {image_width, image_height, z_count},
}; };
cmdbuf.CopyBufferToImage(out_buffer, dst_image, cmdbuf.CopyBufferToImage(out_buffer, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
// Transition image to GENERAL for subsequent shader reads/writes.
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_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 = {aspect, 0, 1, 0, 1},
};
cmdbuf.PipelineBarrier(
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, nullptr, nullptr, post_barrier
);
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
}); });
} }

16
src/video_core/renderer_vulkan/vk_compute_pass.h

@ -153,20 +153,18 @@ public:
bool image_already_uploaded); bool image_already_uploaded);
void UnswizzleChunk( void UnswizzleChunk(
Image& image,
const StagingBufferRef& swizzled,
const VideoCommon::SwizzleParameters& sw,
const BlockLinearSwizzle3DParams& params,
Image &image,
const StagingBufferRef &swizzled,
const VideoCommon::SwizzleParameters &sw,
const BlockLinearSwizzle3DParams &params,
u32 blocks_x, u32 blocks_y, u32 blocks_x, u32 blocks_y,
u32 z_src, u32 z_dst, u32 z_count,
VkImageLayout prior_image_layout);
u32 z_src, u32 z_dst, u32 z_count);
void UnswizzleZeroChunk( void UnswizzleZeroChunk(
Image& image,
Image &image,
u32 blocks_x, u32 blocks_y, u32 blocks_x, u32 blocks_y,
u32 bytes_per_block, u32 bytes_per_block,
u32 z_dst, u32 z_count,
VkImageLayout prior_image_layout);
u32 z_dst, u32 z_count);
private: private:
Scheduler& scheduler; Scheduler& scheduler;

Loading…
Cancel
Save