Re-add unswizzle linear 2D to the pass

This commit is contained in:
CamilleLaVey
2026-09-19 03:30:18 -04:00
parent e9436f8094
commit 3cabafc06a
4 changed files with 200 additions and 53 deletions
@@ -17,6 +17,7 @@
#include "common/div_ceil.h"
#include "common/vector_math.h"
#include "video_core/host_shaders/astc_decoder_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_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"
@@ -565,6 +566,72 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
}
}
namespace {
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 | VK_ACCESS_TRANSFER_WRITE_BIT;
old_layout = VK_IMAGE_LAYOUT_GENERAL;
src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
}
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 = 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);
});
}
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
ASTCDecoderPass::ASTCDecoderPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
@@ -587,37 +654,10 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
VideoCore::Surface::DefaultBlockHeight(image.info.format),
};
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) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(
is_initialized ? VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.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(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
});
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, 8U);
@@ -652,29 +692,55 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
});
}
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);
});
RecordUnswizzleEndBarrier(scheduler, vk_image, aspect_mask);
}
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(VideoCommon::Accelerated::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);
}
} // namespace Vulkan
@@ -153,4 +153,21 @@ private:
MemoryAllocator& memory_allocator;
};
class BlockLinearUnswizzleImage2DPass final : public ComputePass {
public:
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& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
} // namespace Vulkan
@@ -158,6 +158,52 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return 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 IsUnswizzleAcceleratedFormat(const Device& device, PixelFormat format) {
if (!device.IsStorageBuffer8BitAccessSupported() ||
!device.IsStorageBuffer16BitAccessSupported()) {
return false;
}
if (IsPixelFormatASTC(format) || VideoCore::Surface::IsPixelFormatBCn(format)) {
return false;
}
if (VideoCore::Surface::GetFormatType(format) != SurfaceType::ColorTexture) {
return false;
}
const VkFormat storage_format = UnswizzleStorageFormat(BytesPerBlock(format));
if (storage_format == VK_FORMAT_UNDEFINED) {
return false;
}
return device.IsFormatSupported(storage_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
FormatType::Optimal);
}
[[nodiscard]] bool WillUseAcceleratedUnswizzle(const Device& device, const ImageInfo& info) {
if (info.type != ImageType::e2D || info.num_samples > 1) {
return false;
}
if (!device.IsKhrImageFormatListSupported()) {
return false;
}
return IsUnswizzleAcceleratedFormat(device, info.format);
}
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
std::optional<VkFormat> format_override = {}) {
auto format_info =
@@ -944,6 +990,11 @@ 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() && device.IsStorageBuffer8BitAccessSupported() &&
device.IsStorageBuffer16BitAccessSupported()) {
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue);
}
if (!device.IsKhrImageFormatListSupported()) {
return;
}
@@ -951,6 +1002,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);
@@ -1894,6 +1947,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());
}
@@ -2273,6 +2330,8 @@ 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 (True(flags & VideoCommon::ImageFlagBits::AcceleratedUpload)) {
format_info.format = UnswizzleStorageFormat(BytesPerBlock(info.format));
}
view = MakeStorageView(runtime->device.GetLogical(), level, *original_image,
format_info.format);
@@ -3079,7 +3138,11 @@ void TextureCacheRuntime::AccelerateImageUpload(
if (is_rescaled) {
image.ScaleDown(true);
}
astc_decoder_pass->Assemble(image, map, swizzles);
if (IsPixelFormatASTC(image.info.format)) {
astc_decoder_pass->Assemble(image, map, swizzles);
} else {
bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
}
if (is_rescaled) {
image.ScaleUp();
}
@@ -156,6 +156,7 @@ public:
BlitImageHelper& blit_image_helper;
RenderPassCache& render_pass_cache;
std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BlockLinearUnswizzleImage2DPass> bl_unswizzle_2d_pass;
const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;