mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-06-13 14:13:15 +00:00
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:
parent
93402620de
commit
725814ce01
28 changed files with 217 additions and 144 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}} {}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue