shader_recompiler: Improvements to array and cube handling. (#2083)

* shader_recompiler: Account for instruction array flag in image type.

* shader_recompiler: Check da flag for all mimg instructions.

* shader_recompiler: Convert cube images into 2D arrays.

* shader_recompiler: Move image resource functions into sharp type.

* shader_recompiler: Use native AMD cube instructions when possible.

* specialization: Fix buffer storage mistake.
This commit is contained in:
squidbus 2025-01-10 00:48:12 -08:00 committed by GitHub
parent 93402620de
commit 725814ce01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 217 additions and 144 deletions

View file

@ -226,15 +226,13 @@ struct Image {
return pitch + 1;
}
u32 NumLayers(bool is_array) const {
u32 slices = GetType() == ImageType::Color3D ? 1 : depth + 1;
if (GetType() == ImageType::Cube) {
if (is_array) {
slices = last_array + 1;
ASSERT(slices % 6 == 0);
} else {
slices = 6;
}
[[nodiscard]] u32 NumLayers() const noexcept {
u32 slices = depth + 1;
const auto img_type = static_cast<ImageType>(type);
if (img_type == ImageType::Color3D) {
slices = 1;
} else if (img_type == ImageType::Cube) {
slices *= 6;
}
if (pow2pad) {
slices = std::bit_ceil(slices);
@ -257,7 +255,8 @@ struct Image {
}
ImageType GetType() const noexcept {
return static_cast<ImageType>(type);
const auto img_type = static_cast<ImageType>(type);
return img_type == ImageType::Cube ? ImageType::Color2DArray : img_type;
}
DataFormat GetDataFmt() const noexcept {
@ -289,13 +288,40 @@ struct Image {
GetDataFmt() <= DataFormat::FormatFmask64_8;
}
bool IsPartialCubemap() const {
const auto viewed_slice = last_array - base_array + 1;
return GetType() == ImageType::Cube && viewed_slice < 6;
[[nodiscard]] ImageType GetBoundType(const bool is_array) const noexcept {
const auto base_type = GetType();
if (base_type == ImageType::Color1DArray && !is_array) {
return ImageType::Color1D;
}
if (base_type == ImageType::Color2DArray && !is_array) {
return ImageType::Color2D;
}
if (base_type == ImageType::Color2DMsaaArray && !is_array) {
return ImageType::Color2DMsaa;
}
return base_type;
}
ImageType GetBoundType() const noexcept {
return IsPartialCubemap() ? ImageType::Color2DArray : GetType();
[[nodiscard]] u32 NumViewLevels(const bool is_array) const noexcept {
switch (GetBoundType(is_array)) {
case ImageType::Color2DMsaa:
case ImageType::Color2DMsaaArray:
return 1;
default:
return last_level - base_level + 1;
}
}
[[nodiscard]] u32 NumViewLayers(const bool is_array) const noexcept {
switch (GetBoundType(is_array)) {
case ImageType::Color1D:
case ImageType::Color2D:
case ImageType::Color2DMsaa:
case ImageType::Color3D:
return 1;
default:
return last_array - base_array + 1;
}
}
};
static_assert(sizeof(Image) == 32); // 256bits

View file

@ -59,9 +59,8 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler
for (const auto& image : info->images) {
bindings.push_back({
.binding = binding++,
.descriptorType = image.IsStorage(image.GetSharp(*info))
? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.descriptorType = image.is_written ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eCompute,
});

View file

@ -367,9 +367,8 @@ void GraphicsPipeline::BuildDescSetLayout() {
for (const auto& image : stage->images) {
bindings.push_back({
.binding = binding++,
.descriptorType = image.IsStorage(image.GetSharp(*stage))
? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.descriptorType = image.is_written ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.descriptorCount = 1,
.stageFlags = gp_stage_flags,
});

View file

@ -271,6 +271,7 @@ bool Instance::CreateDevice() {
maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME);
image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME);
amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME);
// These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2
// with extensions.

View file

@ -159,6 +159,11 @@ public:
return image_load_store_lod;
}
/// Returns true when VK_AMD_gcn_shader is supported.
bool IsAmdGcnShaderSupported() const {
return amd_gcn_shader;
}
/// Returns true when geometry shaders are supported by the device
bool IsGeometryStageSupported() const {
return features.geometryShader;
@ -334,6 +339,7 @@ private:
bool list_restart{};
bool legacy_vertex_attributes{};
bool image_load_store_lod{};
bool amd_gcn_shader{};
u64 min_imported_host_pointer_alignment{};
u32 subgroup_size{};
bool tooling_info{};

View file

@ -204,6 +204,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
.support_explicit_workgroup_layout = true,
.support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(),
.supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(),
.supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(),
.needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() &&
instance.GetDriverID() == vk::DriverId::eNvidiaProprietary,
.needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary ||

View file

@ -661,7 +661,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
if (image->binding.is_bound) {
// The image is already bound. In case if it is about to be used as storage we need
// to force general layout on it.
image->binding.force_general |= image_desc.IsStorage(tsharp);
image->binding.force_general |= image_desc.is_written;
}
if (image->binding.is_target) {
// The image is already bound as target. Since we read and output to it need to force

View file

@ -153,13 +153,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
// the texture cache should re-create the resource with the usage requested
vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat |
vk::ImageCreateFlagBits::eExtendedUsage};
const bool can_be_cube =
(info.type == vk::ImageType::e2D) &&
((info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6)) == 0) &&
(info.size.width == info.size.height);
if (info.props.is_cube || can_be_cube) {
flags |= vk::ImageCreateFlagBits::eCubeCompatible;
} else if (info.props.is_volume) {
if (info.props.is_volume) {
flags |= vk::ImageCreateFlagBits::e2DArrayCompatible;
}

View file

@ -37,7 +37,6 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept {
return vk::ImageType::e1D;
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color2DMsaa:
case AmdGpu::ImageType::Cube:
case AmdGpu::ImageType::Color2DArray:
return vk::ImageType::e2D;
case AmdGpu::ImageType::Color3D:
@ -130,7 +129,6 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
}
type = ConvertImageType(image.GetType());
props.is_tiled = image.IsTiled();
props.is_cube = image.GetType() == AmdGpu::ImageType::Cube;
props.is_volume = image.GetType() == AmdGpu::ImageType::Color3D;
props.is_pow2 = image.pow2pad;
props.is_block = IsBlockCoded();
@ -139,7 +137,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
size.depth = props.is_volume ? image.depth + 1 : 1;
pitch = image.Pitch();
resources.levels = image.NumLevels();
resources.layers = image.NumLayers(desc.is_array);
resources.layers = image.NumLayers();
num_samples = image.NumSamples();
num_bits = NumBits(image.GetDataFmt());

View file

@ -61,7 +61,6 @@ struct ImageInfo {
} meta_info{};
struct {
u32 is_cube : 1;
u32 is_volume : 1;
u32 is_tiled : 1;
u32 is_pow2 : 1;

View file

@ -20,8 +20,6 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) {
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color2DMsaa:
return vk::ImageViewType::e2D;
case AmdGpu::ImageType::Cube:
return vk::ImageViewType::eCube;
case AmdGpu::ImageType::Color2DArray:
return vk::ImageViewType::e2DArray;
case AmdGpu::ImageType::Color3D:
@ -32,7 +30,7 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) {
}
ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept
: is_storage{desc.IsStorage(image)} {
: is_storage{desc.is_written} {
const auto dfmt = image.GetDataFmt();
auto nfmt = image.GetNumberFmt();
if (is_storage && nfmt == AmdGpu::NumberFormat::Srgb) {
@ -42,30 +40,12 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso
if (desc.is_depth) {
format = Vulkan::LiverpoolToVK::PromoteFormatToDepth(format);
}
range.base.level = image.base_level;
range.base.layer = image.base_array;
if (image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) {
range.extent.levels = 1;
} else {
range.extent.levels = image.last_level - image.base_level + 1;
}
range.extent.layers = image.last_array - image.base_array + 1;
type = ConvertImageViewType(image.GetBoundType());
// Adjust view type for arrays
if (type == vk::ImageViewType::eCube) {
if (desc.is_array) {
type = vk::ImageViewType::eCubeArray;
} else {
// Some games try to bind an array of cubemaps while shader reads only single one.
range.extent.layers = std::min(range.extent.layers, 6u);
}
}
if (type == vk::ImageViewType::e3D && range.extent.layers > 1) {
// Some games pass incorrect layer count for 3D textures so we need to fixup it.
range.extent.layers = 1;
}
range.extent.levels = image.NumViewLevels(desc.is_array);
range.extent.layers = image.NumViewLayers(desc.is_array);
type = ConvertImageViewType(image.GetBoundType(desc.is_array));
if (!is_storage) {
mapping = Vulkan::LiverpoolToVK::ComponentMapping(image.DstSelect());

View file

@ -65,7 +65,7 @@ public:
struct TextureDesc : public BaseDesc {
TextureDesc() = default;
TextureDesc(const AmdGpu::Image& image, const Shader::ImageResource& desc)
: BaseDesc{desc.IsStorage(image) ? BindingType::Storage : BindingType::Texture,
: BaseDesc{desc.is_written ? BindingType::Storage : BindingType::Texture,
ImageInfo{image, desc}, ImageViewInfo{image, desc}} {}
};