mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
shader: support bit-packed image surfaces
This commit is contained in:
@@ -13,8 +13,8 @@ struct FormatInfo {
|
|||||||
uint32_t bytes_per_element;
|
uint32_t bytes_per_element;
|
||||||
uint32_t block_compressed_bytes_per_block;
|
uint32_t block_compressed_bytes_per_block;
|
||||||
uint32_t render_target_bytes_per_element;
|
uint32_t render_target_bytes_per_element;
|
||||||
bool supported_sampled_texture_format;
|
bool sampled_texture;
|
||||||
bool unsigned_integer_texture_format;
|
bool uint_texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr FormatInfo kFormatInfo[] = {
|
constexpr FormatInfo kFormatInfo[] = {
|
||||||
@@ -202,18 +202,22 @@ uint32_t RenderTargetBytesPerElement(BufferFormat format) {
|
|||||||
return info != nullptr ? info->render_target_bytes_per_element : 0;
|
return info != nullptr ? info->render_target_bytes_per_element : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSupportedTextureFormat(BufferFormat format) {
|
|
||||||
const auto* info = FindFormatInfo(format);
|
|
||||||
return info != nullptr && info->supported_sampled_texture_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUintTextureFormat(BufferFormat format) {
|
|
||||||
const auto* info = FindFormatInfo(format);
|
|
||||||
return info != nullptr && info->unsigned_integer_texture_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsFmaskTextureFormat(BufferFormat format) {
|
bool IsFmaskTextureFormat(BufferFormat format) {
|
||||||
return format == BufferFormat::kFmask8_S4_F4;
|
return format == BufferFormat::kFmask8_S4_F4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSampledTextureFormat(BufferFormat format) {
|
||||||
|
const auto* info = FindFormatInfo(format);
|
||||||
|
return info != nullptr && info->sampled_texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUintTextureFormat(BufferFormat format) {
|
||||||
|
const auto* info = FindFormatInfo(format);
|
||||||
|
return info != nullptr && info->uint_texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferFormat RemapTextureFormat(BufferFormat format) {
|
||||||
|
return format == BufferFormat::k11_11_10UInt ? BufferFormat::k32UInt : format;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics::Prospero
|
} // namespace Libs::Graphics::Prospero
|
||||||
|
|||||||
@@ -41,9 +41,10 @@ RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, Chann
|
|||||||
uint32_t NumBytesPerElement(BufferFormat format);
|
uint32_t NumBytesPerElement(BufferFormat format);
|
||||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
|
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
|
||||||
uint32_t RenderTargetBytesPerElement(BufferFormat format);
|
uint32_t RenderTargetBytesPerElement(BufferFormat format);
|
||||||
bool IsSupportedTextureFormat(BufferFormat format);
|
|
||||||
bool IsUintTextureFormat(BufferFormat format);
|
|
||||||
bool IsFmaskTextureFormat(BufferFormat format);
|
bool IsFmaskTextureFormat(BufferFormat format);
|
||||||
|
bool IsSampledTextureFormat(BufferFormat format);
|
||||||
|
bool IsUintTextureFormat(BufferFormat format);
|
||||||
|
BufferFormat RemapTextureFormat(BufferFormat format);
|
||||||
|
|
||||||
} // namespace Libs::Graphics::Prospero
|
} // namespace Libs::Graphics::Prospero
|
||||||
|
|
||||||
|
|||||||
@@ -119,13 +119,16 @@ vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
|
|||||||
return components;
|
return components;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Format TextureGetFormat(Prospero::BufferFormat format) {
|
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
|
||||||
const auto vk_format = VulkanFormat(format);
|
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||||
|
const auto vk_format = VulkanFormat(backing_format);
|
||||||
|
const auto conversion_format =
|
||||||
|
backing_format != format ? format : Prospero::BufferFormat::kInvalid;
|
||||||
if (vk_format != vk::Format::eUndefined) {
|
if (vk_format != vk::Format::eUndefined) {
|
||||||
return vk_format;
|
return SurfaceFormatInfo(vk_format, conversion_format);
|
||||||
}
|
}
|
||||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||||
return vk::Format::eUndefined;
|
return SurfaceFormatInfo(vk::Format::eUndefined, Prospero::BufferFormat::kInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "common/abi.h"
|
#include "common/abi.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "graphics/guest_gpu/gpu_defs.h"
|
#include "graphics/guest_gpu/gpu_format.h"
|
||||||
#include "graphics/guest_gpu/tile.h"
|
#include "graphics/guest_gpu/tile.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
@@ -19,6 +19,15 @@ struct RenderTargetFormatInfo {
|
|||||||
Prospero::ColorComponentMapping export_mapping;
|
Prospero::ColorComponentMapping export_mapping;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SurfaceFormatInfo {
|
||||||
|
explicit constexpr SurfaceFormatInfo(vk::Format format,
|
||||||
|
Prospero::BufferFormat conversion_format)
|
||||||
|
: vk_format(format), conversion_format(conversion_format) {}
|
||||||
|
|
||||||
|
vk::Format vk_format;
|
||||||
|
Prospero::BufferFormat conversion_format;
|
||||||
|
};
|
||||||
|
|
||||||
struct TextureUploadMipLayout {
|
struct TextureUploadMipLayout {
|
||||||
uint64_t offset = 0;
|
uint64_t offset = 0;
|
||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
@@ -35,7 +44,7 @@ struct TextureUploadLayout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
|
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
|
||||||
vk::Format TextureGetFormat(Prospero::BufferFormat format);
|
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format);
|
||||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||||
Prospero::ChannelType type,
|
Prospero::ChannelType type,
|
||||||
Prospero::ChannelOrder order);
|
Prospero::ChannelOrder order);
|
||||||
|
|||||||
@@ -261,8 +261,7 @@ bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, c
|
|||||||
descriptor.BaseArray5() <= descriptor.Depth() &&
|
descriptor.BaseArray5() <= descriptor.Depth() &&
|
||||||
descriptor.Depth() < image.info.resources.layers;
|
descriptor.Depth() < image.info.resources.layers;
|
||||||
const bool levels_ok =
|
const bool levels_ok =
|
||||||
multisampled
|
multisampled ? descriptor.BaseLevel() == 0 && descriptor.LastLevel() >= 1 &&
|
||||||
? descriptor.BaseLevel() == 0 && descriptor.LastLevel() >= 1 &&
|
|
||||||
descriptor.LastLevel() <= 3 &&
|
descriptor.LastLevel() <= 3 &&
|
||||||
(r128 || descriptor.MaxMip() == descriptor.LastLevel()) &&
|
(r128 || descriptor.MaxMip() == descriptor.LastLevel()) &&
|
||||||
image.info.resources.levels == 1 && image.info.samples == samples
|
image.info.resources.levels == 1 && image.info.samples == samples
|
||||||
@@ -422,8 +421,7 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
|||||||
!descriptor.MsaaDepth();
|
!descriptor.MsaaDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsSupportedStorageTextureEncoding(
|
static bool IsSupportedStorageTextureEncoding(const ShaderRecompiler::IR::ImageResource& resource,
|
||||||
const ShaderRecompiler::IR::ImageResource& resource,
|
|
||||||
const ShaderTextureResource& descriptor) {
|
const ShaderTextureResource& descriptor) {
|
||||||
constexpr uint32_t field1_reserved_mask = 0x200fff00u;
|
constexpr uint32_t field1_reserved_mask = 0x200fff00u;
|
||||||
constexpr uint32_t field2_reserved_mask = 0xf0003000u;
|
constexpr uint32_t field2_reserved_mask = 0xf0003000u;
|
||||||
@@ -458,7 +456,8 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
|||||||
const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource &&
|
const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource &&
|
||||||
resource.written && !resource.read && !resource.atomic;
|
resource.written && !resource.read && !resource.atomic;
|
||||||
const bool format_ok =
|
const bool format_ok =
|
||||||
raw_sint_storage || (Prospero::IsSupportedTextureFormat(format) &&
|
raw_sint_storage ||
|
||||||
|
(Prospero::IsSampledTextureFormat(format) &&
|
||||||
uint_resource == Prospero::IsUintTextureFormat(format) &&
|
uint_resource == Prospero::IsUintTextureFormat(format) &&
|
||||||
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
|
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
|
||||||
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
|
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
|
||||||
@@ -567,15 +566,17 @@ static void PopulateTextureMipLayout(ImageInfo& info) {
|
|||||||
|
|
||||||
static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& resource,
|
static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& resource,
|
||||||
const ShaderTextureResource& descriptor, vk::Format format,
|
const ShaderTextureResource& descriptor, vk::Format format,
|
||||||
bool storage, uint32_t view_levels, uint32_t image_layers) {
|
bool shader_conversion, bool storage,
|
||||||
|
uint32_t view_levels, uint32_t image_layers) {
|
||||||
ImageViewInfo view {};
|
ImageViewInfo view {};
|
||||||
view.format = format;
|
view.format = format;
|
||||||
view.aspect = vk::ImageAspectFlagBits::eColor;
|
view.aspect = vk::ImageAspectFlagBits::eColor;
|
||||||
view.base_level = descriptor.BaseLevel();
|
view.base_level = descriptor.BaseLevel();
|
||||||
view.level_count = view_levels;
|
view.level_count = view_levels;
|
||||||
view.usage = storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlagBits::eSampled;
|
view.usage = storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlagBits::eSampled;
|
||||||
view.mapping =
|
view.mapping = storage || shader_conversion
|
||||||
storage ? vk::ComponentMapping {} : TextureGetComponentMapping(descriptor.DstSelXYZW());
|
? vk::ComponentMapping {}
|
||||||
|
: TextureGetComponentMapping(descriptor.DstSelXYZW());
|
||||||
switch (resource.dimension) {
|
switch (resource.dimension) {
|
||||||
case ShaderRecompiler::Decoder::ImageDimension::Dim1D:
|
case ShaderRecompiler::Decoder::ImageDimension::Dim1D:
|
||||||
view.type = vk::ImageViewType::e1D;
|
view.type = vk::ImageViewType::e1D;
|
||||||
@@ -658,8 +659,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
||||||
if ((!multisampled && (base_level > view_last_level || view_last_level >= levels)) ||
|
if ((!multisampled && (base_level > view_last_level || view_last_level >= levels)) ||
|
||||||
(multisampled &&
|
(multisampled &&
|
||||||
(base_level != 0 || last_level == 0 || last_level > 3 || max_mip != last_level || !msaa_tile ||
|
(base_level != 0 || last_level == 0 || last_level > 3 || max_mip != last_level ||
|
||||||
(descriptor.MsaaDepth() && !depth_tile) ||
|
!msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
||||||
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
||||||
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
||||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
||||||
@@ -677,8 +678,13 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
multisampled ? 1u : static_cast<uint32_t>(view_last_level - base_level) + 1u;
|
multisampled ? 1u : static_cast<uint32_t>(view_last_level - base_level) + 1u;
|
||||||
const auto depth = static_cast<uint32_t>(descriptor.Depth()) + 1u;
|
const auto depth = static_cast<uint32_t>(descriptor.Depth()) + 1u;
|
||||||
const auto format = descriptor.Format();
|
const auto format = descriptor.Format();
|
||||||
|
const auto surface_format = TextureGetSurfaceFormatInfo(format);
|
||||||
|
const bool shader_conversion =
|
||||||
|
surface_format.conversion_format != Prospero::BufferFormat::kInvalid;
|
||||||
const bool sampled_numeric_class =
|
const bool sampled_numeric_class =
|
||||||
storage || ((resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) ==
|
storage ||
|
||||||
|
(Prospero::IsSampledTextureFormat(format) &&
|
||||||
|
(resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) ==
|
||||||
Prospero::IsUintTextureFormat(format));
|
Prospero::IsUintTextureFormat(format));
|
||||||
if (!storage &&
|
if (!storage &&
|
||||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
(resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||||
@@ -715,7 +721,7 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
ValidateStorageTexture(resource, descriptor, size.size);
|
ValidateStorageTexture(resource, descriptor, size.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pixel_format = TextureGetFormat(format);
|
const auto pixel_format = surface_format.vk_format;
|
||||||
const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt
|
const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt
|
||||||
? vk::Format::eR32Uint
|
? vk::Format::eR32Uint
|
||||||
: SrgbStorageViewFormat(pixel_format);
|
: SrgbStorageViewFormat(pixel_format);
|
||||||
@@ -740,11 +746,11 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
} else {
|
} else {
|
||||||
PopulateTextureMipLayout(desc.info);
|
PopulateTextureMipLayout(desc.info);
|
||||||
}
|
}
|
||||||
desc.view_info = TextureViewInfo(resource, descriptor, view_format, storage, view_levels,
|
desc.view_info = TextureViewInfo(resource, descriptor, view_format, shader_conversion, storage,
|
||||||
desc.info.resources.layers);
|
view_levels, desc.info.resources.layers);
|
||||||
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
||||||
|
|
||||||
auto id = texture_cache.FindImage(desc);
|
auto id = texture_cache.FindImage(desc, shader_conversion);
|
||||||
auto* image = &texture_cache.GetImage(id);
|
auto* image = &texture_cache.GetImage(id);
|
||||||
const bool stencil_association = static_cast<bool>(image->depth_id);
|
const bool stencil_association = static_cast<bool>(image->depth_id);
|
||||||
if (stencil_association) {
|
if (stencil_association) {
|
||||||
@@ -780,6 +786,9 @@ static vk::Sampler NativeSampler(RenderContext& context,
|
|||||||
if (!depth_compare) {
|
if (!depth_compare) {
|
||||||
descriptor.fields[0] &= ~(0x7u << 12u);
|
descriptor.fields[0] &= ~(0x7u << 12u);
|
||||||
}
|
}
|
||||||
|
if (program.info.samplers[index].force_point_filtering) {
|
||||||
|
descriptor.SetPointFiltering();
|
||||||
|
}
|
||||||
return context.GetSamplerCache().GetSampler(descriptor);
|
return context.GetSamplerCache().GetSampler(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include "graphics/guest_gpu/gpu_format.h"
|
||||||
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -207,9 +208,9 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool
|
|||||||
if (dref) return value;
|
if (dref) return value;
|
||||||
const auto component = gather ? index : DmaskComponent(mem.dmask, index);
|
const auto component = gather ? index : DmaskComponent(mem.dmask, index);
|
||||||
const auto result = ctx.state.builder.AllocateId();
|
const auto result = ctx.state.builder.AllocateId();
|
||||||
ctx.state.builder.AddFunction(
|
ctx.state.builder.AddFunction({OpCompositeExtract,
|
||||||
{OpCompositeExtract, integer ? TypeU32(ctx.state) : TypeF32(ctx.state), result,
|
integer ? TypeU32(ctx.state) : TypeF32(ctx.state),
|
||||||
value, component});
|
result, value, component});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
for (uint32_t word = 0; word < mem.data_dwords; word++) {
|
for (uint32_t word = 0; word < mem.data_dwords; word++) {
|
||||||
@@ -221,8 +222,8 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool
|
|||||||
: (integer ? ConstantU32(ctx.state, 0) : ZeroF32(ctx.state));
|
: (integer ? ConstantU32(ctx.state, 0) : ZeroF32(ctx.state));
|
||||||
if (integer) {
|
if (integer) {
|
||||||
const auto mask = ConstantU32(ctx.state, 0xffffu);
|
const auto mask = ConstantU32(ctx.state, 0xffffu);
|
||||||
packed[word] = Binary(
|
packed[word] =
|
||||||
ctx.state, OpBitwiseOr, TypeU32(ctx.state),
|
Binary(ctx.state, OpBitwiseOr, TypeU32(ctx.state),
|
||||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), low, mask),
|
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), low, mask),
|
||||||
Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state),
|
Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state),
|
||||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), high, mask),
|
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), high, mask),
|
||||||
@@ -357,8 +358,120 @@ uint32_t InverseSwizzle(uint32_t swizzle, uint32_t component) {
|
|||||||
return UINT32_MAX;
|
return UINT32_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Format::BufferFormatInfo ImageConversionFormat(const EmitterState& state,
|
||||||
|
const IR::MemoryInfo& mem) {
|
||||||
|
const auto format = state.program.info.images[mem.resource].conversion_format;
|
||||||
|
if (format == Prospero::BufferFormat::kInvalid) return {};
|
||||||
|
const auto info = Format::GetFormatInfo(format);
|
||||||
|
EXIT_IF(!Prospero::IsSampledTextureFormat(format) ||
|
||||||
|
!Prospero::IsUintTextureFormat(format) ||
|
||||||
|
Prospero::RemapTextureFormat(format) == format ||
|
||||||
|
info.type != Format::ComponentType::Uint || !info.packed_bitfield ||
|
||||||
|
info.byte_size != sizeof(uint32_t) || info.component_count == 0u ||
|
||||||
|
info.component_count > 4u);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t UnpackImageTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t texel) {
|
||||||
|
const auto info = ImageConversionFormat(ctx.state, mem);
|
||||||
|
if (info.format == Prospero::BufferFormat::kInvalid) return texel;
|
||||||
|
|
||||||
|
const auto packed = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction({OpCompositeExtract, TypeU32(ctx.state), packed, texel, 0u});
|
||||||
|
uint32_t components[4] = {ConstantU32(ctx.state, 0), ConstantU32(ctx.state, 0),
|
||||||
|
ConstantU32(ctx.state, 0), ConstantU32(ctx.state, 0)};
|
||||||
|
for (uint32_t component = 0; component < info.component_count; component++) {
|
||||||
|
components[component] = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction({OpBitFieldUExtract, TypeU32(ctx.state),
|
||||||
|
components[component], packed,
|
||||||
|
ConstantU32(ctx.state, info.component_bit_offset[component]),
|
||||||
|
ConstantU32(ctx.state, info.component_bits[component])});
|
||||||
|
}
|
||||||
|
for (uint32_t component = info.component_count; component < 4u; component++) {
|
||||||
|
components[component] = components[component % info.component_count];
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto swizzle = ctx.state.program.info.images[mem.resource].shader_swizzle;
|
||||||
|
uint32_t selected[4] {};
|
||||||
|
for (uint32_t component = 0; component < 4u; component++) {
|
||||||
|
const auto selector = (swizzle >> (component * 3u)) & 7u;
|
||||||
|
if (selector == 1u) {
|
||||||
|
selected[component] = ConstantU32(ctx.state, 1u);
|
||||||
|
} else if (selector >= 4u) {
|
||||||
|
selected[component] = components[selector - 4u];
|
||||||
|
} else {
|
||||||
|
selected[component] = ConstantU32(ctx.state, 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto result = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), result,
|
||||||
|
selected[0], selected[1], selected[2], selected[3]});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t UnpackImageGather(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t gathered) {
|
||||||
|
const auto info = ImageConversionFormat(ctx.state, mem);
|
||||||
|
if (info.format == Prospero::BufferFormat::kInvalid) return gathered;
|
||||||
|
|
||||||
|
const auto component = ImageGatherComponent(mem.dmask);
|
||||||
|
const auto selector =
|
||||||
|
(ctx.state.program.info.images[mem.resource].shader_swizzle >> (component * 3u)) & 7u;
|
||||||
|
if (selector < 4u) {
|
||||||
|
const auto value = ConstantU32(ctx.state, selector == 1u ? 1u : 0u);
|
||||||
|
return ctx.state.builder.Constant(OpConstantComposite, TypeU32Vector(ctx.state, 4),
|
||||||
|
{value, value, value, value});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t values[4] {};
|
||||||
|
for (uint32_t lane = 0; lane < 4u; lane++) {
|
||||||
|
const auto packed = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction(
|
||||||
|
{OpCompositeExtract, TypeU32(ctx.state), packed, gathered, lane});
|
||||||
|
values[lane] = ctx.state.builder.AllocateId();
|
||||||
|
const auto physical = (selector - 4u) % info.component_count;
|
||||||
|
ctx.state.builder.AddFunction({OpBitFieldUExtract, TypeU32(ctx.state), values[lane], packed,
|
||||||
|
ConstantU32(ctx.state, info.component_bit_offset[physical]),
|
||||||
|
ConstantU32(ctx.state, info.component_bits[physical])});
|
||||||
|
}
|
||||||
|
const auto result = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), result,
|
||||||
|
values[0], values[1], values[2], values[3]});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PackImageTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t texel) {
|
||||||
|
const auto info = ImageConversionFormat(ctx.state, mem);
|
||||||
|
if (info.format == Prospero::BufferFormat::kInvalid) return texel;
|
||||||
|
|
||||||
|
auto packed = ConstantU32(ctx.state, 0u);
|
||||||
|
for (uint32_t component = 0; component < info.component_count; component++) {
|
||||||
|
const auto value = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction(
|
||||||
|
{OpCompositeExtract, TypeU32(ctx.state), value, texel, component});
|
||||||
|
const auto maximum =
|
||||||
|
ConstantU32(ctx.state, info.component_bits[component] == 32u
|
||||||
|
? UINT32_MAX
|
||||||
|
: (1u << info.component_bits[component]) - 1u);
|
||||||
|
const auto within = Binary(ctx.state, OpULessThan, TypeBool(ctx.state), value, maximum);
|
||||||
|
const auto clamped = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction(
|
||||||
|
{OpSelect, TypeU32(ctx.state), clamped, within, value, maximum});
|
||||||
|
const auto shifted =
|
||||||
|
info.component_bit_offset[component] == 0u
|
||||||
|
? clamped
|
||||||
|
: Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state), clamped,
|
||||||
|
ConstantU32(ctx.state, info.component_bit_offset[component]));
|
||||||
|
packed = Binary(ctx.state, OpBitwiseOr, TypeU32(ctx.state), packed, shifted);
|
||||||
|
}
|
||||||
|
const auto zero = ConstantU32(ctx.state, 0u);
|
||||||
|
const auto result = ctx.state.builder.AllocateId();
|
||||||
|
ctx.state.builder.AddFunction(
|
||||||
|
{OpCompositeConstruct, TypeU32Vector(ctx.state, 4), result, packed, zero, zero, zero});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t data, bool integer) {
|
uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t data, bool integer) {
|
||||||
const auto swizzle = ctx.state.program.info.images[mem.resource].storage_swizzle;
|
const auto swizzle = ctx.state.program.info.images[mem.resource].shader_swizzle;
|
||||||
uint32_t values[4] {};
|
uint32_t values[4] {};
|
||||||
const auto dmask = mem.dmask != 0u ? mem.dmask : 1u;
|
const auto dmask = mem.dmask != 0u ? mem.dmask : 1u;
|
||||||
for (uint32_t component = 0; component < 4u; component++) {
|
for (uint32_t component = 0; component < 4u; component++) {
|
||||||
@@ -380,15 +493,15 @@ uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
values[component] = integer ? raw
|
values[component] = integer ? raw
|
||||||
: mem.data_bits == 16u ? EmitF16BitsToF32(ctx.state, raw)
|
: mem.data_bits == 16u
|
||||||
: Unary(ctx.state, OpBitcast,
|
? EmitF16BitsToF32(ctx.state, raw)
|
||||||
TypeF32(ctx.state), raw);
|
: Unary(ctx.state, OpBitcast, TypeF32(ctx.state), raw);
|
||||||
}
|
}
|
||||||
const auto texel = ctx.state.builder.AllocateId();
|
const auto texel = ctx.state.builder.AllocateId();
|
||||||
ctx.state.builder.AddFunction(
|
ctx.state.builder.AddFunction(
|
||||||
{OpCompositeConstruct, integer ? TypeU32Vector(ctx.state, 4) : TypeF32Vector(ctx.state, 4),
|
{OpCompositeConstruct, integer ? TypeU32Vector(ctx.state, 4) : TypeF32Vector(ctx.state, 4),
|
||||||
texel, values[0], values[1], values[2], values[3]});
|
texel, values[0], values[1], values[2], values[3]});
|
||||||
return texel;
|
return PackImageTexel(ctx, mem, texel);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ImageAtomicOpcode(IR::ValueOpcode opcode) {
|
uint32_t ImageAtomicOpcode(IR::ValueOpcode opcode) {
|
||||||
@@ -468,7 +581,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color,
|
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color,
|
||||||
image, coord, ImageOperandsLodMask, LodU32(ctx, mem, *address, view)});
|
image, coord, ImageOperandsLodMask, LodU32(ctx, mem, *address, view)});
|
||||||
}
|
}
|
||||||
return ResultVector(ctx, color, integer, false, mem);
|
return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), integer, false,
|
||||||
|
mem);
|
||||||
}));
|
}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -491,6 +605,11 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto layout = Layout(mem, view);
|
const auto layout = Layout(mem, view);
|
||||||
const bool integer = mem.kind == IR::ResourceKind::ImageUint;
|
const bool integer = mem.kind == IR::ResourceKind::ImageUint;
|
||||||
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
||||||
|
if (dref && state.program.info.images[mem.resource].conversion_format !=
|
||||||
|
Prospero::BufferFormat::kInvalid) {
|
||||||
|
ctx.Fail(inst, "uses depth comparison with a packed integer image");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const auto coord =
|
const auto coord =
|
||||||
CoordF32(ctx, mem, *address, layout.coord, ImageViewCoordinateComponents(view));
|
CoordF32(ctx, mem, *address, layout.coord, ImageViewCoordinateComponents(view));
|
||||||
if (op == IR::ValueOpcode::ImageGatherRaw) {
|
if (op == IR::ValueOpcode::ImageGatherRaw) {
|
||||||
@@ -511,7 +630,10 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
sample,
|
sample,
|
||||||
sampled,
|
sampled,
|
||||||
coord,
|
coord,
|
||||||
ConstantU32(state, ImageGatherComponent(mem.dmask))};
|
ConstantU32(state, ImageConversionFormat(state, mem).format ==
|
||||||
|
Prospero::BufferFormat::kInvalid
|
||||||
|
? ImageGatherComponent(mem.dmask)
|
||||||
|
: 0u)};
|
||||||
if (HasFlag(mem, Decoder::ImageSampleFlagGatherHorizontal)) {
|
if (HasFlag(mem, Decoder::ImageSampleFlagGatherHorizontal)) {
|
||||||
words.push_back(ImageOperandsConstOffsetsMask);
|
words.push_back(ImageOperandsConstOffsetsMask);
|
||||||
words.push_back(HorizontalOffsets(state, view));
|
words.push_back(HorizontalOffsets(state, view));
|
||||||
@@ -520,7 +642,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
words.push_back(PackedOffset(ctx, mem, *address, layout, view));
|
words.push_back(PackedOffset(ctx, mem, *address, layout, view));
|
||||||
}
|
}
|
||||||
state.builder.AddFunction(words);
|
state.builder.AddFunction(words);
|
||||||
ctx.Define(inst, ResultVector(ctx, sample, integer && !dref, false, mem, true));
|
ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample),
|
||||||
|
integer && !dref, false, mem, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const bool explicit_lod = HasFlag(mem, Decoder::ImageSampleFlagDerivative) ||
|
const bool explicit_lod = HasFlag(mem, Decoder::ImageSampleFlagDerivative) ||
|
||||||
@@ -570,7 +693,9 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
};
|
};
|
||||||
const auto& image = state.program.info.images[mem.resource];
|
const auto& image = state.program.info.images[mem.resource];
|
||||||
if (image.indirect_root != mem.resource) {
|
if (image.indirect_root != mem.resource) {
|
||||||
ctx.Define(inst, ResultVector(ctx, EmitSample(mem.resource), integer, dref, mem));
|
const auto sample = EmitSample(mem.resource);
|
||||||
|
ctx.Define(inst, ResultVector(ctx, dref ? sample : UnpackImageTexel(ctx, mem, sample),
|
||||||
|
integer, dref, mem));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto* handle = image_arg.ResolveInstruction();
|
const auto* handle = image_arg.ResolveInstruction();
|
||||||
@@ -661,7 +786,9 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
}
|
}
|
||||||
EmitLabel(state, merge_label);
|
EmitLabel(state, merge_label);
|
||||||
state.builder.AddFunction(phi_words);
|
state.builder.AddFunction(phi_words);
|
||||||
ctx.Define(inst, ResultVector(ctx, phi_words[2], integer, dref, mem));
|
ctx.Define(inst,
|
||||||
|
ResultVector(ctx, dref ? phi_words[2] : UnpackImageTexel(ctx, mem, phi_words[2]),
|
||||||
|
integer, dref, mem));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto atomic_opcode = ImageAtomicOpcode(op);
|
const auto atomic_opcode = ImageAtomicOpcode(op);
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ struct BufferResource {
|
|||||||
|
|
||||||
enum class ImageMipMode { None, DynamicStorage };
|
enum class ImageMipMode { None, DynamicStorage };
|
||||||
|
|
||||||
constexpr uint32_t StorageImageIdentitySwizzle = 0x00000facu;
|
constexpr uint32_t ShaderImageIdentitySwizzle = 0x00000facu;
|
||||||
|
|
||||||
struct ImageResource {
|
struct ImageResource {
|
||||||
static constexpr uint32_t NoIndirectImage = UINT32_MAX;
|
static constexpr uint32_t NoIndirectImage = UINT32_MAX;
|
||||||
@@ -221,7 +221,8 @@ struct ImageResource {
|
|||||||
Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown;
|
Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown;
|
||||||
ImageMipMode mip_mode = ImageMipMode::None;
|
ImageMipMode mip_mode = ImageMipMode::None;
|
||||||
uint32_t mip_count = 1;
|
uint32_t mip_count = 1;
|
||||||
uint32_t storage_swizzle = StorageImageIdentitySwizzle;
|
Prospero::BufferFormat conversion_format = Prospero::BufferFormat::kInvalid;
|
||||||
|
uint32_t shader_swizzle = ShaderImageIdentitySwizzle;
|
||||||
bool read = false;
|
bool read = false;
|
||||||
bool written = false;
|
bool written = false;
|
||||||
bool atomic = false;
|
bool atomic = false;
|
||||||
@@ -239,6 +240,7 @@ struct ImageResource {
|
|||||||
struct SamplerResource {
|
struct SamplerResource {
|
||||||
uint32_t source = 0;
|
uint32_t source = 0;
|
||||||
uint32_t first_use_pc = 0;
|
uint32_t first_use_pc = 0;
|
||||||
|
bool force_point_filtering = false;
|
||||||
|
|
||||||
bool operator==(const SamplerResource& other) const = default;
|
bool operator==(const SamplerResource& other) const = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ bool ValidImageDescriptor(const DescriptorValue& descriptor, bool r128 = false)
|
|||||||
if (type < Prospero::ImageType::kColor1D || format == Prospero::BufferFormat::kInvalid) {
|
if (type < Prospero::ImageType::kColor1D || format == Prospero::BufferFormat::kInvalid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (r128 && type != Prospero::ImageType::kColor1D &&
|
if (r128 && type != Prospero::ImageType::kColor1D && type != Prospero::ImageType::kColor2D &&
|
||||||
type != Prospero::ImageType::kColor2D && type != Prospero::ImageType::kColor2DMsaa) {
|
type != Prospero::ImageType::kColor2DMsaa) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (type == Prospero::ImageType::kColor2DMsaa ||
|
if (type == Prospero::ImageType::kColor2DMsaa ||
|
||||||
@@ -77,6 +77,11 @@ uint32_t DescriptorImageSwizzle(const DescriptorValue& descriptor) {
|
|||||||
return descriptor.dwords[3] & 0xfffu;
|
return descriptor.dwords[3] & 0xfffu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Prospero::BufferFormat ImageConversionFormat(Prospero::BufferFormat format) {
|
||||||
|
return Prospero::RemapTextureFormat(format) != format ? format
|
||||||
|
: Prospero::BufferFormat::kInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
||||||
return static_cast<Prospero::ImageType>((descriptor.dwords[3] >> 28u) & 0xfu) ==
|
return static_cast<Prospero::ImageType>((descriptor.dwords[3] >> 28u) & 0xfu) ==
|
||||||
Prospero::ImageType::kCube;
|
Prospero::ImageType::kCube;
|
||||||
@@ -428,7 +433,8 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
|||||||
program.info.images[root].dimension != image.dimension ||
|
program.info.images[root].dimension != image.dimension ||
|
||||||
program.info.images[root].mip_mode != image.mip_mode ||
|
program.info.images[root].mip_mode != image.mip_mode ||
|
||||||
program.info.images[root].mip_count != image.mip_count ||
|
program.info.images[root].mip_count != image.mip_count ||
|
||||||
program.info.images[root].storage_swizzle != image.storage_swizzle ||
|
program.info.images[root].conversion_format != image.conversion_format ||
|
||||||
|
program.info.images[root].shader_swizzle != image.shader_swizzle ||
|
||||||
program.info.images[root].cube != image.cube) {
|
program.info.images[root].cube != image.cube) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = fmt::format(
|
*error = fmt::format(
|
||||||
@@ -471,17 +477,35 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
|||||||
image.kind == ResourceKind::StorageImageUint) {
|
image.kind == ResourceKind::StorageImageUint) {
|
||||||
const bool storage = image.kind == ResourceKind::StorageImage ||
|
const bool storage = image.kind == ResourceKind::StorageImage ||
|
||||||
image.kind == ResourceKind::StorageImageUint;
|
image.kind == ResourceKind::StorageImageUint;
|
||||||
if (storage && image.storage_swizzle != DescriptorImageSwizzle(descriptor)) {
|
const auto format =
|
||||||
|
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||||
|
if (image.atomic && format != Prospero::BufferFormat::k32UInt) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = fmt::format("storage image descriptor {} changed swizzle", i);
|
*error = fmt::format(
|
||||||
|
"atomic image descriptor {} changed to unsupported format {}", i,
|
||||||
|
static_cast<uint32_t>(format));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto format =
|
const auto conversion_format = ImageConversionFormat(format);
|
||||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
if (image.conversion_format != conversion_format) {
|
||||||
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
if (error != nullptr) {
|
||||||
|
*error = fmt::format("image descriptor {} changed format conversion", i);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((storage || conversion_format != Prospero::BufferFormat::kInvalid) &&
|
||||||
|
image.shader_swizzle != DescriptorImageSwizzle(descriptor)) {
|
||||||
|
if (error != nullptr) {
|
||||||
|
*error = fmt::format("image descriptor {} changed swizzle", i);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const bool raw_sint_storage =
|
||||||
|
storage && format == Prospero::BufferFormat::k32SInt && image.written &&
|
||||||
!image.read && !image.atomic;
|
!image.read && !image.atomic;
|
||||||
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
const bool uint_descriptor =
|
||||||
|
Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||||
image.kind == ResourceKind::StorageImageUint;
|
image.kind == ResourceKind::StorageImageUint;
|
||||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||||
@@ -811,16 +835,23 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
|||||||
}
|
}
|
||||||
image.dimension = descriptor_dimension;
|
image.dimension = descriptor_dimension;
|
||||||
image.cube = DescriptorIsCube(descriptor);
|
image.cube = DescriptorIsCube(descriptor);
|
||||||
if (image.kind == ResourceKind::StorageImage ||
|
|
||||||
image.kind == ResourceKind::StorageImageUint) {
|
|
||||||
image.storage_swizzle = DescriptorImageSwizzle(descriptor);
|
|
||||||
}
|
|
||||||
const auto format =
|
const auto format =
|
||||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||||
|
if (image.atomic && format != Prospero::BufferFormat::k32UInt) {
|
||||||
|
if (error != nullptr) {
|
||||||
|
*error = fmt::format("atomic image descriptor {} uses unsupported format {}", i,
|
||||||
|
static_cast<uint32_t>(format));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const bool storage = image.kind == ResourceKind::StorageImage ||
|
const bool storage = image.kind == ResourceKind::StorageImage ||
|
||||||
image.kind == ResourceKind::StorageImageUint;
|
image.kind == ResourceKind::StorageImageUint;
|
||||||
const bool raw_sint_storage =
|
image.conversion_format = ImageConversionFormat(format);
|
||||||
storage && format == Prospero::BufferFormat::k32SInt && !image.read && !image.atomic;
|
if (storage || image.conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||||
|
image.shader_swizzle = DescriptorImageSwizzle(descriptor);
|
||||||
|
}
|
||||||
|
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
||||||
|
image.written && !image.read && !image.atomic;
|
||||||
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||||
if (uint_image) {
|
if (uint_image) {
|
||||||
switch (image.kind) {
|
switch (image.kind) {
|
||||||
@@ -874,13 +905,15 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
|||||||
image.kind = image_class.kind;
|
image.kind = image_class.kind;
|
||||||
image.dimension = image_class.dimension;
|
image.dimension = image_class.dimension;
|
||||||
image.mip_count = image_class.mip_count;
|
image.mip_count = image_class.mip_count;
|
||||||
image.storage_swizzle = image_class.storage_swizzle;
|
image.conversion_format = image_class.conversion_format;
|
||||||
|
image.shader_swizzle = image_class.shader_swizzle;
|
||||||
image.cube = image_class.cube;
|
image.cube = image_class.cube;
|
||||||
}
|
}
|
||||||
if (image.kind != image_class.kind || image.dimension != image_class.dimension ||
|
if (image.kind != image_class.kind || image.dimension != image_class.dimension ||
|
||||||
image.mip_mode != image_class.mip_mode ||
|
image.mip_mode != image_class.mip_mode ||
|
||||||
image.mip_count != image_class.mip_count ||
|
image.mip_count != image_class.mip_count ||
|
||||||
image.storage_swizzle != image_class.storage_swizzle ||
|
image.conversion_format != image_class.conversion_format ||
|
||||||
|
image.shader_swizzle != image_class.shader_swizzle ||
|
||||||
image.depth_compare != image_class.depth_compare ||
|
image.depth_compare != image_class.depth_compare ||
|
||||||
image.cube != image_class.cube) {
|
image.cube != image_class.cube) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
@@ -899,9 +932,58 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto memory_info = program.values->memory_info;
|
auto memory_info = program.values->memory_info;
|
||||||
|
|
||||||
|
struct SamplerUsage {
|
||||||
|
bool native = false;
|
||||||
|
bool point_only = false;
|
||||||
|
};
|
||||||
|
std::vector<SamplerUsage> sampler_usage(next.samplers.size());
|
||||||
|
for (const auto& pair: next.sampled_pairs) {
|
||||||
|
if (pair.image >= next.images.size() || pair.sampler >= next.samplers.size()) {
|
||||||
|
if (error != nullptr) {
|
||||||
|
*error = fmt::format("sampled resource pair at pc 0x{:08x} is invalid",
|
||||||
|
pair.first_use_pc);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto& usage = sampler_usage[pair.sampler];
|
||||||
|
if (next.images[pair.image].conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||||
|
usage.point_only = true;
|
||||||
|
} else {
|
||||||
|
usage.native = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<uint32_t> point_sampler(next.samplers.size(), UINT32_MAX);
|
||||||
|
for (uint32_t i = 0; i < sampler_usage.size(); i++) {
|
||||||
|
if (!sampler_usage[i].point_only) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!sampler_usage[i].native) {
|
||||||
|
next.samplers[i].force_point_filtering = true;
|
||||||
|
point_sampler[i] = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (next.samplers.size() >= ShaderInfo::MaxSamplers) {
|
||||||
|
if (error != nullptr) {
|
||||||
|
*error = "point-filter sampler variants exceed the dense sampler resource limit";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
point_sampler[i] = static_cast<uint32_t>(next.samplers.size());
|
||||||
|
auto sampler = next.samplers[i];
|
||||||
|
sampler.force_point_filtering = true;
|
||||||
|
next.samplers.push_back(sampler);
|
||||||
|
next_snapshot.samplers.push_back(next_snapshot.samplers[i]);
|
||||||
|
}
|
||||||
|
for (auto& pair: next.sampled_pairs) {
|
||||||
|
if (next.images[pair.image].conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||||
|
pair.sampler = point_sampler[pair.sampler];
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const auto* block: program.values->blocks) {
|
for (const auto* block: program.values->blocks) {
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
if (ImageOpcodeInfoOf(inst.GetOpcode()).access == ImageAccess::None) {
|
const auto image_opcode = ImageOpcodeInfoOf(inst.GetOpcode());
|
||||||
|
if (image_opcode.access == ImageAccess::None) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto index = inst.Flags<MemoryFlags>().index;
|
const auto index = inst.Flags<MemoryFlags>().index;
|
||||||
@@ -921,6 +1003,11 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto& image = next.images[memory.resource];
|
const auto& image = next.images[memory.resource];
|
||||||
|
if (image_opcode.needs_sampler &&
|
||||||
|
image.conversion_format != Prospero::BufferFormat::kInvalid &&
|
||||||
|
memory.sampler < point_sampler.size()) {
|
||||||
|
memory.sampler = point_sampler[memory.sampler];
|
||||||
|
}
|
||||||
if (image.indirect_root == memory.resource &&
|
if (image.indirect_root == memory.resource &&
|
||||||
inst.GetOpcode() != ValueOpcode::ImageSampleRaw) {
|
inst.GetOpcode() != ValueOpcode::ImageSampleRaw) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
|
|||||||
@@ -1040,9 +1040,10 @@ static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Prog
|
|||||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||||
const auto& image = program.info.images[i];
|
const auto& image = program.info.images[i];
|
||||||
ret += fmt::format(
|
ret += fmt::format(
|
||||||
" i{}[kind={} dim={} mip={}/{} swizzle=0x{:03x} indirect={}/{}+{}]", i,
|
" i{}[kind={} dim={} mip={}/{} format={} swizzle=0x{:03x} indirect={}/{}+{}]", i,
|
||||||
static_cast<uint32_t>(image.kind), static_cast<uint32_t>(image.dimension),
|
static_cast<uint32_t>(image.kind), static_cast<uint32_t>(image.dimension),
|
||||||
static_cast<uint32_t>(image.mip_mode), image.mip_count, image.storage_swizzle,
|
static_cast<uint32_t>(image.mip_mode), image.mip_count,
|
||||||
|
static_cast<uint32_t>(image.conversion_format), image.shader_swizzle,
|
||||||
image.indirect_root, image.indirect_mapping_offset, image.indirect_mapping_capacity);
|
image.indirect_root, image.indirect_mapping_offset, image.indirect_mapping_capacity);
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
||||||
@@ -1083,7 +1084,8 @@ static void ShaderAppendNativeSpecialization(std::vector<uint32_t>&
|
|||||||
ids.push_back(static_cast<uint32_t>(image.cube));
|
ids.push_back(static_cast<uint32_t>(image.cube));
|
||||||
ids.push_back(static_cast<uint32_t>(image.mip_mode));
|
ids.push_back(static_cast<uint32_t>(image.mip_mode));
|
||||||
ids.push_back(image.mip_count);
|
ids.push_back(image.mip_count);
|
||||||
ids.push_back(image.storage_swizzle);
|
ids.push_back(static_cast<uint32_t>(image.conversion_format));
|
||||||
|
ids.push_back(image.shader_swizzle);
|
||||||
ids.push_back(image.indirect_root);
|
ids.push_back(image.indirect_root);
|
||||||
ids.push_back(image.indirect_mapping_offset);
|
ids.push_back(image.indirect_mapping_offset);
|
||||||
ids.push_back(image.indirect_mapping_capacity);
|
ids.push_back(image.indirect_mapping_capacity);
|
||||||
|
|||||||
@@ -157,6 +157,16 @@ struct ShaderSamplerResource {
|
|||||||
[[nodiscard]] bool PointPreclamp() const { return ((fields[2] >> 28u) & 0x1u) == 1; }
|
[[nodiscard]] bool PointPreclamp() const { return ((fields[2] >> 28u) & 0x1u) == 1; }
|
||||||
[[nodiscard]] bool AnisoOverride() const { return ((fields[2] >> 29u) & 0x1u) == 1; }
|
[[nodiscard]] bool AnisoOverride() const { return ((fields[2] >> 29u) & 0x1u) == 1; }
|
||||||
[[nodiscard]] bool BlendZeroPrt() const { return ((fields[2] >> 30u) & 0x1u) == 1; }
|
[[nodiscard]] bool BlendZeroPrt() const { return ((fields[2] >> 30u) & 0x1u) == 1; }
|
||||||
|
|
||||||
|
void SetPointFiltering() {
|
||||||
|
const bool mipmapped = static_cast<Prospero::SamplerMipFilter>(MipFilter()) !=
|
||||||
|
Prospero::SamplerMipFilter::kNone;
|
||||||
|
fields[2] &= ~(0xffu << 20u);
|
||||||
|
fields[2] |= 1u << 24u;
|
||||||
|
if (mipmapped) {
|
||||||
|
fields[2] |= static_cast<uint32_t>(Prospero::SamplerMipFilter::kPoint) << 26u;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderVertexInputBuffer {
|
struct ShaderVertexInputBuffer {
|
||||||
|
|||||||
@@ -864,6 +864,7 @@ struct TestCase {
|
|||||||
size_t storage_buffer_range_dwords = 0;
|
size_t storage_buffer_range_dwords = 0;
|
||||||
std::vector<u32> storage_buffer_offsets;
|
std::vector<u32> storage_buffer_offsets;
|
||||||
bool force_shader_data_storage = false;
|
bool force_shader_data_storage = false;
|
||||||
|
bool expected_force_point_sampler = false;
|
||||||
std::optional<uint64_t> flat_memory_base;
|
std::optional<uint64_t> flat_memory_base;
|
||||||
std::vector<u32> gds_initial;
|
std::vector<u32> gds_initial;
|
||||||
std::vector<u32> expected_gds;
|
std::vector<u32> expected_gds;
|
||||||
@@ -9946,30 +9947,52 @@ public:
|
|||||||
constexpr u32 width = 128, height = 128, levels = 8;
|
constexpr u32 width = 128, height = 128, levels = 8;
|
||||||
TileSurfaceLayout surface{};
|
TileSurfaceLayout surface{};
|
||||||
const TileSurfaceDescription description{
|
const TileSurfaceDescription description{
|
||||||
format, tile, TileSurfaceDimension::Dim2D, width, height, 1, levels, 1};
|
format, tile, TileSurfaceDimension::Dim2D, width, height, 1,
|
||||||
|
levels, 1};
|
||||||
TileSizeAlign total{};
|
TileSizeAlign total{};
|
||||||
TileGetTextureTotalSize(format, width, height, 1, levels, tile, false,
|
TileGetTextureTotalSize(format, width, height, 1, levels, tile, false,
|
||||||
total);
|
total);
|
||||||
const bool built = TileGetTiledTextureLayout(description, surface);
|
const bool built = TileGetTiledTextureLayout(description, surface);
|
||||||
constexpr u32 tail_xy[7][2] = {
|
const auto surface_format = TextureGetSurfaceFormatInfo(format);
|
||||||
{64, 0}, {0, 64}, {32, 0}, {0, 32}, {16, 0}, {8, 16}, {0, 24}};
|
constexpr u32 tail_xy[7][2] = {{64, 0}, {0, 64}, {32, 0}, {0, 32},
|
||||||
bool valid = built && Prospero::NumBytesPerElement(format) == 4 &&
|
{16, 0}, {8, 16}, {0, 24}};
|
||||||
|
bool valid =
|
||||||
|
built && Prospero::NumBytesPerElement(format) == 4 &&
|
||||||
Prospero::BlockCompressedBytesPerBlock(format) == 0 &&
|
Prospero::BlockCompressedBytesPerBlock(format) == 0 &&
|
||||||
Prospero::IsSupportedTextureFormat(format) &&
|
Prospero::IsSampledTextureFormat(format) &&
|
||||||
Prospero::IsUintTextureFormat(format) &&
|
Prospero::IsUintTextureFormat(format) &&
|
||||||
|
Prospero::RemapTextureFormat(format) ==
|
||||||
|
Prospero::BufferFormat::k32UInt &&
|
||||||
|
surface_format.vk_format == vk::Format::eR32Uint &&
|
||||||
|
surface_format.conversion_format == format &&
|
||||||
|
VulkanFormat(format) == vk::Format::eUndefined &&
|
||||||
surface.texture.block.block_width == 128 &&
|
surface.texture.block.block_width == 128 &&
|
||||||
surface.texture.block.block_height == 128 &&
|
surface.texture.block.block_height == 128 &&
|
||||||
surface.texture.block.block_depth == 1 &&
|
surface.texture.block.block_depth == 1 &&
|
||||||
surface.first_tail_level == 1 &&
|
surface.first_tail_level == 1 && surface.mips[0].offset == 0x10000 &&
|
||||||
surface.mips[0].offset == 0x10000 && total.size == 0x20000 &&
|
total.size == 0x20000 && total.align == 0x10000;
|
||||||
total.align == 0x10000;
|
|
||||||
for (u32 level = 1; level < levels && valid; level++) {
|
for (u32 level = 1; level < levels && valid; level++) {
|
||||||
valid &= surface.mips[level].offset == 0 &&
|
valid &= surface.mips[level].offset == 0 &&
|
||||||
surface.mips[level].tail_x == tail_xy[level - 1][0] &&
|
surface.mips[level].tail_x == tail_xy[level - 1][0] &&
|
||||||
surface.mips[level].tail_y == tail_xy[level - 1][1];
|
surface.mips[level].tail_y == tail_xy[level - 1][1];
|
||||||
}
|
}
|
||||||
Require(name, "11_11_10 uint layout", valid,
|
Require(name, "11_11_10 uint layout", valid,
|
||||||
"PS5 packed integer texture metadata or Standard64KB mip tail changed");
|
"PS5 packed integer texture metadata or Standard64KB mip tail "
|
||||||
|
"changed");
|
||||||
|
|
||||||
|
const auto storage_only_format = Prospero::BufferFormat::k32SInt;
|
||||||
|
const auto storage_only_surface =
|
||||||
|
TextureGetSurfaceFormatInfo(storage_only_format);
|
||||||
|
Require(
|
||||||
|
name, "storage-only surface mapping",
|
||||||
|
!Prospero::IsSampledTextureFormat(storage_only_format) &&
|
||||||
|
!Prospero::IsUintTextureFormat(storage_only_format) &&
|
||||||
|
Prospero::RemapTextureFormat(storage_only_format) ==
|
||||||
|
storage_only_format &&
|
||||||
|
storage_only_surface.vk_format == vk::Format::eR32Sint &&
|
||||||
|
storage_only_surface.conversion_format ==
|
||||||
|
Prospero::BufferFormat::kInvalid,
|
||||||
|
"storage-only native backing was conflated with sampled support");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -10914,11 +10937,17 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
|
|||||||
if (test.image_descriptor_swizzle != DstSel(4, 5, 6, 7)) {
|
if (test.image_descriptor_swizzle != DstSel(4, 5, 6, 7)) {
|
||||||
Require(test.name, "resource specialization",
|
Require(test.name, "resource specialization",
|
||||||
!compiled.program.info.images.empty() &&
|
!compiled.program.info.images.empty() &&
|
||||||
compiled.program.info.images[0].storage_swizzle ==
|
compiled.program.info.images[0].shader_swizzle ==
|
||||||
test.image_descriptor_swizzle,
|
test.image_descriptor_swizzle,
|
||||||
"storage image descriptor swizzle did not reach the specialized "
|
"storage image descriptor swizzle did not reach the specialized "
|
||||||
"program");
|
"program");
|
||||||
}
|
}
|
||||||
|
if (test.expected_force_point_sampler) {
|
||||||
|
Require(test.name, "sampler specialization",
|
||||||
|
compiled.program.info.samplers.size() == 1u &&
|
||||||
|
compiled.program.info.samplers[0].force_point_filtering,
|
||||||
|
"bit-packed sampled image did not force point filtering");
|
||||||
|
}
|
||||||
if (test.compile_only) {
|
if (test.compile_only) {
|
||||||
std::printf("[compute] %-32s ok\n", test.name);
|
std::printf("[compute] %-32s ok\n", test.name);
|
||||||
return;
|
return;
|
||||||
@@ -17658,6 +17687,72 @@ TestCase ImageLoadR32UintUsesIntegerSampledImage() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase ImageLoadPackedUintUnpacksAndSwizzles() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
std::vector<u32> code;
|
||||||
|
AppendVMovU32(&code, 20, 2);
|
||||||
|
AppendVMovU32(&code, 21, 1);
|
||||||
|
code.push_back(EncodeMimg0(0x00, 0xf));
|
||||||
|
code.push_back(EncodeMimg1(0, 20));
|
||||||
|
for (u32 component = 0; component < 4; component++) {
|
||||||
|
AppendStoreVgpr(&code, component, component);
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "ImageLoadPackedUintUnpacksAndSwizzles";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.expected = {0x2abu, 0x456u, 0x321u, 0x321u};
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::IMAGE_LOAD, O::BUFFER_STORE_DWORD,
|
||||||
|
O::S_ENDPGM};
|
||||||
|
test.sampled_image_rgba.resize(16);
|
||||||
|
test.sampled_image_rgba[6] = 0xaae2b321u;
|
||||||
|
test.sampled_image_format = vk::Format::eR32Uint;
|
||||||
|
test.sampled_image_dwords_per_pixel = 1;
|
||||||
|
test.user_data =
|
||||||
|
MakeSampledTextureData(Prospero::BufferFormat::k11_11_10UInt);
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.image_descriptor_swizzle = DstSel(6, 5, 4, 7);
|
||||||
|
test.required_spirv = {"OpImageFetch %v4uint", "OpBitFieldUExtract"};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase ImageSamplePackedUintConvertsSampleAndGather() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
std::vector<u32> code;
|
||||||
|
AppendVMovLiteral(&code, 20, 0x3f200000u);
|
||||||
|
AppendVMovLiteral(&code, 21, 0x3ec00000u);
|
||||||
|
code.push_back(EncodeMimg0(0x20, 0xf));
|
||||||
|
code.push_back(EncodeMimg1(0, 20));
|
||||||
|
code.push_back(EncodeMimg0(0x47, 0x2));
|
||||||
|
code.push_back(EncodeMimg1(4, 20));
|
||||||
|
for (u32 component = 0; component < 8; component++) {
|
||||||
|
AppendStoreVgpr(&code, component, component);
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "ImageSamplePackedUintConvertsSampleAndGather";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.expected = {0x2abu, 0x456u, 0x321u, 0x321u,
|
||||||
|
0x456u, 0x456u, 0x456u, 0x456u};
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GATHER4_LZ,
|
||||||
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.sampled_image_rgba = std::vector<u32>(16, 0xaae2b321u);
|
||||||
|
test.sampled_image_format = vk::Format::eR32Uint;
|
||||||
|
test.sampled_image_dwords_per_pixel = 1;
|
||||||
|
test.user_data =
|
||||||
|
MakeSampledTextureData(Prospero::BufferFormat::k11_11_10UInt);
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.image_descriptor_swizzle = DstSel(6, 5, 4, 7);
|
||||||
|
test.expected_force_point_sampler = true;
|
||||||
|
test.required_spirv = {"OpImageSampleExplicitLod", "OpImageGather",
|
||||||
|
"OpBitFieldUExtract"};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase ImageLoadR128IgnoresAdjacentMaskSgprs() {
|
TestCase ImageLoadR128IgnoresAdjacentMaskSgprs() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -18569,6 +18664,67 @@ TestCase ImageStoreR32UintUsesUintStorageImage() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase ImageStorePackedUintSaturatesChannels() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
std::vector<u32> code;
|
||||||
|
AppendVMovU32(&code, 20, 2);
|
||||||
|
AppendVMovU32(&code, 21, 1);
|
||||||
|
AppendVMovU32(&code, 22, 0);
|
||||||
|
AppendVMovU32(&code, 0, 0x800u);
|
||||||
|
AppendVMovLiteral(&code, 1, UINT32_MAX);
|
||||||
|
AppendVMovU32(&code, 2, 0x400u);
|
||||||
|
AppendVMovU32(&code, 3, 0x55u);
|
||||||
|
code.push_back(EncodeMimg0(0x08, 0xf));
|
||||||
|
code.push_back(EncodeMimg1(0, 20));
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
std::vector<u32> expected_image(16, 0);
|
||||||
|
expected_image[1 * 4 + 2] = UINT32_MAX;
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "ImageStorePackedUintSaturatesChannels";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::IMAGE_STORE, O::S_ENDPGM};
|
||||||
|
test.user_data =
|
||||||
|
MakeStorageTextureData(Prospero::BufferFormat::k11_11_10UInt);
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.image_descriptor_swizzle = DstSel(4, 5, 6, 0);
|
||||||
|
test.storage_image_r32ui = std::vector<u32>(16, 0);
|
||||||
|
test.expected_storage_image_r32ui = std::move(expected_image);
|
||||||
|
test.required_spirv = {"R32ui", "OpULessThan", "OpSelect"};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase ImageStorePackedUintHonorsSparseDmask() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
std::vector<u32> code;
|
||||||
|
AppendVMovU32(&code, 20, 2);
|
||||||
|
AppendVMovU32(&code, 21, 1);
|
||||||
|
AppendVMovU32(&code, 22, 0);
|
||||||
|
AppendVMovU32(&code, 0, 0x321u);
|
||||||
|
AppendVMovU32(&code, 1, 0x2abu);
|
||||||
|
code.push_back(EncodeMimg0(0x08, 0x5));
|
||||||
|
code.push_back(EncodeMimg1(0, 20));
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
std::vector<u32> expected_image(16, 0);
|
||||||
|
expected_image[1 * 4 + 2] = 0xaac00321u;
|
||||||
|
|
||||||
|
TestCase test;
|
||||||
|
test.name = "ImageStorePackedUintHonorsSparseDmask";
|
||||||
|
test.code = std::move(code);
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::IMAGE_STORE, O::S_ENDPGM};
|
||||||
|
test.user_data =
|
||||||
|
MakeStorageTextureData(Prospero::BufferFormat::k11_11_10UInt);
|
||||||
|
test.has_user_data = true;
|
||||||
|
test.image_descriptor_swizzle = DstSel(4, 5, 6, 0);
|
||||||
|
test.storage_image_r32ui = std::vector<u32>(16, 0);
|
||||||
|
test.expected_storage_image_r32ui = std::move(expected_image);
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase ComputeTgSizeSgprUsesWaveMetadata() {
|
TestCase ComputeTgSizeSgprUsesWaveMetadata() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -19157,6 +19313,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(BufferAtomicFMaxContendedWorkgroup);
|
AddCase(BufferAtomicFMaxContendedWorkgroup);
|
||||||
AddCase(ImageLoadVariants);
|
AddCase(ImageLoadVariants);
|
||||||
AddCase(ImageLoadR32UintUsesIntegerSampledImage);
|
AddCase(ImageLoadR32UintUsesIntegerSampledImage);
|
||||||
|
AddCase(ImageLoadPackedUintUnpacksAndSwizzles);
|
||||||
|
AddCase(ImageSamplePackedUintConvertsSampleAndGather);
|
||||||
AddCase(ImageLoadR128IgnoresAdjacentMaskSgprs);
|
AddCase(ImageLoadR128IgnoresAdjacentMaskSgprs);
|
||||||
AddCase(ImageLoad1DUsesScalarCoordinate);
|
AddCase(ImageLoad1DUsesScalarCoordinate);
|
||||||
AddCase(ImageLoad1DArrayUsesLayerCoordinate);
|
AddCase(ImageLoad1DArrayUsesLayerCoordinate);
|
||||||
@@ -19183,6 +19341,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(ImageStoreR32FloatUsesFormatlessStorageImage);
|
AddCase(ImageStoreR32FloatUsesFormatlessStorageImage);
|
||||||
AddCase(ImageStoreR32SintUsesRawUintView);
|
AddCase(ImageStoreR32SintUsesRawUintView);
|
||||||
AddCase(ImageStoreR32UintUsesUintStorageImage);
|
AddCase(ImageStoreR32UintUsesUintStorageImage);
|
||||||
|
AddCase(ImageStorePackedUintSaturatesChannels);
|
||||||
|
AddCase(ImageStorePackedUintHonorsSparseDmask);
|
||||||
AddCase(ComputeTgSizeSgprUsesWaveMetadata);
|
AddCase(ComputeTgSizeSgprUsesWaveMetadata);
|
||||||
AddCase(ImageAtomicVariants);
|
AddCase(ImageAtomicVariants);
|
||||||
AddCase(ImageAtomicGlc0DoesNotReturnOldValue);
|
AddCase(ImageAtomicGlc0DoesNotReturnOldValue);
|
||||||
@@ -21227,7 +21387,7 @@ void CheckStorageTextureDepthTileUploadLayout() {
|
|||||||
"1x1 R8_UINT depth tile lost its 64 KiB source footprint");
|
"1x1 R8_UINT depth tile lost its 64 KiB source footprint");
|
||||||
std::printf("[host] %-32s ok\n", "StorageTextureDepthTileUpload");
|
std::printf("[host] %-32s ok\n", "StorageTextureDepthTileUpload");
|
||||||
}
|
}
|
||||||
void CheckStorageImageSwizzleSpecializationId() {
|
void CheckImageSpecializationId() {
|
||||||
std::array<u32, 1> code{};
|
std::array<u32, 1> code{};
|
||||||
HW::ComputeShaderInfo regs{};
|
HW::ComputeShaderInfo regs{};
|
||||||
regs.cs_regs.data_addr = reinterpret_cast<uint64_t>(code.data());
|
regs.cs_regs.data_addr = reinterpret_cast<uint64_t>(code.data());
|
||||||
@@ -21239,7 +21399,10 @@ void CheckStorageImageSwizzleSpecializationId() {
|
|||||||
image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||||
identity_program.info.images.push_back(image);
|
identity_program.info.images.push_back(image);
|
||||||
auto rgb1_program = identity_program;
|
auto rgb1_program = identity_program;
|
||||||
rgb1_program.info.images[0].storage_swizzle = DstSel(4, 5, 6, 1);
|
rgb1_program.info.images[0].shader_swizzle = DstSel(4, 5, 6, 1);
|
||||||
|
auto converted_program = identity_program;
|
||||||
|
converted_program.info.images[0].conversion_format =
|
||||||
|
Prospero::BufferFormat::k11_11_10UInt;
|
||||||
auto mip1_program = identity_program;
|
auto mip1_program = identity_program;
|
||||||
mip1_program.info.images[0].mip_mode =
|
mip1_program.info.images[0].mip_mode =
|
||||||
ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
|
ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
|
||||||
@@ -21260,6 +21423,9 @@ void CheckStorageImageSwizzleSpecializationId() {
|
|||||||
auto rgb1_info = identity_info;
|
auto rgb1_info = identity_info;
|
||||||
rgb1_info.stage.program =
|
rgb1_info.stage.program =
|
||||||
std::make_shared<const ShaderRecompiler::IR::Program>(rgb1_program);
|
std::make_shared<const ShaderRecompiler::IR::Program>(rgb1_program);
|
||||||
|
auto converted_info = identity_info;
|
||||||
|
converted_info.stage.program =
|
||||||
|
std::make_shared<const ShaderRecompiler::IR::Program>(converted_program);
|
||||||
auto mip1_info = identity_info;
|
auto mip1_info = identity_info;
|
||||||
mip1_info.stage.program =
|
mip1_info.stage.program =
|
||||||
std::make_shared<const ShaderRecompiler::IR::Program>(mip1_program);
|
std::make_shared<const ShaderRecompiler::IR::Program>(mip1_program);
|
||||||
@@ -21275,17 +21441,109 @@ void CheckStorageImageSwizzleSpecializationId() {
|
|||||||
|
|
||||||
const auto identity_id = ShaderGetIdCS(regs, identity_info, true);
|
const auto identity_id = ShaderGetIdCS(regs, identity_info, true);
|
||||||
const auto rgb1_id = ShaderGetIdCS(regs, rgb1_info, true);
|
const auto rgb1_id = ShaderGetIdCS(regs, rgb1_info, true);
|
||||||
|
const auto converted_id = ShaderGetIdCS(regs, converted_info, true);
|
||||||
const auto mip1_id = ShaderGetIdCS(regs, mip1_info, true);
|
const auto mip1_id = ShaderGetIdCS(regs, mip1_info, true);
|
||||||
const auto mip3_id = ShaderGetIdCS(regs, mip3_info, true);
|
const auto mip3_id = ShaderGetIdCS(regs, mip3_info, true);
|
||||||
const auto array_id = ShaderGetIdCS(regs, array_info, true);
|
const auto array_id = ShaderGetIdCS(regs, array_info, true);
|
||||||
const auto cube_id = ShaderGetIdCS(regs, cube_info, true);
|
const auto cube_id = ShaderGetIdCS(regs, cube_info, true);
|
||||||
Require("StorageImageSwizzleSpecializationId", "pipeline cache key",
|
Require("ImageSpecializationId", "pipeline cache key",
|
||||||
identity_id != rgb1_id &&
|
identity_id != rgb1_id &&
|
||||||
identity_id.ids.size() == rgb1_id.ids.size() &&
|
identity_id.ids.size() == rgb1_id.ids.size() &&
|
||||||
|
identity_id != converted_id &&
|
||||||
|
identity_id.ids.size() == converted_id.ids.size() &&
|
||||||
mip1_id != mip3_id && mip1_id.ids.size() == mip3_id.ids.size() &&
|
mip1_id != mip3_id && mip1_id.ids.size() == mip3_id.ids.size() &&
|
||||||
array_id != cube_id && array_id.ids.size() == cube_id.ids.size(),
|
array_id != cube_id && array_id.ids.size() == cube_id.ids.size(),
|
||||||
"storage swizzle, mip-count, or cube variants share a pipeline ID");
|
"image conversion, swizzle, mip-count, or cube variants share a "
|
||||||
std::printf("[host] %-32s ok\n", "StorageImageSwizzlePipelineId");
|
"pipeline ID");
|
||||||
|
|
||||||
|
ShaderSamplerResource filtered_sampler{};
|
||||||
|
filtered_sampler.fields[2] =
|
||||||
|
(1u << 20u) | (3u << 22u) | (2u << 24u) | (2u << 26u);
|
||||||
|
filtered_sampler.SetPointFiltering();
|
||||||
|
Require("ImageSpecializationId", "forced point sampler",
|
||||||
|
filtered_sampler.XyMagFilter() == 0u &&
|
||||||
|
filtered_sampler.XyMinFilter() == 0u &&
|
||||||
|
filtered_sampler.ZFilter() == 1u &&
|
||||||
|
filtered_sampler.MipFilter() == 1u,
|
||||||
|
"point-only image format retained a filtered sampler mode");
|
||||||
|
|
||||||
|
ShaderSamplerResource base_mip_sampler{};
|
||||||
|
base_mip_sampler.fields[2] = (3u << 20u) | (1u << 22u) | (2u << 24u);
|
||||||
|
base_mip_sampler.SetPointFiltering();
|
||||||
|
Require("ImageSpecializationId", "base-mip point sampler",
|
||||||
|
base_mip_sampler.XyMagFilter() == 0u &&
|
||||||
|
base_mip_sampler.XyMinFilter() == 0u &&
|
||||||
|
base_mip_sampler.ZFilter() == 1u &&
|
||||||
|
base_mip_sampler.MipFilter() == 0u,
|
||||||
|
"forcing point filtering enabled mipmapping on a base-mip sampler");
|
||||||
|
|
||||||
|
ShaderRecompiler::IR::Program mixed_sampler_program;
|
||||||
|
mixed_sampler_program.values =
|
||||||
|
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||||
|
mixed_sampler_program.resource_tracking_complete = true;
|
||||||
|
ShaderRecompiler::IR::ImageResource sampled_image;
|
||||||
|
sampled_image.kind = ShaderRecompiler::IR::ResourceKind::Image;
|
||||||
|
sampled_image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||||
|
sampled_image.read = true;
|
||||||
|
mixed_sampler_program.info.images = {sampled_image, sampled_image};
|
||||||
|
mixed_sampler_program.info.samplers.push_back({0u, 4u});
|
||||||
|
mixed_sampler_program.info.sampled_pairs = {{0u, 0u, 8u}, {1u, 0u, 12u}};
|
||||||
|
|
||||||
|
ShaderRecompiler::IR::DescriptorValue native_image_descriptor{};
|
||||||
|
native_image_descriptor.dword_count = 8;
|
||||||
|
native_image_descriptor.dwords[0] = 0x1000u;
|
||||||
|
native_image_descriptor.dwords[1] =
|
||||||
|
static_cast<uint32_t>(Prospero::BufferFormat::k32UInt) << 20u;
|
||||||
|
native_image_descriptor.dwords[2] = 3u | (3u << 14u);
|
||||||
|
native_image_descriptor.dwords[3] =
|
||||||
|
DstSel(4, 5, 6, 7) |
|
||||||
|
(static_cast<uint32_t>(Prospero::ImageType::kColor2D) << 28u);
|
||||||
|
auto packed_image_descriptor = native_image_descriptor;
|
||||||
|
packed_image_descriptor.dwords[0] = 0x2000u;
|
||||||
|
packed_image_descriptor.dwords[1] =
|
||||||
|
static_cast<uint32_t>(Prospero::BufferFormat::k11_11_10UInt) << 20u;
|
||||||
|
|
||||||
|
ShaderRecompiler::IR::ResourceSnapshot mixed_sampler_snapshot;
|
||||||
|
mixed_sampler_snapshot.images = {native_image_descriptor,
|
||||||
|
packed_image_descriptor};
|
||||||
|
ShaderRecompiler::IR::DescriptorValue sampler_descriptor{};
|
||||||
|
sampler_descriptor.dword_count = 4;
|
||||||
|
mixed_sampler_snapshot.samplers.push_back(sampler_descriptor);
|
||||||
|
std::string specialization_error;
|
||||||
|
Require("ImageSpecializationId", "mixed sampler specialization",
|
||||||
|
ShaderRecompiler::IR::SpecializeResources(mixed_sampler_program,
|
||||||
|
mixed_sampler_snapshot,
|
||||||
|
&specialization_error),
|
||||||
|
specialization_error);
|
||||||
|
Require("ImageSpecializationId", "mixed sampler variant",
|
||||||
|
mixed_sampler_program.info.samplers.size() == 2u &&
|
||||||
|
!mixed_sampler_program.info.samplers[0].force_point_filtering &&
|
||||||
|
mixed_sampler_program.info.samplers[1].force_point_filtering &&
|
||||||
|
mixed_sampler_program.info.sampled_pairs[0].sampler == 0u &&
|
||||||
|
mixed_sampler_program.info.sampled_pairs[1].sampler == 1u &&
|
||||||
|
mixed_sampler_snapshot.samplers.size() == 2u,
|
||||||
|
"a shared native/bit-packed sampler was not split into point and "
|
||||||
|
"native variants");
|
||||||
|
|
||||||
|
ShaderRecompiler::IR::Program packed_atomic_program;
|
||||||
|
packed_atomic_program.values =
|
||||||
|
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||||
|
packed_atomic_program.resource_tracking_complete = true;
|
||||||
|
auto packed_atomic_image = sampled_image;
|
||||||
|
packed_atomic_image.kind =
|
||||||
|
ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||||
|
packed_atomic_image.written = true;
|
||||||
|
packed_atomic_image.atomic = true;
|
||||||
|
packed_atomic_program.info.images.push_back(packed_atomic_image);
|
||||||
|
ShaderRecompiler::IR::ResourceSnapshot packed_atomic_snapshot;
|
||||||
|
packed_atomic_snapshot.images.push_back(packed_image_descriptor);
|
||||||
|
std::string atomic_error;
|
||||||
|
Require("ImageSpecializationId", "packed atomic rejection",
|
||||||
|
!ShaderRecompiler::IR::SpecializeResources(
|
||||||
|
packed_atomic_program, packed_atomic_snapshot, &atomic_error) &&
|
||||||
|
atomic_error.find("unsupported format 34") != std::string::npos,
|
||||||
|
"a non-atomic PS5 packed format entered the typed atomic path");
|
||||||
|
std::printf("[host] %-32s ok\n", "ImageSpecializationPipelineId");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckStorageTextureVolumeUploadLayout() {
|
void CheckStorageTextureVolumeUploadLayout() {
|
||||||
@@ -22910,7 +23168,7 @@ int main(int argc, char **argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--indirect-image-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--indirect-image-only") == 0) {
|
||||||
CheckStorageImageSwizzleSpecializationId();
|
CheckImageSpecializationId();
|
||||||
CheckIndirectImageKeySwitch();
|
CheckIndirectImageKeySwitch();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -22952,7 +23210,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckBasicStorageTextureDescriptor();
|
CheckBasicStorageTextureDescriptor();
|
||||||
CheckStorageTextureLinearUploadLayout();
|
CheckStorageTextureLinearUploadLayout();
|
||||||
CheckStorageTextureDepthTileUploadLayout();
|
CheckStorageTextureDepthTileUploadLayout();
|
||||||
CheckStorageImageSwizzleSpecializationId();
|
CheckImageSpecializationId();
|
||||||
CheckStandard64RenderTargetTileRoundTrip();
|
CheckStandard64RenderTargetTileRoundTrip();
|
||||||
CheckStorageTextureVolumeUploadLayout();
|
CheckStorageTextureVolumeUploadLayout();
|
||||||
CheckStorageTextureVolumeMipRegions();
|
CheckStorageTextureVolumeMipRegions();
|
||||||
|
|||||||
Reference in New Issue
Block a user