mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-22 15:48:56 +00:00
[TEST] Debugger GPU unswizzle shaders
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 430
|
||||
|
||||
#ifdef VULKAN
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#define HAS_EXTENDED_TYPES 0
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
@@ -42,9 +40,6 @@ layout(push_constant) uniform PushConstants {
|
||||
|
||||
uint block_depth; // Offset 48
|
||||
uint block_depth_mask; // Offset 52
|
||||
int _pad; // Offset 56
|
||||
|
||||
ivec3 destination; // Offset 60
|
||||
} pc;
|
||||
#else
|
||||
BEGIN_PUSH_CONSTANTS
|
||||
|
||||
@@ -169,6 +169,7 @@ try
|
||||
}
|
||||
|
||||
RendererVulkan::~RendererVulkan() {
|
||||
scheduler.WaitWorker();
|
||||
scheduler.RegisterOnSubmit([] {});
|
||||
void(device.GetLogical().WaitIdle());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
#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_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_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"
|
||||
@@ -233,6 +236,26 @@ struct QueriesPrefixScanPushConstants {
|
||||
struct ConditionalRenderingResolvePushConstants {
|
||||
u32 compare_to_zero;
|
||||
};
|
||||
|
||||
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;
|
||||
u32 pitch;
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, DescriptorPool& descriptor_pool,
|
||||
@@ -654,6 +677,235 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
scheduler.Finish();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void RecordUnswizzleImageEntry(vk::CommandBuffer cmdbuf, VkPipeline pipeline, VkImage image,
|
||||
VkImageAspectFlags aspect_mask, bool is_initialized) {
|
||||
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_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 = 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, pipeline);
|
||||
}
|
||||
|
||||
void RecordUnswizzleImageExit(vk::CommandBuffer cmdbuf, VkImage image,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
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 = 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 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) {
|
||||
RecordUnswizzleImageEntry(cmdbuf, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
});
|
||||
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, 32U);
|
||||
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);
|
||||
});
|
||||
}
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
RecordUnswizzleImageExit(cmdbuf, 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 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) {
|
||||
RecordUnswizzleImageEntry(cmdbuf, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
});
|
||||
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, 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 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);
|
||||
});
|
||||
}
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
RecordUnswizzleImageExit(cmdbuf, 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) {
|
||||
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) {
|
||||
RecordUnswizzleImageEntry(cmdbuf, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
});
|
||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
|
||||
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, 32U);
|
||||
|
||||
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 = 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);
|
||||
});
|
||||
}
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
RecordUnswizzleImageExit(cmdbuf, vk_image, aspect_mask);
|
||||
});
|
||||
scheduler.Finish();
|
||||
}
|
||||
|
||||
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
|
||||
|
||||
@@ -671,10 +923,6 @@ struct alignas(16) BlockLinearUnswizzle3DPushConstants {
|
||||
|
||||
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);
|
||||
|
||||
@@ -781,10 +1029,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
||||
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;
|
||||
|
||||
@@ -25,6 +25,7 @@ struct SwizzleParameters;
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using VideoCommon::Accelerated::BlockLinearSwizzle2DParams;
|
||||
using VideoCommon::Accelerated::BlockLinearSwizzle3DParams;
|
||||
|
||||
class Device;
|
||||
@@ -165,6 +166,57 @@ private:
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
StagingBufferPool& staging_buffer_pool;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class MSAACopyPass final : public ComputePass {
|
||||
public:
|
||||
explicit MSAACopyPass(const Device& device_, Scheduler& scheduler_,
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -220,8 +222,50 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& 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(const Device& device, u32 bytes_per_block) {
|
||||
switch (bytes_per_block) {
|
||||
case 1:
|
||||
if (!device.IsStorageBuffer8BitAccessSupported()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!device.IsStorageBuffer16BitAccessSupported()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return device.IsFormatSupported(UnswizzleStorageFormat(bytes_per_block),
|
||||
VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, FormatType::Optimal);
|
||||
}
|
||||
|
||||
[[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,
|
||||
@@ -232,7 +276,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,
|
||||
@@ -915,10 +959,56 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
if (!device.IsKhrImageFormatListSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool enable_2d = Settings::values.gpu_compute_unswizzle_2d.GetValue();
|
||||
bool enable_3d = Settings::values.gpu_compute_unswizzle_3d.GetValue();
|
||||
bool enable_pitch = Settings::values.gpu_compute_unswizzle_pitch.GetValue();
|
||||
if (const char* const unswizzle_env = std::getenv("GPU_COMPUTE_UNSWIZZLE")) {
|
||||
const std::string_view selection{unswizzle_env};
|
||||
if (selection == "0" || selection == "none") {
|
||||
enable_2d = enable_3d = enable_pitch = false;
|
||||
} else if (selection != "1" && selection != "all") {
|
||||
enable_2d = selection.find("2d") != std::string_view::npos;
|
||||
enable_3d = selection.find("3d") != std::string_view::npos;
|
||||
enable_pitch = selection.find("pitch") != std::string_view::npos;
|
||||
}
|
||||
LOG_INFO(Render_Vulkan, "GPU_COMPUTE_UNSWIZZLE={} (2d={}, 3d={}, pitch={})", selection,
|
||||
enable_2d, enable_3d, enable_pitch);
|
||||
}
|
||||
const bool unswizzle_caps_ok = device.IsStorageBuffer8BitAccessSupported() &&
|
||||
device.IsStorageBuffer16BitAccessSupported() &&
|
||||
device.IsFormatlessImageStoreSupported();
|
||||
if (!unswizzle_caps_ok) {
|
||||
LOG_INFO(Render_Vulkan,
|
||||
"GPU image unswizzle passes unsupported (8bit={}, 16bit={}, formatless={})",
|
||||
device.IsStorageBuffer8BitAccessSupported(),
|
||||
device.IsStorageBuffer16BitAccessSupported(),
|
||||
device.IsFormatlessImageStoreSupported());
|
||||
} else {
|
||||
if (enable_2d) {
|
||||
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
}
|
||||
if (enable_3d) {
|
||||
bl_unswizzle_3d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
}
|
||||
if (enable_pitch) {
|
||||
pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
}
|
||||
}
|
||||
for (size_t index_a = 0; index_a < VideoCore::Surface::MaxPixelFormat; index_a++) {
|
||||
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 (HasImageUnswizzlePasses() && !IsPixelFormatBCn(image_format) &&
|
||||
VideoCore::Surface::GetFormatType(image_format) ==
|
||||
VideoCore::Surface::SurfaceType::ColorTexture) {
|
||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image_format);
|
||||
if (IsUnswizzleStorageFormatSupported(device, bytes_per_block)) {
|
||||
view_formats[index_a].push_back(UnswizzleStorageFormat(bytes_per_block));
|
||||
}
|
||||
}
|
||||
for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) {
|
||||
const auto view_format = static_cast<PixelFormat>(index_b);
|
||||
@@ -1611,6 +1701,14 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||
}
|
||||
if (runtime->HasUnswizzlePassFor(info.type) && !IsPixelFormatASTC(info.format) &&
|
||||
!IsPixelFormatBCn(info.format) && info.num_samples == 1 &&
|
||||
VideoCore::Surface::GetFormatType(info.format) ==
|
||||
VideoCore::Surface::SurfaceType::ColorTexture) {
|
||||
if (IsUnswizzleStorageFormatSupported(runtime->device, BytesPerBlock(info.format))) {
|
||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||
}
|
||||
}
|
||||
if (runtime->device.HasDebuggingToolAttached()) {
|
||||
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
||||
}
|
||||
@@ -1976,13 +2074,20 @@ 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 = WillUseWidenedAstcFormat(runtime->device, info)
|
||||
? VK_FORMAT_R32G32B32A32_SFLOAT
|
||||
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
} else if (True(flags & ImageFlagBits::AcceleratedUpload) &&
|
||||
!IsPixelFormatASTC(info.format) && !IsPixelFormatBCn(info.format)) {
|
||||
format_info.format = UnswizzleStorageFormat(BytesPerBlock(info.format));
|
||||
view_type = info.type == ImageType::e3D ? VK_IMAGE_VIEW_TYPE_3D
|
||||
: info.type == ImageType::Linear ? VK_IMAGE_VIEW_TYPE_2D
|
||||
: VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
}
|
||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
|
||||
format_info.format);
|
||||
format_info.format, view_type);
|
||||
}
|
||||
return *view;
|
||||
}
|
||||
@@ -2516,16 +2621,36 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
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");
|
||||
if (IsPixelFormatBCn(image.info.format)) {
|
||||
if (Settings::values.gpu_unswizzle_enabled.GetValue() && bl3d_unswizzle_pass &&
|
||||
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);
|
||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (HasImageUnswizzlePasses()) {
|
||||
switch (image.info.type) {
|
||||
case ImageType::e2D:
|
||||
if (bl_unswizzle_2d_pass) {
|
||||
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
break;
|
||||
case ImageType::e3D:
|
||||
if (bl_unswizzle_3d_pass) {
|
||||
return bl_unswizzle_3d_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
break;
|
||||
case ImageType::Linear:
|
||||
if (pitch_unswizzle_pass) {
|
||||
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(false);
|
||||
|
||||
@@ -130,7 +130,30 @@ public:
|
||||
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;
|
||||
std::optional<MSAACopyPass> msaa_copy_pass;
|
||||
|
||||
/// True when the buffer->image unswizzle pass for this image type is active.
|
||||
bool HasUnswizzlePassFor(VideoCommon::ImageType type) const noexcept {
|
||||
switch (type) {
|
||||
case VideoCommon::ImageType::e2D:
|
||||
return bl_unswizzle_2d_pass.has_value();
|
||||
case VideoCommon::ImageType::e3D:
|
||||
return bl_unswizzle_3d_pass.has_value();
|
||||
case VideoCommon::ImageType::Linear:
|
||||
return pitch_unswizzle_pass.has_value();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// True when any buffer->image unswizzle pass is active on this device.
|
||||
bool HasImageUnswizzlePasses() const noexcept {
|
||||
return bl_unswizzle_2d_pass.has_value() || bl_unswizzle_3d_pass.has_value() ||
|
||||
pitch_unswizzle_pass.has_value();
|
||||
}
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||
|
||||
|
||||
@@ -400,6 +400,16 @@ FN_MAX_LIMIT_LIST
|
||||
return features.bit16_storage.uniformAndStorageBuffer16BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports reading 8-bit values from a storage buffer.
|
||||
bool IsStorageBuffer8BitAccessSupported() const {
|
||||
return features.bit8_storage.storageBuffer8BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports reading 16-bit values from a storage buffer.
|
||||
bool IsStorageBuffer16BitAccessSupported() const {
|
||||
return features.bit16_storage.storageBuffer16BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports binding multisample images as storage images.
|
||||
bool IsStorageImageMultisampleSupported() const {
|
||||
return features.features.shaderStorageImageMultisample;
|
||||
@@ -430,6 +440,11 @@ FN_MAX_LIMIT_LIST
|
||||
return features.features.shaderStorageImageReadWithoutFormat;
|
||||
}
|
||||
|
||||
/// Returns true if formatless image store is supported.
|
||||
bool IsFormatlessImageStoreSupported() const {
|
||||
return features.features.shaderStorageImageWriteWithoutFormat;
|
||||
}
|
||||
|
||||
/// Returns true if shader int64 is supported.
|
||||
bool IsShaderInt64Supported() const {
|
||||
return features.features.shaderInt64;
|
||||
|
||||
Reference in New Issue
Block a user