shader: simplify image resource semantics

This commit is contained in:
nmzik committed 2026-09-02 15:45:15 +02:00
1 parent 93be8c201b
commit 86e3c90e32
17 files changed
+381 -513

No files matched your search

@@ -103,11 +103,13 @@ SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t
[[nodiscard]] inline bool [[nodiscard]] inline bool
IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept { IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
if (!ShaderRecompiler::IR::ImageResourceKindMatches( if (resource.resource_class != ShaderRecompiler::IR::ImageResourceClass::Sampled) {
resource.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled)) {
return false; return false;
} }
if (resource.kind != ShaderRecompiler::IR::ResourceKind::Image && resource.depth_compare) { if (resource.numeric_class == Prospero::TextureNumericClass::Unsupported) {
return false;
}
if (resource.numeric_class != Prospero::TextureNumericClass::Float && resource.depth_compare) {
return false; return false;
} }
return (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D || return (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
@@ -128,8 +130,9 @@ inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_fo
[[nodiscard]] inline bool [[nodiscard]] inline bool
IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept { IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
return (resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImage || return resource.resource_class == ShaderRecompiler::IR::ImageResourceClass::Storage &&
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) && (resource.numeric_class == Prospero::TextureNumericClass::Float ||
resource.numeric_class == Prospero::TextureNumericClass::Uint) &&
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D || (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray || resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D || resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
@@ -141,19 +144,19 @@ IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resou
resource.mip_count != 0u)) && resource.mip_count != 0u)) &&
resource.written && resource.written &&
(!resource.atomic || (!resource.atomic ||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint && (resource.numeric_class == Prospero::TextureNumericClass::Uint && resource.read)) &&
resource.read)) &&
!resource.depth_compare; !resource.depth_compare;
} }
inline void inline void
ValidateStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept { ValidateStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
if (!IsSupportedStorageImageResource(resource)) { if (!IsSupportedStorageImageResource(resource)) {
EXIT("unsupported storage color image resource: kind=%u dimension=%u mip=%u " EXIT("unsupported storage color image resource: class=%u numeric=%u dimension=%u mip=%u "
"read=%d written=%d atomic=%d depth_compare=%d\n", "read=%d written=%d atomic=%d depth_compare=%d\n",
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.resource_class),
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, static_cast<uint32_t>(resource.numeric_class),
resource.atomic, resource.depth_compare); static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
resource.read, resource.written, resource.atomic, resource.depth_compare);
} }
} }
@@ -236,11 +236,10 @@ static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageRes
break; break;
default: break; default: break;
} }
const bool sampled_kind = ShaderRecompiler::IR::ImageResourceKindMatches( return resource.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled &&
resource.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled); resource.numeric_class != Prospero::TextureNumericClass::Unsupported &&
return sampled_kind && supported_dimension && supported_dimension && resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None &&
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read && resource.read && !resource.written && !resource.atomic && !resource.depth_compare;
!resource.written && !resource.atomic && !resource.depth_compare;
} }
TargetTextureViewInfo ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource, TargetTextureViewInfo ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource,
@@ -394,15 +393,17 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u, TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u,
descriptor.TileMode()); descriptor.TileMode());
EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d " EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d "
"kind=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d " "class=%u numeric=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d "
"guest_format=%u swizzle=0x%03x image_format=%d view_format=%d image_layers=%u " "guest_format=%u swizzle=0x%03x image_format=%d view_format=%d image_layers=%u "
"descriptor_type=%u base_array=%u depth=%u descriptor_pitch=%u target_pitch=%u " "descriptor_type=%u base_array=%u depth=%u descriptor_pitch=%u target_pitch=%u "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "addr=0x%016" PRIx64 " size=0x%016" PRIx64
" dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", " dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast<uint32_t>(resource.kind), resource_ok, descriptor_ok, encoding_ok, format_ok,
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode), static_cast<uint32_t>(resource.resource_class),
resource.read, resource.written, resource.atomic, resource.depth_compare, static_cast<uint32_t>(resource.numeric_class), static_cast<uint32_t>(resource.dimension),
static_cast<uint32_t>(descriptor.Format()), descriptor.DstSelXYZW(), static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, resource.atomic,
resource.depth_compare, static_cast<uint32_t>(descriptor.Format()),
descriptor.DstSelXYZW(),
image == nullptr ? static_cast<int>(vk::Format::eUndefined) image == nullptr ? static_cast<int>(vk::Format::eUndefined)
: static_cast<int>(image->info.pixel_format), : static_cast<int>(image->info.pixel_format),
static_cast<int>(view_format), image == nullptr ? 0u : image->info.resources.layers, static_cast<int>(view_format), image == nullptr ? 0u : image->info.resources.layers,
@@ -515,8 +516,7 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
const bool resource_ok = IsSupportedStorageImageResource(resource); const bool resource_ok = IsSupportedStorageImageResource(resource);
const bool descriptor_ok = IsSupportedStorageTextureDescriptor(resource, descriptor); const bool descriptor_ok = IsSupportedStorageTextureDescriptor(resource, descriptor);
const bool encoding_ok = IsSupportedStorageTextureEncoding(resource, descriptor); const bool encoding_ok = IsSupportedStorageTextureEncoding(resource, descriptor);
const bool uint_resource = const bool uint_resource = resource.numeric_class == Prospero::TextureNumericClass::Uint;
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
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 auto numeric_class = Prospero::SampledTextureNumericClass(format); const auto numeric_class = Prospero::SampledTextureNumericClass(format);
@@ -530,17 +530,19 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
return; return;
} }
EXIT("unsupported storage texture: resource=%d descriptor=%d encoding=%d format=%d " EXIT("unsupported storage texture: resource=%d descriptor=%d encoding=%d format=%d "
"kind=%u dimension=%u mip_mode=%u atomic=%d compare=%d " "class=%u numeric=%u dimension=%u mip_mode=%u atomic=%d compare=%d "
"base_level=%u last_level=%u max_mip=%u min_lod=%u base_array=%u bc=%u msaa=%d " "base_level=%u last_level=%u max_mip=%u min_lod=%u base_array=%u bc=%u msaa=%d "
"depth_tile_bpe=%u swizzle_ok=%d " "depth_tile_bpe=%u swizzle_ok=%d "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "addr=0x%016" PRIx64 " size=0x%016" PRIx64
" extent=%ux%ux%u type=%u format=%u tile=%u swizzle=0x%03x read=%d written=%d " " extent=%ux%ux%u type=%u format=%u tile=%u swizzle=0x%03x read=%d written=%d "
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", "dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast<uint32_t>(resource.kind), resource_ok, descriptor_ok, encoding_ok, format_ok,
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode), static_cast<uint32_t>(resource.resource_class),
resource.atomic, resource.depth_compare, descriptor.BaseLevel(), descriptor.LastLevel(), static_cast<uint32_t>(resource.numeric_class), static_cast<uint32_t>(resource.dimension),
descriptor.MaxMip(), descriptor.MinLod(), descriptor.BaseArray5(), descriptor.BCSwizzle(), static_cast<uint32_t>(resource.mip_mode), resource.atomic, resource.depth_compare,
descriptor.MsaaDepth(), Prospero::RenderTargetBytesPerElement(format), descriptor.BaseLevel(), descriptor.LastLevel(), descriptor.MaxMip(), descriptor.MinLod(),
descriptor.BaseArray5(), descriptor.BCSwizzle(), descriptor.MsaaDepth(),
Prospero::RenderTargetBytesPerElement(format),
IsValidImageSwizzle(descriptor.DstSelXYZW()), descriptor.Base40(), size, IsValidImageSwizzle(descriptor.DstSelXYZW()), descriptor.Base40(), size,
static_cast<uint32_t>(descriptor.Width5()) + 1u, static_cast<uint32_t>(descriptor.Width5()) + 1u,
static_cast<uint32_t>(descriptor.Height5()) + 1u, static_cast<uint32_t>(descriptor.Height5()) + 1u,
@@ -557,13 +559,16 @@ struct NullImageSpec {
}; };
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) { static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
if (resource.kind == ShaderRecompiler::IR::ResourceKind::ImageSint) { switch (resource.numeric_class) {
return {vk::Format::eR32Sint, Prospero::BufferFormat::k32SInt}; case Prospero::TextureNumericClass::Float:
return {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
case Prospero::TextureNumericClass::Uint:
return {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt};
case Prospero::TextureNumericClass::Sint:
return {vk::Format::eR32Sint, Prospero::BufferFormat::k32SInt};
case Prospero::TextureNumericClass::Unsupported: break;
} }
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint || EXIT("null image has unsupported numeric class\n");
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt}
: NullImageSpec {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
} }
static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::ImageResource& resource, static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::ImageResource& resource,
@@ -731,15 +736,16 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
!msaa_tile || (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 " "class=%u numeric=%u dimension=%u mip_mode=%u read=%d written=%d "
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", "dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
base_level, last_level, levels, descriptor.MaxMip(), base_level, last_level, levels, descriptor.MaxMip(),
static_cast<uint32_t>(descriptor.Type()), static_cast<uint32_t>(tile), static_cast<uint32_t>(descriptor.Type()), static_cast<uint32_t>(tile),
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.resource_class),
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, static_cast<uint32_t>(resource.numeric_class),
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], resource.read, resource.written, descriptor.fields[0], descriptor.fields[1],
descriptor.fields[7]); descriptor.fields[2], descriptor.fields[3], descriptor.fields[4], descriptor.fields[5],
descriptor.fields[6], descriptor.fields[7]);
} }
const auto samples = multisampled ? 1u << last_level : 1u; const auto samples = multisampled ? 1u << last_level : 1u;
const auto view_levels = const auto view_levels =
@@ -749,28 +755,12 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
const auto surface_format = TextureGetSurfaceFormatInfo(format); const auto surface_format = TextureGetSurfaceFormatInfo(format);
const bool shader_conversion = const bool shader_conversion =
surface_format.conversion_format != Prospero::BufferFormat::kInvalid; surface_format.conversion_format != Prospero::BufferFormat::kInvalid;
Prospero::TextureNumericClass resource_numeric_class =
Prospero::TextureNumericClass::Unsupported;
switch (resource.kind) {
case ShaderRecompiler::IR::ResourceKind::Image:
resource_numeric_class = Prospero::TextureNumericClass::Float;
break;
case ShaderRecompiler::IR::ResourceKind::ImageUint:
resource_numeric_class = Prospero::TextureNumericClass::Uint;
break;
case ShaderRecompiler::IR::ResourceKind::ImageSint:
resource_numeric_class = Prospero::TextureNumericClass::Sint;
break;
default: break;
}
const bool sampled_numeric_class = const bool sampled_numeric_class =
storage || resource_numeric_class == Prospero::SampledTextureNumericClass(format); storage || resource.numeric_class == Prospero::SampledTextureNumericClass(format);
if (!storage && if (!storage && resource.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled &&
ShaderRecompiler::IR::ImageResourceKindMatches(
resource.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled) &&
!sampled_numeric_class) { !sampled_numeric_class) {
EXIT("sampled image numeric class mismatch: kind=%u format=%u addr=0x%016" PRIx64 "\n", EXIT("sampled image numeric class mismatch: numeric=%u format=%u addr=0x%016" PRIx64 "\n",
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(format), address); static_cast<uint32_t>(resource.numeric_class), static_cast<uint32_t>(format), address);
} }
const bool volume = type == Prospero::ImageType::kColor3D; const bool volume = type == Prospero::ImageType::kColor3D;
@@ -236,8 +236,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
} }
const auto sampled_images = std::count_if( const auto sampled_images = std::count_if(
program.info.images.begin(), program.info.images.end(), [](const auto& image) { program.info.images.begin(), program.info.images.end(), [](const auto& image) {
return ShaderRecompiler::IR::ImageResourceKindMatches( return image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled;
image.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled);
}); });
const bool has_sampler = !program.info.samplers.empty(); const bool has_sampler = !program.info.samplers.empty();
static std::atomic<uint32_t> dispatch_log_count {0}; static std::atomic<uint32_t> dispatch_log_count {0};
@@ -265,8 +264,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
LOGF(" CS texture[%u]: source=%u usage=%s sampled=%s addr=0x%010" PRIx64 LOGF(" CS texture[%u]: source=%u usage=%s sampled=%s addr=0x%010" PRIx64
" type=%u fmt=%u extent=%ux%u depth=%u levels=%u tile=%u\n", " type=%u fmt=%u extent=%ux%u depth=%u levels=%u tile=%u\n",
i, image.source, image.written ? "read-write" : "read-only", i, image.source, image.written ? "read-write" : "read-only",
ShaderRecompiler::IR::ImageResourceKindMatches( image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled
image.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled)
? "true" ? "true"
: "false", : "false",
r.Base40(), static_cast<uint32_t>(r.Type()), static_cast<uint32_t>(r.Format()), r.Base40(), static_cast<uint32_t>(r.Type()), static_cast<uint32_t>(r.Format()),
@@ -347,8 +345,8 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
std::any_of(program.info.images.begin(), program.info.images.end(), std::any_of(program.info.images.begin(), program.info.images.end(),
[](const auto& image) { [](const auto& image) {
return image.written && return image.written &&
(image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage || image.resource_class ==
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint); ShaderRecompiler::IR::ImageResourceClass::Storage;
}) || }) ||
has_storage_writes; has_storage_writes;
if (has_storage_writes) { if (has_storage_writes) {
@@ -199,8 +199,7 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
const auto& ps_resources = ps_input_info.stage.program->info; const auto& ps_resources = ps_input_info.stage.program->info;
const auto sampled_images = std::count_if( const auto sampled_images = std::count_if(
ps_resources.images.begin(), ps_resources.images.end(), [](const auto& image) { ps_resources.images.begin(), ps_resources.images.end(), [](const auto& image) {
return ShaderRecompiler::IR::ImageResourceKindMatches( return image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled;
image.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled);
}); });
vk::Extent2D extent = color.image_id ? color.extent : vk::Extent2D {}; vk::Extent2D extent = color.image_id ? color.extent : vk::Extent2D {};
@@ -185,29 +185,36 @@ uint32_t StorageImagePointerType(EmitterState& state, StorageImageClass image_cl
} }
StorageImageClass StorageImageClassForResource(const EmitterState& state, uint32_t resource) { StorageImageClass StorageImageClassForResource(const EmitterState& state, uint32_t resource) {
const auto& image = state.program.info.images.at(resource); const auto& image = state.program.info.images.at(resource);
const bool uint_image = image.kind == IR::ResourceKind::StorageImageUint; EXIT_IF(image.resource_class != IR::ImageResourceClass::Storage);
EXIT_IF(image.kind != IR::ResourceKind::StorageImage && !uint_image); if (image.atomic) {
EXIT_IF(image.atomic && !uint_image); EXIT_IF(image.numeric_class != Prospero::TextureNumericClass::Uint);
return StorageImageClassFor(uint_image, image.atomic); return StorageImageClass::AtomicUint;
}
switch (image.numeric_class) {
case Prospero::TextureNumericClass::Float: return StorageImageClass::FormatlessFloat;
case Prospero::TextureNumericClass::Uint: return StorageImageClass::FormatlessUint;
case Prospero::TextureNumericClass::Sint:
case Prospero::TextureNumericClass::Unsupported:
EXIT("invalid storage image numeric class");
}
EXIT("invalid storage image numeric class");
} }
uint32_t StorageImageVariable(const EmitterState& state, StorageImageClass image_class, uint32_t LoadSampledImageDescriptor(EmitterState& state, uint32_t resource) {
ImageDimension dimension) { const auto& image_resource = state.program.info.images.at(resource);
return state.storage_image_variables[StorageImageIndex(image_class, dimension)]; EXIT_IF(image_resource.resource_class != IR::ImageResourceClass::Sampled);
} const auto dimension = image_resource.dimension;
const auto image_class = SampledImageClassFor(image_resource.numeric_class);
uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& mem, EXIT_IF(image_class == SampledImageClass::Count);
ImageDimension dimension) {
const auto image_class = SampledImageClassFor(mem.kind);
const auto kind = SampledBindingKind(image_class, dimension); const auto kind = SampledBindingKind(image_class, dimension);
const auto array_index = ResourceForDescriptor(state, kind, mem.resource); const auto array_index = ResourceForDescriptor(state, kind, resource);
const auto variable = state.sampled_image_variables[SampledImageIndex(image_class, dimension)]; const auto variable = state.sampled_image_variables[SampledImageIndex(image_class, dimension)];
const auto pointer_type = const auto pointer_type =
state.builder.Type(OpTypePointer, {StorageClassUniformConstant, state.builder.Type(OpTypePointer, {StorageClassUniformConstant,
ImageViewImageType(state, dimension, image_class)}); ImageViewImageType(state, dimension, image_class)});
const auto pointer = const auto pointer =
DescriptorElementPointer(state, pointer_type, variable, array_index, kind, mem.resource, DescriptorElementPointer(state, pointer_type, variable, array_index, kind, resource,
"sampled image descriptor array was not emitted"); "sampled image descriptor array was not emitted");
const auto image = state.builder.AllocateId(); const auto image = state.builder.AllocateId();
state.builder.AddFunction( state.builder.AddFunction(
@@ -229,43 +236,37 @@ uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler) {
return sampler_id; return sampler_id;
} }
uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampler) {
ImageDimension dimension) { const auto& image_resource = state.program.info.images.at(resource);
const auto image = LoadSampledImageDescriptor(state, mem, dimension); const auto dimension = image_resource.dimension;
const auto sampler = LoadSamplerDescriptor(state, mem.sampler); const auto image_class = SampledImageClassFor(image_resource.numeric_class);
if (image == 0 || sampler == 0) { const auto image = LoadSampledImageDescriptor(state, resource);
ExitDescriptorBindingFailure( const auto sampler_id = LoadSamplerDescriptor(state, sampler);
state, SampledBindingKind(SampledImageClassFor(mem.kind), dimension), mem.resource, if (image == 0 || sampler_id == 0) {
"sampled image or sampler descriptor load failed"); ExitDescriptorBindingFailure(state, SampledBindingKind(image_class, dimension), resource,
"sampled image or sampler descriptor load failed");
} }
const auto sampled_image = state.builder.AllocateId(); const auto sampled_image = state.builder.AllocateId();
state.builder.AddFunction( state.builder.AddFunction({OpSampledImage,
{OpSampledImage, ImageViewSampledImageType(state, dimension, image_class),
ImageViewSampledImageType(state, dimension, SampledImageClassFor(mem.kind)), sampled_image, sampled_image, image, sampler_id});
image, sampler});
return sampled_image; return sampled_image;
} }
uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, ImageDimension dimension, uint32_t StorageImageDescriptorPointer(EmitterState& state, uint32_t resource) {
uint32_t image_resource) { const auto dimension = state.program.info.images.at(resource).dimension;
auto selected = mem;
selected.resource = image_resource;
return MakeSampledImage(state, selected, dimension);
}
uint32_t StorageImageDescriptorPointer(EmitterState& state, uint32_t resource,
ImageDimension dimension) {
const auto image_class = StorageImageClassForResource(state, resource); const auto image_class = StorageImageClassForResource(state, resource);
const auto kind = StorageBindingKind(image_class, dimension); const auto kind = StorageBindingKind(image_class, dimension);
const auto array_index = ResourceForDescriptor(state, kind, resource); const auto array_index = ResourceForDescriptor(state, kind, resource);
const auto ptr_type = StorageImagePointerType(state, image_class, dimension); const auto ptr_type = StorageImagePointerType(state, image_class, dimension);
const auto variable = StorageImageVariable(state, image_class, dimension); const auto variable = state.storage_image_variables[StorageImageIndex(image_class, dimension)];
return DescriptorElementPointer(state, ptr_type, variable, array_index, kind, resource, return DescriptorElementPointer(state, ptr_type, variable, array_index, kind, resource,
"storage image descriptor array was not emitted"); "storage image descriptor array was not emitted");
} }
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, ImageDimension dimension, void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
uint32_t mip_lod, uint32_t coord, uint32_t texel) { uint32_t texel) {
const auto dimension = state.program.info.images.at(resource).dimension;
const auto image_class = StorageImageClassForResource(state, resource); const auto image_class = StorageImageClassForResource(state, resource);
if (image_class != StorageImageClass::AtomicUint) { if (image_class != StorageImageClass::AtomicUint) {
state.builder.RequireCapability(CapabilityStorageImageWriteWithoutFormat); state.builder.RequireCapability(CapabilityStorageImageWriteWithoutFormat);
@@ -276,8 +277,8 @@ void EmitStorageImageWrite(EmitterState& state, uint32_t resource, ImageDimensio
const auto LoadAt = [&](uint32_t array_index) { const auto LoadAt = [&](uint32_t array_index) {
const auto pointer = DescriptorElementPointer( const auto pointer = DescriptorElementPointer(
state, StorageImagePointerType(state, image_class, dimension), state, StorageImagePointerType(state, image_class, dimension),
StorageImageVariable(state, image_class, dimension), array_index, kind, resource, state.storage_image_variables[StorageImageIndex(image_class, dimension)], array_index,
"storage image descriptor array was not emitted"); kind, resource, "storage image descriptor array was not emitted");
const auto descriptor = state.builder.AllocateId(); const auto descriptor = state.builder.AllocateId();
state.builder.AddFunction( state.builder.AddFunction(
{OpLoad, StorageImageType(state, image_class, dimension), descriptor, pointer}); {OpLoad, StorageImageType(state, image_class, dimension), descriptor, pointer});
@@ -145,11 +145,12 @@ uint32_t CubeLayer(EmitterState& state, uint32_t value) {
uint32_t CoordF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst& address, uint32_t CoordF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst& address,
uint32_t first, uint32_t components) { uint32_t first, uint32_t components) {
const bool cube = ctx.state.program.info.images.at(mem.resource).cube;
auto x = AddressF32(ctx, mem, address, first); auto x = AddressF32(ctx, mem, address, first);
if (components == 1u) return x; if (components == 1u) return x;
auto y = mem.image_address_components > first + 1u ? AddressF32(ctx, mem, address, first + 1u) auto y = mem.image_address_components > first + 1u ? AddressF32(ctx, mem, address, first + 1u)
: ZeroF32(ctx.state); : ZeroF32(ctx.state);
if (mem.image_cube) { if (cube) {
x = CubeAxis(ctx.state, x); x = CubeAxis(ctx.state, x);
y = CubeAxis(ctx.state, y); y = CubeAxis(ctx.state, y);
} }
@@ -158,7 +159,7 @@ uint32_t CoordF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::In
auto z = mem.image_address_components > first + 2u auto z = mem.image_address_components > first + 2u
? AddressF32(ctx, mem, address, first + 2u) ? AddressF32(ctx, mem, address, first + 2u)
: ZeroF32(ctx.state); : ZeroF32(ctx.state);
if (mem.image_cube) z = CubeLayer(ctx.state, z); if (cube) z = CubeLayer(ctx.state, z);
ctx.state.builder.AddFunction( ctx.state.builder.AddFunction(
{OpCompositeConstruct, TypeF32Vector(ctx.state, 3), result, x, y, z}); {OpCompositeConstruct, TypeF32Vector(ctx.state, 3), result, x, y, z});
} else { } else {
@@ -284,9 +285,9 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, SampledImageClass i
uint32_t QueryDimensions(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t QueryDimensions(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
const IR::Inst& address) { const IR::Inst& address) {
const auto dimension = mem.image_dimension; const auto dimension = ctx.state.program.info.images.at(mem.resource).dimension;
const auto& info = ImageDimensionInfoFor(dimension); const auto& info = ImageDimensionInfoFor(dimension);
const auto image = LoadSampledImageDescriptor(ctx.state, mem, dimension); const auto image = LoadSampledImageDescriptor(ctx.state, mem.resource);
const auto size = ctx.state.builder.AllocateId(); const auto size = ctx.state.builder.AllocateId();
if (info.multisampled != 0u) { if (info.multisampled != 0u) {
ctx.state.builder.AddFunction( ctx.state.builder.AddFunction(
@@ -464,7 +465,7 @@ uint32_t EmitOneDimensionalGatherLz(ValueEmitContext& ctx, const IR::MemoryInfo&
uint32_t coord, SampledImageClass image_class) { uint32_t coord, SampledImageClass image_class) {
auto& state = ctx.state; auto& state = ctx.state;
state.builder.RequireCapability(CapabilityImageQuery); state.builder.RequireCapability(CapabilityImageQuery);
const auto image = LoadSampledImageDescriptor(state, mem, ImageDimension::Dim1D); const auto image = LoadSampledImageDescriptor(state, mem.resource);
const auto width = state.builder.AllocateId(); const auto width = state.builder.AllocateId();
state.builder.AddFunction( state.builder.AddFunction(
{OpImageQuerySizeLod, TypeU32(state), width, image, ConstantU32(state, 0)}); {OpImageQuerySizeLod, TypeU32(state), width, image, ConstantU32(state, 0)});
@@ -476,7 +477,7 @@ uint32_t EmitOneDimensionalGatherLz(ValueEmitContext& ctx, const IR::MemoryInfo&
Binary(state, OpFMul, TypeF32(state), coord, width_f32), Binary(state, OpFMul, TypeF32(state), coord, width_f32),
ConstantF32(state, 0x3f000000u))}); ConstantF32(state, 0x3f000000u))});
const auto sampled = MakeSampledImage(state, mem, ImageDimension::Dim1D); const auto sampled = MakeSampledImage(state, mem.resource, mem.sampler);
const auto vector_type = SampledImageVectorType(state, image_class, 4); const auto vector_type = SampledImageVectorType(state, image_class, 4);
const auto scalar_type = SampledImageScalarType(state, image_class); const auto scalar_type = SampledImageScalarType(state, image_class);
const auto component = const auto component =
@@ -592,6 +593,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto& mem = ctx.Memory(inst); const auto& mem = ctx.Memory(inst);
const auto image_arg = inst.Arg(0); const auto image_arg = inst.Arg(0);
ctx.ResourceIndex(image_arg, IR::ValueOpcode::GetImageResource); ctx.ResourceIndex(image_arg, IR::ValueOpcode::GetImageResource);
const auto& image = state.program.info.images.at(mem.resource);
const auto* address = ctx.ImageAddress(inst.Arg(image_info.needs_sampler ? 2 : 1)); const auto* address = ctx.ImageAddress(inst.Arg(image_info.needs_sampler ? 2 : 1));
if (address == nullptr) return true; if (address == nullptr) return true;
if (op == IR::ValueOpcode::ImageQueryDimensions) { if (op == IR::ValueOpcode::ImageQueryDimensions) {
@@ -601,8 +603,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
} }
if (op == IR::ValueOpcode::ImageQueryLod) { if (op == IR::ValueOpcode::ImageQueryLod) {
state.builder.RequireCapability(CapabilityImageQuery); state.builder.RequireCapability(CapabilityImageQuery);
const auto dimension = mem.image_dimension; const auto dimension = image.dimension;
const auto sampled = MakeSampledImage(state, mem, dimension); const auto sampled = MakeSampledImage(state, mem.resource, mem.sampler);
const auto lod = state.builder.AllocateId(); const auto lod = state.builder.AllocateId();
state.builder.AddFunction( state.builder.AddFunction(
{OpImageQueryLod, TypeF32Vector(state, 2), lod, sampled, {OpImageQueryLod, TypeF32Vector(state, 2), lod, sampled,
@@ -621,27 +623,27 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
return true; return true;
} }
if (op == IR::ValueOpcode::ImageRead) { if (op == IR::ValueOpcode::ImageRead) {
const auto dimension = mem.image_dimension; const auto dimension = image.dimension;
const auto& dimension_info = ImageDimensionInfoFor(dimension); const auto& dimension_info = ImageDimensionInfoFor(dimension);
const auto image_class = SampledImageClassFor(mem.kind); const auto image_class = SampledImageClassFor(image.numeric_class);
const auto condition = ctx.Arg(inst, 2); const auto condition = ctx.Arg(inst, 2);
ctx.Define( ctx.Define(
inst, inst,
EmitValueOrDefaultIfCondition( EmitValueOrDefaultIfCondition(
state, condition, TypeU32Vector(state, 4), ConstantU32CompositeZero(state, 4), state, condition, TypeU32Vector(state, 4), ConstantU32CompositeZero(state, 4),
[&]() { [&]() {
const auto image = LoadSampledImageDescriptor(state, mem, dimension); const auto descriptor = LoadSampledImageDescriptor(state, mem.resource);
const auto color = state.builder.AllocateId(); const auto color = state.builder.AllocateId();
const auto coord = CoordU32(ctx, mem, *address, dimension); const auto coord = CoordU32(ctx, mem, *address, dimension);
if (dimension_info.multisampled != 0u) { if (dimension_info.multisampled != 0u) {
state.builder.AddFunction( state.builder.AddFunction(
{OpImageFetch, SampledImageVectorType(state, image_class, 4), color, {OpImageFetch, SampledImageVectorType(state, image_class, 4), color,
image, coord, ImageOperandsSampleMask, descriptor, coord, ImageOperandsSampleMask,
AddressU32(ctx, mem, *address, dimension_info.coordinate_components)}); AddressU32(ctx, mem, *address, dimension_info.coordinate_components)});
} else { } else {
state.builder.AddFunction({OpImageFetch, state.builder.AddFunction({OpImageFetch,
SampledImageVectorType(state, image_class, 4), SampledImageVectorType(state, image_class, 4),
color, image, coord, ImageOperandsLodMask, color, descriptor, coord, ImageOperandsLodMask,
LodU32(ctx, mem, *address, dimension)}); LodU32(ctx, mem, *address, dimension)});
} }
return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), image_class, false, return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), image_class, false,
@@ -650,8 +652,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
return true; return true;
} }
if (op == IR::ValueOpcode::ImageWrite) { if (op == IR::ValueOpcode::ImageWrite) {
const auto uint_image = mem.kind == IR::ResourceKind::StorageImageUint; const bool uint_image = image.numeric_class == Prospero::TextureNumericClass::Uint;
const auto dimension = mem.image_dimension; const auto dimension = image.dimension;
EmitIfCondition(state, ctx.Arg(inst, 3), [&]() { EmitIfCondition(state, ctx.Arg(inst, 3), [&]() {
const auto mip_lod = const auto mip_lod =
state.program.info.images[mem.resource].mip_mode == IR::ImageMipMode::DynamicStorage state.program.info.images[mem.resource].mip_mode == IR::ImageMipMode::DynamicStorage
@@ -659,15 +661,15 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
: 0u; : 0u;
const auto coord = CoordU32(ctx, mem, *address, dimension); const auto coord = CoordU32(ctx, mem, *address, dimension);
const auto texel = StoreTexel(ctx, mem, ctx.Arg(inst, 2), uint_image); const auto texel = StoreTexel(ctx, mem, ctx.Arg(inst, 2), uint_image);
EmitStorageImageWrite(state, mem.resource, dimension, mip_lod, coord, texel); EmitStorageImageWrite(state, mem.resource, mip_lod, coord, texel);
}); });
return true; return true;
} }
if (op == IR::ValueOpcode::ImageSampleRaw || op == IR::ValueOpcode::ImageGatherRaw) { if (op == IR::ValueOpcode::ImageSampleRaw || op == IR::ValueOpcode::ImageGatherRaw) {
const auto dimension = mem.image_dimension; const auto dimension = image.dimension;
const auto& dimension_info = ImageDimensionInfoFor(dimension); const auto& dimension_info = ImageDimensionInfoFor(dimension);
const auto layout = Layout(mem, dimension); const auto layout = Layout(mem, dimension);
const auto image_class = SampledImageClassFor(mem.kind); const auto image_class = SampledImageClassFor(image.numeric_class);
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare); const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
if (dref && state.program.info.images[mem.resource].conversion_format != if (dref && state.program.info.images[mem.resource].conversion_format !=
Prospero::BufferFormat::kInvalid) { Prospero::BufferFormat::kInvalid) {
@@ -693,7 +695,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
ctx.Fail(inst, "has an unsupported 1D-array gather"); ctx.Fail(inst, "has an unsupported 1D-array gather");
return true; return true;
} }
const auto sampled = MakeSampledImage(state, mem, dimension); const auto sampled = MakeSampledImage(state, mem.resource, mem.sampler);
const auto sample = state.builder.AllocateId(); const auto sample = state.builder.AllocateId();
std::vector<uint32_t> words = std::vector<uint32_t> words =
dref ? std::vector<uint32_t> {OpImageDrefGather, dref ? std::vector<uint32_t> {OpImageDrefGather,
@@ -759,7 +761,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
operands.push_back(AddressF32(ctx, mem, *address, layout.bias)); operands.push_back(AddressF32(ctx, mem, *address, layout.bias));
} }
const auto EmitSample = [&](uint32_t resource) { const auto EmitSample = [&](uint32_t resource) {
const auto sampled = MakeSampledImage(state, mem, dimension, resource); const auto sampled = MakeSampledImage(state, resource, mem.sampler);
const auto sample = state.builder.AllocateId(); const auto sample = state.builder.AllocateId();
std::vector<uint32_t> words {opcode, result_type, sample, sampled, coord}; std::vector<uint32_t> words {opcode, result_type, sample, sampled, coord};
if (dref) { if (dref) {
@@ -772,7 +774,6 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
state.builder.AddFunction(words); state.builder.AddFunction(words);
return sample; return sample;
}; };
const auto& image = state.program.info.images[mem.resource];
if (image.indirect_root != mem.resource) { if (image.indirect_root != mem.resource) {
const auto sample = EmitSample(mem.resource); const auto sample = EmitSample(mem.resource);
ctx.Define(inst, ResultVector(ctx, dref ? sample : UnpackImageTexel(ctx, mem, sample), ctx.Define(inst, ResultVector(ctx, dref ? sample : UnpackImageTexel(ctx, mem, sample),
@@ -874,14 +875,14 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
} }
const auto atomic_opcode = ImageAtomicOpcode(op); const auto atomic_opcode = ImageAtomicOpcode(op);
if (atomic_opcode != 0u) { if (atomic_opcode != 0u) {
const auto dimension = mem.image_dimension; const auto dimension = image.dimension;
ctx.Define(inst, EmitValueOrZeroIfCondition(state, ctx.Arg(inst, 3), [&]() { ctx.Define(inst, EmitValueOrZeroIfCondition(state, ctx.Arg(inst, 3), [&]() {
const auto pointer = state.builder.AllocateId(); const auto pointer = state.builder.AllocateId();
const auto pointer_type = const auto pointer_type =
state.builder.Type(OpTypePointer, {StorageClassImage, TypeU32(state)}); state.builder.Type(OpTypePointer, {StorageClassImage, TypeU32(state)});
state.builder.AddFunction( state.builder.AddFunction(
{OpImageTexelPointer, pointer_type, pointer, {OpImageTexelPointer, pointer_type, pointer,
StorageImageDescriptorPointer(state, mem.resource, dimension), StorageImageDescriptorPointer(state, mem.resource),
CoordU32(ctx, mem, *address, dimension), ConstantU32(state, 0)}); CoordU32(ctx, mem, *address, dimension), ConstantU32(state, 0)});
const auto old = state.builder.AllocateId(); const auto old = state.builder.AllocateId();
state.builder.AddFunction({atomic_opcode, TypeU32(state), old, pointer, state.builder.AddFunction({atomic_opcode, TypeU32(state), old, pointer,
@@ -482,13 +482,14 @@ struct ImageSampleLayout {
uint32_t grad_y = NoImageComponent; uint32_t grad_y = NoImageComponent;
}; };
constexpr SampledImageClass SampledImageClassFor(IR::ResourceKind kind) { constexpr SampledImageClass SampledImageClassFor(Prospero::TextureNumericClass numeric_class) {
switch (kind) { switch (numeric_class) {
case IR::ResourceKind::Image: return SampledImageClass::Float; case Prospero::TextureNumericClass::Float: return SampledImageClass::Float;
case IR::ResourceKind::ImageUint: return SampledImageClass::Uint; case Prospero::TextureNumericClass::Uint: return SampledImageClass::Uint;
case IR::ResourceKind::ImageSint: return SampledImageClass::Sint; case Prospero::TextureNumericClass::Sint: return SampledImageClass::Sint;
default: return SampledImageClass::Count; case Prospero::TextureNumericClass::Unsupported: return SampledImageClass::Count;
} }
return SampledImageClass::Count;
} }
constexpr bool IsIntegerSampledImage(SampledImageClass image_class) { constexpr bool IsIntegerSampledImage(SampledImageClass image_class) {
@@ -500,16 +501,6 @@ inline uint32_t SampledImageIndex(SampledImageClass image_class, ImageDimension
static_cast<uint32_t>(image_class) * SampledImageDimensionCount; static_cast<uint32_t>(image_class) * SampledImageDimensionCount;
} }
constexpr StorageImageClass StorageImageClassFor(bool uint_image, bool atomic) {
if (atomic) {
return StorageImageClass::AtomicUint;
}
if (uint_image) {
return StorageImageClass::FormatlessUint;
}
return StorageImageClass::FormatlessFloat;
}
inline uint32_t StorageImageIndex(StorageImageClass image_class, ImageDimension dimension) { inline uint32_t StorageImageIndex(StorageImageClass image_class, ImageDimension dimension) {
return ImageDimensionInfoFor(dimension).ordinal + return ImageDimensionInfoFor(dimension).ordinal +
static_cast<uint32_t>(image_class) * StorageImageDimensionCount; static_cast<uint32_t>(image_class) * StorageImageDimensionCount;
@@ -667,20 +658,16 @@ uint32_t StorageImageType(EmitterState& state, StorageImageClass image_class,
uint32_t StorageImagePointerType(EmitterState& state, StorageImageClass image_class, uint32_t StorageImagePointerType(EmitterState& state, StorageImageClass image_class,
ImageDimension dimension); ImageDimension dimension);
uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& mem, uint32_t LoadSampledImageDescriptor(EmitterState& state, uint32_t resource);
ImageDimension dimension);
uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler); uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler);
uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, ImageDimension dimension); uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampler);
uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, ImageDimension dimension,
uint32_t image_resource);
uint32_t StorageImageDescriptorPointer(EmitterState& state, uint32_t resource, uint32_t StorageImageDescriptorPointer(EmitterState& state, uint32_t resource);
ImageDimension dimension);
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, ImageDimension dimension, void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
uint32_t mip_lod, uint32_t coord, uint32_t texel); uint32_t texel);
uint32_t ExecutionModelForStage(ShaderType stage); uint32_t ExecutionModelForStage(ShaderType stage);
@@ -84,18 +84,7 @@ ResourceKind MemoryKind(const Decoder::Instruction& decoded) {
case Decoder::Family::MTBUF: return ResourceKind::Buffer; case Decoder::Family::MTBUF: return ResourceKind::Buffer;
case Decoder::Family::FLAT: return FlatSegmentResourceKind(decoded.memory_segment); case Decoder::Family::FLAT: return FlatSegmentResourceKind(decoded.memory_segment);
case Decoder::Family::DS: return decoded.gds ? ResourceKind::Gds : ResourceKind::Lds; case Decoder::Family::DS: return decoded.gds ? ResourceKind::Gds : ResourceKind::Lds;
case Decoder::Family::MIMG: case Decoder::Family::MIMG: return ResourceKind::Image;
switch (decoded.opcode) {
case Decoder::Opcode::IMAGE_STORE:
case Decoder::Opcode::IMAGE_STORE_MIP: return ResourceKind::StorageImage;
case Decoder::Opcode::IMAGE_ATOMIC_ADD:
case Decoder::Opcode::IMAGE_ATOMIC_UMIN:
case Decoder::Opcode::IMAGE_ATOMIC_UMAX:
case Decoder::Opcode::IMAGE_ATOMIC_AND:
case Decoder::Opcode::IMAGE_ATOMIC_OR:
case Decoder::Opcode::IMAGE_ATOMIC_XOR: return ResourceKind::StorageImageUint;
default: return ResourceKind::Image;
}
default: return ResourceKind::None; default: return ResourceKind::None;
} }
} }
@@ -153,10 +142,6 @@ IR::MemoryInfo MemoryInfoFromDecoded(const Decoder::Instruction& decoded) {
decoded.opcode == Decoder::Opcode::DS_WRITE2ST64_B32) { decoded.opcode == Decoder::Opcode::DS_WRITE2ST64_B32) {
memory.data_dwords = 2u; memory.data_dwords = 2u;
} }
if (memory.kind == ResourceKind::StorageImage ||
memory.kind == ResourceKind::StorageImageUint) {
memory.sampler = 0;
}
return memory; return memory;
} }
@@ -240,7 +225,10 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in
decoded.opcode == Decoder::Opcode::IMAGE_STORE_MIP || decoded.opcode == Decoder::Opcode::IMAGE_STORE_MIP ||
(decoded.opcode >= Decoder::Opcode::IMAGE_ATOMIC_ADD && (decoded.opcode >= Decoder::Opcode::IMAGE_ATOMIC_ADD &&
decoded.opcode <= Decoder::Opcode::IMAGE_ATOMIC_XOR); decoded.opcode <= Decoder::Opcode::IMAGE_ATOMIC_XOR);
return store_or_atomic ? (index == 0u ? decoded.dst : decoded.src0) : decoded.src0; if (store_or_atomic) {
return index == 0u ? decoded.dst : decoded.src0;
}
return decoded.src0;
} }
if (decoded.family == Decoder::Family::DS) { if (decoded.family == Decoder::Family::DS) {
switch (decoded.opcode) { switch (decoded.opcode) {
@@ -450,7 +450,8 @@ void ValidateProgram(const Program& program, bool require_ssa) {
ValueOpcodeName(inst.GetOpcode()))); ValueOpcodeName(inst.GetOpcode())));
} }
const auto& memory = program.memory_info[memory_index]; const auto& memory = program.memory_info[memory_index];
if (!ImageResourceKindMatches(memory.kind, image_info.resource_class)) { if (memory.kind != ResourceKind::Image ||
image_info.resource_class == ImageResourceClass::None) {
return Fail(fmt::format("{} has invalid image-memory metadata", return Fail(fmt::format("{} has invalid image-memory metadata",
ValueOpcodeName(inst.GetOpcode()))); ValueOpcodeName(inst.GetOpcode())));
} }
+83 -100
View File
@@ -4,6 +4,7 @@
#include "common/common.h" #include "common/common.h"
#include "common/stringUtils.h" #include "common/stringUtils.h"
#include "graphics/guest_gpu/gpu_defs.h" #include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h" #include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/Block.h" #include "graphics/shader/recompiler/ir/Block.h"
@@ -30,10 +31,6 @@ enum class ResourceKind {
Lds, Lds,
Gds, Gds,
Image, Image,
ImageUint,
ImageSint,
StorageImage,
StorageImageUint,
Sampler Sampler
}; };
@@ -42,20 +39,6 @@ enum class ResourceKind {
kind == ResourceKind::Global || kind == ResourceKind::Scratch; kind == ResourceKind::Global || kind == ResourceKind::Scratch;
} }
[[nodiscard]] constexpr bool ImageResourceKindMatches(ResourceKind kind,
ImageResourceClass resource_class) {
switch (resource_class) {
case ImageResourceClass::Sampled:
return kind == ResourceKind::Image || kind == ResourceKind::ImageUint ||
kind == ResourceKind::ImageSint;
case ImageResourceClass::Storage:
return kind == ResourceKind::StorageImage || kind == ResourceKind::StorageImageUint;
case ImageResourceClass::StorageUint: return kind == ResourceKind::StorageImageUint;
case ImageResourceClass::None: return false;
}
return false;
}
struct MemoryInfo { struct MemoryInfo {
ResourceKind kind = ResourceKind::None; ResourceKind kind = ResourceKind::None;
uint32_t resource = 0; uint32_t resource = 0;
@@ -80,7 +63,6 @@ struct MemoryInfo {
bool typed = false; bool typed = false;
bool formatted = false; bool formatted = false;
bool image_has_mip = false; bool image_has_mip = false;
bool image_cube = false;
bool image_r128 = false; bool image_r128 = false;
bool glc = false; bool glc = false;
bool slc = false; bool slc = false;
@@ -140,24 +122,25 @@ constexpr uint32_t ShaderImageIdentitySwizzle = 0x00000facu;
struct ImageResource { struct ImageResource {
static constexpr uint32_t NoIndirectImage = UINT32_MAX; static constexpr uint32_t NoIndirectImage = UINT32_MAX;
uint32_t source = 0; uint32_t source = 0;
uint32_t first_use_pc = 0; uint32_t first_use_pc = 0;
ResourceKind kind = ResourceKind::None; ImageResourceClass resource_class = ImageResourceClass::None;
Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown; Prospero::TextureNumericClass numeric_class = Prospero::TextureNumericClass::Unsupported;
ImageMipMode mip_mode = ImageMipMode::None; Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown;
uint32_t mip_count = 1; ImageMipMode mip_mode = ImageMipMode::None;
Prospero::BufferFormat conversion_format = Prospero::BufferFormat::kInvalid; uint32_t mip_count = 1;
uint32_t shader_swizzle = ShaderImageIdentitySwizzle; Prospero::BufferFormat conversion_format = Prospero::BufferFormat::kInvalid;
bool read = false; uint32_t shader_swizzle = ShaderImageIdentitySwizzle;
bool written = false; bool read = false;
bool atomic = false; bool written = false;
bool depth_compare = false; bool atomic = false;
bool cube = false; bool depth_compare = false;
bool r128 = false; bool cube = false;
uint32_t indirect_root = NoIndirectImage; bool r128 = false;
uint32_t indirect_mapping_offset = 0; uint32_t indirect_root = NoIndirectImage;
uint32_t indirect_mapping_capacity = 0; uint32_t indirect_mapping_offset = 0;
std::vector<uint32_t> indirect_resources; uint32_t indirect_mapping_capacity = 0;
std::vector<uint32_t> indirect_resources;
bool operator==(const ImageResource& other) const = default; bool operator==(const ImageResource& other) const = default;
}; };
@@ -335,8 +318,34 @@ DescriptorBindingForImage(const ImageResource& image) {
using Dimension = Decoder::ImageDimension; using Dimension = Decoder::ImageDimension;
using Kind = DescriptorBindingKind; using Kind = DescriptorBindingKind;
switch (image.kind) { switch (image.resource_class) {
case ResourceKind::Image: case ImageResourceClass::Sampled:
switch (image.numeric_class) {
case Prospero::TextureNumericClass::Float: break;
case Prospero::TextureNumericClass::Uint:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::SampledUint1D;
case Dimension::Dim1DArray: return Kind::SampledUint1DArray;
case Dimension::Dim2D: return Kind::SampledUint2D;
case Dimension::Dim2DArray: return Kind::SampledUint2DArray;
case Dimension::Dim2DMsaa: return Kind::SampledUint2DMsaa;
case Dimension::Dim2DMsaaArray: return Kind::SampledUint2DMsaaArray;
case Dimension::Dim3D: return Kind::SampledUint3D;
default: return std::nullopt;
}
case Prospero::TextureNumericClass::Sint:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::SampledSint1D;
case Dimension::Dim1DArray: return Kind::SampledSint1DArray;
case Dimension::Dim2D: return Kind::SampledSint2D;
case Dimension::Dim2DArray: return Kind::SampledSint2DArray;
case Dimension::Dim2DMsaa: return Kind::SampledSint2DMsaa;
case Dimension::Dim2DMsaaArray: return Kind::SampledSint2DMsaaArray;
case Dimension::Dim3D: return Kind::SampledSint3D;
default: return std::nullopt;
}
case Prospero::TextureNumericClass::Unsupported: return std::nullopt;
}
switch (image.dimension) { switch (image.dimension) {
case Dimension::Dim1D: return Kind::Sampled1D; case Dimension::Dim1D: return Kind::Sampled1D;
case Dimension::Dim1DArray: return Kind::Sampled1DArray; case Dimension::Dim1DArray: return Kind::Sampled1DArray;
@@ -347,71 +356,45 @@ DescriptorBindingForImage(const ImageResource& image) {
case Dimension::Dim3D: return Kind::Sampled3D; case Dimension::Dim3D: return Kind::Sampled3D;
default: return std::nullopt; default: return std::nullopt;
} }
case ResourceKind::ImageUint: case ImageResourceClass::Storage:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::SampledUint1D;
case Dimension::Dim1DArray: return Kind::SampledUint1DArray;
case Dimension::Dim2D: return Kind::SampledUint2D;
case Dimension::Dim2DArray: return Kind::SampledUint2DArray;
case Dimension::Dim2DMsaa: return Kind::SampledUint2DMsaa;
case Dimension::Dim2DMsaaArray: return Kind::SampledUint2DMsaaArray;
case Dimension::Dim3D: return Kind::SampledUint3D;
default: return std::nullopt;
}
case ResourceKind::ImageSint:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::SampledSint1D;
case Dimension::Dim1DArray: return Kind::SampledSint1DArray;
case Dimension::Dim2D: return Kind::SampledSint2D;
case Dimension::Dim2DArray: return Kind::SampledSint2DArray;
case Dimension::Dim2DMsaa: return Kind::SampledSint2DMsaa;
case Dimension::Dim2DMsaaArray: return Kind::SampledSint2DMsaaArray;
case Dimension::Dim3D: return Kind::SampledSint3D;
default: return std::nullopt;
}
case ResourceKind::StorageImage:
if (image.atomic) { if (image.atomic) {
return std::nullopt; if (image.numeric_class != Prospero::TextureNumericClass::Uint) {
return std::nullopt;
}
switch (image.dimension) {
case Dimension::Dim1D: return Kind::StorageAtomic1D;
case Dimension::Dim1DArray: return Kind::StorageAtomic1DArray;
case Dimension::Dim2D: return Kind::StorageAtomic2D;
case Dimension::Dim2DArray: return Kind::StorageAtomic2DArray;
case Dimension::Dim3D: return Kind::StorageAtomic3D;
default: return std::nullopt;
}
} }
switch (image.dimension) { switch (image.numeric_class) {
case Dimension::Dim1D: return Kind::Storage1D; case Prospero::TextureNumericClass::Float:
case Dimension::Dim1DArray: return Kind::Storage1DArray; switch (image.dimension) {
case Dimension::Dim2D: return Kind::Storage2D; case Dimension::Dim1D: return Kind::Storage1D;
case Dimension::Dim2DArray: return Kind::Storage2DArray; case Dimension::Dim1DArray: return Kind::Storage1DArray;
case Dimension::Dim3D: return Kind::Storage3D; case Dimension::Dim2D: return Kind::Storage2D;
default: return std::nullopt; case Dimension::Dim2DArray: return Kind::Storage2DArray;
case Dimension::Dim3D: return Kind::Storage3D;
default: return std::nullopt;
}
case Prospero::TextureNumericClass::Uint:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::StorageUint1D;
case Dimension::Dim1DArray: return Kind::StorageUint1DArray;
case Dimension::Dim2D: return Kind::StorageUint2D;
case Dimension::Dim2DArray: return Kind::StorageUint2DArray;
case Dimension::Dim3D: return Kind::StorageUint3D;
default: return std::nullopt;
}
case Prospero::TextureNumericClass::Sint:
case Prospero::TextureNumericClass::Unsupported: return std::nullopt;
} }
case ResourceKind::StorageImageUint: case ImageResourceClass::None: return std::nullopt;
switch (image.dimension) {
case Dimension::Dim1D:
if (image.atomic) {
return Kind::StorageAtomic1D;
}
return Kind::StorageUint1D;
case Dimension::Dim1DArray:
if (image.atomic) {
return Kind::StorageAtomic1DArray;
}
return Kind::StorageUint1DArray;
case Dimension::Dim2D:
if (image.atomic) {
return Kind::StorageAtomic2D;
}
return Kind::StorageUint2D;
case Dimension::Dim2DArray:
if (image.atomic) {
return Kind::StorageAtomic2DArray;
}
return Kind::StorageUint2DArray;
case Dimension::Dim3D:
if (image.atomic) {
return Kind::StorageAtomic3D;
}
return Kind::StorageUint3D;
default: return std::nullopt;
}
default: return std::nullopt;
} }
return std::nullopt;
} }
struct DescriptorBinding { struct DescriptorBinding {
@@ -210,7 +210,7 @@ ImageOpcodeInfo ImageOpcodeInfoOf(ValueOpcode opcode) {
case ValueOpcode::ImageAtomicAnd32: case ValueOpcode::ImageAtomicAnd32:
case ValueOpcode::ImageAtomicOr32: case ValueOpcode::ImageAtomicOr32:
case ValueOpcode::ImageAtomicXor32: case ValueOpcode::ImageAtomicXor32:
return {ImageAccess::Atomic, ImageResourceClass::StorageUint, false}; return {ImageAccess::Atomic, ImageResourceClass::Storage, false};
default: return {}; default: return {};
} }
} }
@@ -20,7 +20,7 @@ enum class BufferAccess { None, Read, Write, Atomic };
enum class SharedAccess { None, Read, Write, Atomic, Append, Consume }; enum class SharedAccess { None, Read, Write, Atomic, Append, Consume };
enum class AddressAccess { None, Read, Write }; enum class AddressAccess { None, Read, Write };
enum class ImageAccess { None, Read, Write, Atomic }; enum class ImageAccess { None, Read, Write, Atomic };
enum class ImageResourceClass { None, Sampled, Storage, StorageUint }; enum class ImageResourceClass { None, Sampled, Storage };
struct AddressOpcodeInfo { struct AddressOpcodeInfo {
AddressAccess access = AddressAccess::None; AddressAccess access = AddressAccess::None;
@@ -89,17 +89,8 @@ Prospero::BufferFormat ImageConversionFormat(Prospero::BufferFormat format) {
: Prospero::BufferFormat::kInvalid; : Prospero::BufferFormat::kInvalid;
} }
Prospero::TextureNumericClass SampledResourceNumericClass(ResourceKind kind) {
switch (kind) {
case ResourceKind::Image: return Prospero::TextureNumericClass::Float;
case ResourceKind::ImageUint: return Prospero::TextureNumericClass::Uint;
case ResourceKind::ImageSint: return Prospero::TextureNumericClass::Sint;
default: return Prospero::TextureNumericClass::Unsupported;
}
}
bool RequiresPointSampler(const ImageResource& image) { bool RequiresPointSampler(const ImageResource& image) {
return image.kind == ResourceKind::ImageSint || return image.numeric_class == Prospero::TextureNumericClass::Sint ||
image.conversion_format != Prospero::BufferFormat::kInvalid; image.conversion_format != Prospero::BufferFormat::kInvalid;
} }
@@ -365,7 +356,8 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
if (image.indirect_root != ImageResource::NoIndirectImage) { if (image.indirect_root != ImageResource::NoIndirectImage) {
const auto root = image.indirect_root; const auto root = image.indirect_root;
if (root >= program.info.images.size() || if (root >= program.info.images.size() ||
program.info.images[root].kind != image.kind || program.info.images[root].resource_class != image.resource_class ||
program.info.images[root].numeric_class != image.numeric_class ||
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 ||
@@ -376,13 +368,11 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
} }
continue; continue;
} }
bool canonical_kind = const auto canonical_numeric = image.atomic ? Prospero::TextureNumericClass::Uint
image.kind == ResourceKind::Image || image.kind == ResourceKind::StorageImage; : Prospero::TextureNumericClass::Float;
if (image.atomic) {
canonical_kind = image.kind == ResourceKind::StorageImageUint;
}
if (image.dimension != Decoder::ImageDimension::Dim2D || image.cube || if (image.dimension != Decoder::ImageDimension::Dim2D || image.cube ||
!canonical_kind) { image.resource_class == ImageResourceClass::None ||
image.numeric_class != canonical_numeric) {
return false; return false;
} }
continue; continue;
@@ -392,45 +382,39 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
DescriptorIsCube(descriptor) != image.cube) { DescriptorIsCube(descriptor) != image.cube) {
return false; return false;
} }
if (ImageResourceKindMatches(image.kind, ImageResourceClass::Sampled) || if (image.resource_class == ImageResourceClass::None) {
ImageResourceKindMatches(image.kind, ImageResourceClass::Storage)) { return false;
const bool storage = image.kind == ResourceKind::StorageImage || }
image.kind == ResourceKind::StorageImageUint; const bool storage = image.resource_class == ImageResourceClass::Storage;
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 (image.atomic && format != Prospero::BufferFormat::k32UInt) {
return false;
}
const auto conversion_format = ImageConversionFormat(format);
if (image.conversion_format != conversion_format) {
return false;
}
if ((storage || conversion_format != Prospero::BufferFormat::kInvalid) &&
image.shader_swizzle != DescriptorImageSwizzle(descriptor)) {
return false;
}
auto numeric_class = Prospero::SampledTextureNumericClass(format);
if (storage) {
const bool raw_sint = format == Prospero::BufferFormat::k32SInt && image.written &&
!image.read && !image.atomic;
if ((!raw_sint && numeric_class == Prospero::TextureNumericClass::Sint) ||
numeric_class == Prospero::TextureNumericClass::Unsupported) {
return false; return false;
} }
const auto conversion_format = ImageConversionFormat(format); if (raw_sint) {
if (image.conversion_format != conversion_format) { numeric_class = Prospero::TextureNumericClass::Uint;
return false;
}
if ((storage || conversion_format != Prospero::BufferFormat::kInvalid) &&
image.shader_swizzle != DescriptorImageSwizzle(descriptor)) {
return false;
}
if (storage) {
const bool raw_sint = format == Prospero::BufferFormat::k32SInt && image.written &&
!image.read && !image.atomic;
const auto numeric_class = Prospero::SampledTextureNumericClass(format);
if ((!raw_sint && numeric_class == Prospero::TextureNumericClass::Sint) ||
numeric_class == Prospero::TextureNumericClass::Unsupported) {
return false;
}
const bool uint_descriptor =
numeric_class == Prospero::TextureNumericClass::Uint || raw_sint;
const bool uint_program = image.kind == ResourceKind::StorageImageUint;
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
return false;
}
} else {
const auto numeric_class = SampledResourceNumericClass(image.kind);
if (numeric_class != Prospero::SampledTextureNumericClass(format) ||
(image.depth_compare &&
numeric_class != Prospero::TextureNumericClass::Float)) {
return false;
}
} }
} else if (image.depth_compare && numeric_class != Prospero::TextureNumericClass::Float) {
return false;
}
if (image.numeric_class != numeric_class) {
return false;
} }
} }
return true; return true;
@@ -628,24 +612,20 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
for (uint32_t i = 0; i < next.images.size(); i++) { for (uint32_t i = 0; i < next.images.size(); i++) {
const auto& descriptor = next_snapshot.images[i]; const auto& descriptor = next_snapshot.images[i];
auto& image = next.images[i]; auto& image = next.images[i];
if (image.resource_class == ImageResourceClass::None ||
(image.atomic && image.resource_class != ImageResourceClass::Storage)) {
return SpecializationFail(fmt::format("image resource {} has an invalid class", i));
}
image.mip_count = StorageMipCount(image, descriptor); image.mip_count = StorageMipCount(image, descriptor);
if (image.mip_count == 0u) { if (image.mip_count == 0u) {
return SpecializationFail( return SpecializationFail(
fmt::format("storage image descriptor {} has an invalid mip range", i)); fmt::format("storage image descriptor {} has an invalid mip range", i));
} }
if (NullImageDescriptor(descriptor)) { if (NullImageDescriptor(descriptor)) {
image.dimension = Decoder::ImageDimension::Dim2D; image.numeric_class = image.atomic ? Prospero::TextureNumericClass::Uint
image.cube = false; : Prospero::TextureNumericClass::Float;
switch (image.kind) { image.dimension = Decoder::ImageDimension::Dim2D;
case ResourceKind::ImageUint: image.kind = ResourceKind::Image; break; image.cube = false;
case ResourceKind::ImageSint: image.kind = ResourceKind::Image; break;
case ResourceKind::StorageImageUint:
if (!image.atomic) {
image.kind = ResourceKind::StorageImage;
}
break;
default: break;
}
continue; continue;
} }
const auto descriptor_dimension = DescriptorDimension(descriptor, image.dimension); const auto descriptor_dimension = DescriptorDimension(descriptor, image.dimension);
@@ -666,39 +646,30 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
fmt::format("atomic image descriptor {} uses unsupported format {}", i, fmt::format("atomic image descriptor {} uses unsupported format {}", i,
static_cast<uint32_t>(format))); static_cast<uint32_t>(format)));
} }
const bool storage = image.kind == ResourceKind::StorageImage || const bool storage = image.resource_class == ImageResourceClass::Storage;
image.kind == ResourceKind::StorageImageUint;
image.conversion_format = ImageConversionFormat(format); image.conversion_format = ImageConversionFormat(format);
if (storage || image.conversion_format != Prospero::BufferFormat::kInvalid) { if (storage || image.conversion_format != Prospero::BufferFormat::kInvalid) {
image.shader_swizzle = DescriptorImageSwizzle(descriptor); image.shader_swizzle = DescriptorImageSwizzle(descriptor);
} }
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt && const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
image.written && !image.read && !image.atomic; image.written && !image.read && !image.atomic;
const auto numeric_class = Prospero::SampledTextureNumericClass(format); image.numeric_class = Prospero::SampledTextureNumericClass(format);
if (storage) { if (storage) {
if ((!raw_sint_storage && numeric_class == Prospero::TextureNumericClass::Sint) || if ((!raw_sint_storage && image.numeric_class == Prospero::TextureNumericClass::Sint) ||
numeric_class == Prospero::TextureNumericClass::Unsupported) { image.numeric_class == Prospero::TextureNumericClass::Unsupported) {
return SpecializationFail( return SpecializationFail(
fmt::format("storage image descriptor {} uses unsupported format {}", i, fmt::format("storage image descriptor {} uses unsupported format {}", i,
static_cast<uint32_t>(format))); static_cast<uint32_t>(format)));
} }
if (numeric_class == Prospero::TextureNumericClass::Uint || raw_sint_storage) { if (raw_sint_storage) {
image.kind = ResourceKind::StorageImageUint; image.numeric_class = Prospero::TextureNumericClass::Uint;
}
} else {
switch (numeric_class) {
case Prospero::TextureNumericClass::Uint:
image.kind = ResourceKind::ImageUint;
break;
case Prospero::TextureNumericClass::Sint:
image.kind = ResourceKind::ImageSint;
break;
case Prospero::TextureNumericClass::Float: break;
case Prospero::TextureNumericClass::Unsupported:
return SpecializationFail(
fmt::format("sampled image descriptor {} uses unsupported format {}", i,
static_cast<uint32_t>(format)));
} }
} else if (image.numeric_class == Prospero::TextureNumericClass::Unsupported ||
(image.depth_compare &&
image.numeric_class != Prospero::TextureNumericClass::Float)) {
return SpecializationFail(
fmt::format("sampled image descriptor {} uses unsupported format {}", i,
static_cast<uint32_t>(format)));
} }
} }
for (uint32_t root_index = 0; root_index < next.images.size(); root_index++) { for (uint32_t root_index = 0; root_index < next.images.size(); root_index++) {
@@ -733,14 +704,17 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
continue; continue;
} }
if (NullImageDescriptor(next_snapshot.images[candidate])) { if (NullImageDescriptor(next_snapshot.images[candidate])) {
image.kind = image_class.kind; image.resource_class = image_class.resource_class;
image.numeric_class = image_class.numeric_class;
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.conversion_format = image_class.conversion_format; image.conversion_format = image_class.conversion_format;
image.shader_swizzle = image_class.shader_swizzle; 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.resource_class != image_class.resource_class ||
image.numeric_class != image_class.numeric_class ||
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.conversion_format != image_class.conversion_format || image.conversion_format != image_class.conversion_format ||
@@ -824,9 +798,6 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
fmt::format("indirect image table at pc 0x{:08x} is used by unsupported {}", fmt::format("indirect image table at pc 0x{:08x} is used by unsupported {}",
inst.Flags<MemoryFlags>().pc, ValueOpcodeName(inst.GetOpcode()))); inst.Flags<MemoryFlags>().pc, ValueOpcodeName(inst.GetOpcode())));
} }
memory.kind = image.kind;
memory.image_dimension = image.dimension;
memory.image_cube = image.cube;
} }
} }
program.info = std::move(next); program.info = std::move(next);
@@ -80,17 +80,6 @@ uint32_t ByteExtent(const MemoryInfo& memory) {
return end > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(end); return end > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(end);
} }
bool IsStorageImage(ResourceKind kind) {
return kind == ResourceKind::StorageImage || kind == ResourceKind::StorageImageUint;
}
ImageMipMode MipMode(const MemoryInfo& memory) {
if (IsStorageImage(memory.kind) && memory.image_has_mip) {
return ImageMipMode::DynamicStorage;
}
return ImageMipMode::None;
}
class Tracker { class Tracker {
public: public:
explicit Tracker(Program& program): m_program(program), m_info(program.info) { explicit Tracker(Program& program): m_program(program), m_info(program.info) {
@@ -526,18 +515,16 @@ private:
} }
uint32_t AddImage(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) { uint32_t AddImage(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) {
const auto mip = MipMode(memory); const auto resource_class = ImageOpcodeInfoOf(op).resource_class;
const auto mip = resource_class == ImageResourceClass::Storage && memory.image_has_mip
? ImageMipMode::DynamicStorage
: ImageMipMode::None;
const bool depth = (memory.image_sample_flags & Decoder::ImageSampleFlagCompare) != 0; const bool depth = (memory.image_sample_flags & Decoder::ImageSampleFlagCompare) != 0;
for (uint32_t i = 0; i < m_info.images.size(); i++) { for (uint32_t i = 0; i < m_info.images.size(); i++) {
auto& image = m_info.images[i]; auto& image = m_info.images[i];
const bool compatible_kind = image.kind == memory.kind || (IsStorageImage(image.kind) && if (image.source == source && image.resource_class == resource_class &&
IsStorageImage(memory.kind));
if (image.source == source && compatible_kind &&
image.dimension == memory.image_dimension && image.mip_mode == mip && image.dimension == memory.image_dimension && image.mip_mode == mip &&
image.depth_compare == depth && image.r128 == memory.image_r128) { image.depth_compare == depth && image.r128 == memory.image_r128) {
if (memory.kind == ResourceKind::StorageImageUint) {
image.kind = ResourceKind::StorageImageUint;
}
Merge(image, op, pc); Merge(image, op, pc);
return i; return i;
} }
@@ -546,13 +533,13 @@ private:
return UINT32_MAX; return UINT32_MAX;
} }
ImageResource image; ImageResource image;
image.source = source; image.source = source;
image.first_use_pc = pc; image.first_use_pc = pc;
image.kind = memory.kind; image.resource_class = resource_class;
image.dimension = memory.image_dimension; image.dimension = memory.image_dimension;
image.mip_mode = mip; image.mip_mode = mip;
image.depth_compare = depth; image.depth_compare = depth;
image.r128 = memory.image_r128; image.r128 = memory.image_r128;
Merge(image, op, pc); Merge(image, op, pc);
m_info.images.push_back(image); m_info.images.push_back(image);
return static_cast<uint32_t>(m_info.images.size() - 1); return static_cast<uint32_t>(m_info.images.size() - 1);
@@ -681,7 +668,8 @@ private:
return; return;
} }
if (!ImageResourceKindMatches(memory.kind, image_info.resource_class)) { if (memory.kind != ResourceKind::Image ||
image_info.resource_class == ImageResourceClass::None) {
Fail(flags.pc, "image operation has invalid resource kind"); Fail(flags.pc, "image operation has invalid resource kind");
} }
handle = inst.Arg(0).Resolve().TryInstruction(); handle = inst.Arg(0).Resolve().TryInstruction();
+3 -37
View File
@@ -584,7 +584,7 @@ void TestImagesSamplersAndAliases() {
const auto storage = fixture.Image(image_words, 16); const auto storage = fixture.Image(image_words, 16);
MemoryInfo storage_memory; MemoryInfo storage_memory;
storage_memory.kind = ResourceKind::StorageImageUint; storage_memory.kind = ResourceKind::Image;
storage_memory.image_dimension = Decoder::ImageDimension::Dim2D; storage_memory.image_dimension = Decoder::ImageDimension::Dim2D;
fixture.Emit(ValueOpcode::ImageAtomicIAdd32, fixture.Emit(ValueOpcode::ImageAtomicIAdd32,
{storage, image_address, Value(1u), Value(true)}, {storage, image_address, Value(1u), Value(true)},
@@ -713,7 +713,7 @@ void TestDynamicStorageMipTracking() {
{Value(0u), Value(0u), lod, Value(0u), Value(0u), Value(0u), Value(0u), {Value(0u), Value(0u), lod, Value(0u), Value(0u), Value(0u), Value(0u),
Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u)}); Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u)});
MemoryInfo memory; MemoryInfo memory;
memory.kind = ResourceKind::StorageImage; memory.kind = ResourceKind::Image;
memory.image_dimension = Decoder::ImageDimension::Dim2D; memory.image_dimension = Decoder::ImageDimension::Dim2D;
memory.image_address_components = has_mip ? 3u : 2u; memory.image_address_components = has_mip ? 3u : 2u;
memory.image_has_mip = has_mip; memory.image_has_mip = has_mip;
@@ -776,7 +776,7 @@ void TestDynamicStorageMipTracking() {
Program null_program; Program null_program;
null_program.resource_tracking_complete = true; null_program.resource_tracking_complete = true;
ImageResource null_image; ImageResource null_image;
null_image.kind = ResourceKind::StorageImage; null_image.resource_class = ImageResourceClass::Storage;
null_image.dimension = Decoder::ImageDimension::Dim2D; null_image.dimension = Decoder::ImageDimension::Dim2D;
null_image.mip_mode = ImageMipMode::DynamicStorage; null_image.mip_mode = ImageMipMode::DynamicStorage;
null_image.written = true; null_image.written = true;
@@ -1283,40 +1283,6 @@ void TestMalformedMemoryKindsRejected() {
"image operation has invalid resource kind", "image operation has invalid resource kind",
"resource tracking accepted an image opcode with address metadata"); "resource tracking accepted an image opcode with address metadata");
} }
{
Fixture fixture;
const auto image =
fixture.Image({Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
Value(0u), Value(0u), Value(0u)},
12);
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
fixture.Emit(ValueOpcode::ImageRead,
{image, fixture.ImageAddress(), Value(true)},
fixture.AddMemory(memory, 12));
BuildSrtPlan(fixture.program);
CheckFatal(
[&] { TrackResources(fixture.program); },
"image operation has invalid resource kind",
"resource tracking accepted a sampled read with storage metadata");
}
{
Fixture fixture;
const auto image =
fixture.Image({Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
Value(0u), Value(0u), Value(0u)},
16);
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
fixture.Emit(ValueOpcode::ImageAtomicIAdd32,
{image, fixture.ImageAddress(), Value(1u), Value(true)},
fixture.AddMemory(memory, 16));
BuildSrtPlan(fixture.program);
CheckFatal(
[&] { TrackResources(fixture.program); },
"image operation has invalid resource kind",
"resource tracking accepted a uint atomic with float storage metadata");
}
} }
} // namespace } // namespace
+66 -36
View File
@@ -7721,7 +7721,9 @@ public:
null_program.stage = ShaderType::Compute; null_program.stage = ShaderType::Compute;
null_program.resource_tracking_complete = true; null_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource null_resource{}; ShaderRecompiler::IR::ImageResource null_resource{};
null_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; null_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
null_resource.numeric_class = Prospero::TextureNumericClass::Uint;
null_resource.dimension = null_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D; ShaderRecompiler::Decoder::ImageDimension::Dim2D;
null_resource.read = true; null_resource.read = true;
@@ -7730,7 +7732,8 @@ public:
null_volume.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim3D; null_volume.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim3D;
null_program.info.images.push_back(null_volume); null_program.info.images.push_back(null_volume);
auto null_storage = null_resource; auto null_storage = null_resource;
null_storage.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; null_storage.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Storage;
null_storage.read = false; null_storage.read = false;
null_storage.written = true; null_storage.written = true;
null_program.info.images.push_back(null_storage); null_program.info.images.push_back(null_storage);
@@ -7826,8 +7829,9 @@ public:
storage_program.stage = ShaderType::Vertex; storage_program.stage = ShaderType::Vertex;
storage_program.resource_tracking_complete = true; storage_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource storage_resource{}; ShaderRecompiler::IR::ImageResource storage_resource{};
storage_resource.kind = storage_resource.resource_class =
ShaderRecompiler::IR::ResourceKind::StorageImageUint; ShaderRecompiler::IR::ImageResourceClass::Storage;
storage_resource.numeric_class = Prospero::TextureNumericClass::Uint;
storage_resource.dimension = storage_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D; ShaderRecompiler::Decoder::ImageDimension::Dim2D;
storage_resource.written = true; storage_resource.written = true;
@@ -7870,8 +7874,10 @@ public:
ShaderRecompiler::IR::ImageMipMode::DynamicStorage; ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
mipped_storage_resource.mip_count = 3; mipped_storage_resource.mip_count = 3;
auto sampled_overwide_resource = storage_resource; auto sampled_overwide_resource = storage_resource;
sampled_overwide_resource.kind = sampled_overwide_resource.resource_class =
ShaderRecompiler::IR::ResourceKind::ImageUint; ShaderRecompiler::IR::ImageResourceClass::Sampled;
sampled_overwide_resource.numeric_class =
Prospero::TextureNumericClass::Uint;
sampled_overwide_resource.read = true; sampled_overwide_resource.read = true;
sampled_overwide_resource.written = false; sampled_overwide_resource.written = false;
auto plain_mipped_storage_binding = auto plain_mipped_storage_binding =
@@ -7979,8 +7985,10 @@ public:
srgb_storage_descriptor.dwords.begin()); srgb_storage_descriptor.dwords.begin());
srgb_storage_descriptor.dword_count = 8; srgb_storage_descriptor.dword_count = 8;
auto srgb_storage_resource = storage_resource; auto srgb_storage_resource = storage_resource;
srgb_storage_resource.kind = srgb_storage_resource.resource_class =
ShaderRecompiler::IR::ResourceKind::StorageImage; ShaderRecompiler::IR::ImageResourceClass::Storage;
srgb_storage_resource.numeric_class =
Prospero::TextureNumericClass::Float;
const auto srgb_storage_binding = const auto srgb_storage_binding =
RenderExecutorTestAccess::ResolveTexture( RenderExecutorTestAccess::ResolveTexture(
executor, srgb_storage_resource, srgb_storage_descriptor); executor, srgb_storage_resource, srgb_storage_descriptor);
@@ -8023,8 +8031,9 @@ public:
sint_storage_descriptor.dwords.begin()); sint_storage_descriptor.dwords.begin());
sint_storage_descriptor.dword_count = 8; sint_storage_descriptor.dword_count = 8;
auto sint_storage_resource = srgb_storage_resource; auto sint_storage_resource = srgb_storage_resource;
sint_storage_resource.kind = sint_storage_resource.resource_class =
ShaderRecompiler::IR::ResourceKind::StorageImageUint; ShaderRecompiler::IR::ImageResourceClass::Storage;
sint_storage_resource.numeric_class = Prospero::TextureNumericClass::Uint;
const auto sint_storage_binding = const auto sint_storage_binding =
RenderExecutorTestAccess::ResolveTexture( RenderExecutorTestAccess::ResolveTexture(
executor, sint_storage_resource, sint_storage_descriptor); executor, sint_storage_resource, sint_storage_descriptor);
@@ -8105,7 +8114,9 @@ public:
sampled_program.stage = ShaderType::Pixel; sampled_program.stage = ShaderType::Pixel;
sampled_program.resource_tracking_complete = true; sampled_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource sampled_resource{}; ShaderRecompiler::IR::ImageResource sampled_resource{};
sampled_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; sampled_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
sampled_resource.numeric_class = Prospero::TextureNumericClass::Uint;
sampled_resource.dimension = sampled_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D; ShaderRecompiler::Decoder::ImageDimension::Dim2D;
sampled_resource.read = true; sampled_resource.read = true;
@@ -8551,8 +8562,10 @@ public:
sampled_depth_value.dwords.begin()); sampled_depth_value.dwords.begin());
sampled_depth_value.dword_count = 8; sampled_depth_value.dword_count = 8;
ShaderRecompiler::IR::ImageResource sampled_depth_resource{}; ShaderRecompiler::IR::ImageResource sampled_depth_resource{};
sampled_depth_resource.kind = sampled_depth_resource.resource_class =
ShaderRecompiler::IR::ResourceKind::ImageUint; ShaderRecompiler::IR::ImageResourceClass::Sampled;
sampled_depth_resource.numeric_class =
Prospero::TextureNumericClass::Uint;
sampled_depth_resource.dimension = sampled_depth_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D; ShaderRecompiler::Decoder::ImageDimension::Dim2D;
sampled_depth_resource.read = true; sampled_depth_resource.read = true;
@@ -8661,7 +8674,9 @@ public:
array_program.stage = ShaderType::Compute; array_program.stage = ShaderType::Compute;
array_program.resource_tracking_complete = true; array_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource array_resource{}; ShaderRecompiler::IR::ImageResource array_resource{};
array_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; array_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
array_resource.numeric_class = Prospero::TextureNumericClass::Uint;
array_resource.dimension = array_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray; ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
array_resource.read = true; array_resource.read = true;
@@ -9078,7 +9093,9 @@ public:
program.stage = ShaderType::Compute; program.stage = ShaderType::Compute;
program.resource_tracking_complete = true; program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource stencil_resource{}; ShaderRecompiler::IR::ImageResource stencil_resource{};
stencil_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; stencil_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
stencil_resource.numeric_class = Prospero::TextureNumericClass::Uint;
stencil_resource.dimension = stencil_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D; ShaderRecompiler::Decoder::ImageDimension::Dim2D;
stencil_resource.read = true; stencil_resource.read = true;
@@ -9151,8 +9168,8 @@ public:
stencil_storage_program->stage = ShaderType::Compute; stencil_storage_program->stage = ShaderType::Compute;
stencil_storage_program->resource_tracking_complete = true; stencil_storage_program->resource_tracking_complete = true;
auto stencil_storage_resource = stencil_resource; auto stencil_storage_resource = stencil_resource;
stencil_storage_resource.kind = stencil_storage_resource.resource_class =
ShaderRecompiler::IR::ResourceKind::StorageImageUint; ShaderRecompiler::IR::ImageResourceClass::Storage;
stencil_storage_resource.read = false; stencil_storage_resource.read = false;
stencil_storage_resource.written = true; stencil_storage_resource.written = true;
stencil_storage_program->info.images.push_back(stencil_storage_resource); stencil_storage_program->info.images.push_back(stencil_storage_resource);
@@ -20509,7 +20526,8 @@ void CheckIndirectImageKeySwitch() {
ImageResource root{}; ImageResource root{};
root.source = 0; root.source = 0;
root.first_use_pc = 0x10f0u; root.first_use_pc = 0x10f0u;
root.kind = ResourceKind::Image; root.resource_class = ImageResourceClass::Sampled;
root.numeric_class = Prospero::TextureNumericClass::Float;
root.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; root.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
root.read = true; root.read = true;
root.indirect_root = 0; root.indirect_root = 0;
@@ -21963,11 +21981,13 @@ void CheckRenderTargetFormatContract() {
(void)volume.FindView(view); (void)volume.FindView(view);
} else { } else {
ShaderRecompiler::IR::ImageResource resource{}; ShaderRecompiler::IR::ImageResource resource{};
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImage; resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Storage;
resource.numeric_class = Prospero::TextureNumericClass::Float;
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
resource.written = true; resource.written = true;
if (std::strcmp(kind, "storage-kind") == 0) { if (std::strcmp(kind, "storage-kind") == 0) {
resource.kind = ShaderRecompiler::IR::ResourceKind::Image; resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
} else if (std::strcmp(kind, "storage-no-write") == 0) { } else if (std::strcmp(kind, "storage-no-write") == 0) {
resource.written = false; resource.written = false;
} else if (std::strcmp(kind, "storage-nonuint-atomic") == 0) { } else if (std::strcmp(kind, "storage-nonuint-atomic") == 0) {
@@ -21992,7 +22012,9 @@ void CheckSampledColorViews() {
DstSel(4, 5, 6, 7)) == DstSel(4, 5, 6, 7), DstSel(4, 5, 6, 7)) == DstSel(4, 5, 6, 7),
"RGBA did not select the identity view"); "RGBA did not select the identity view");
ShaderRecompiler::IR::ImageResource cube_resource{}; ShaderRecompiler::IR::ImageResource cube_resource{};
cube_resource.kind = ShaderRecompiler::IR::ResourceKind::Image; cube_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
cube_resource.numeric_class = Prospero::TextureNumericClass::Float;
cube_resource.dimension = cube_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray; ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
cube_resource.read = true; cube_resource.read = true;
@@ -22164,7 +22186,9 @@ void CheckSampledColorViews() {
DstSel(4, 0, 0, 1)) == DstSel(4, 0, 0, 1), DstSel(4, 0, 0, 1)) == DstSel(4, 0, 0, 1),
"D32S8 depth target did not select its R001 depth-aspect view"); "D32S8 depth target did not select its R001 depth-aspect view");
ShaderRecompiler::IR::ImageResource storage_resource{}; ShaderRecompiler::IR::ImageResource storage_resource{};
storage_resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImage; storage_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Storage;
storage_resource.numeric_class = Prospero::TextureNumericClass::Float;
storage_resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; storage_resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
storage_resource.written = true; storage_resource.written = true;
Require("SampledColorViews", "storage resource", Require("SampledColorViews", "storage resource",
@@ -22181,7 +22205,7 @@ void CheckSampledColorViews() {
Require("SampledColorViews", "write-only 2D-array storage resource", Require("SampledColorViews", "write-only 2D-array storage resource",
IsSupportedStorageImageResource(storage_resource), IsSupportedStorageImageResource(storage_resource),
"basic write-only 2D-array storage resource was rejected"); "basic write-only 2D-array storage resource was rejected");
storage_resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; storage_resource.numeric_class = Prospero::TextureNumericClass::Uint;
Require("SampledColorViews", "write-only uint 2D-array storage resource", Require("SampledColorViews", "write-only uint 2D-array storage resource",
IsSupportedStorageImageResource(storage_resource), IsSupportedStorageImageResource(storage_resource),
"basic write-only uint 2D-array storage resource was rejected"); "basic write-only uint 2D-array storage resource was rejected");
@@ -22237,7 +22261,8 @@ void CheckSampledColorViews() {
void CheckSampledDepthResource() { void CheckSampledDepthResource() {
ShaderRecompiler::IR::ImageResource resource{}; ShaderRecompiler::IR::ImageResource resource{};
resource.kind = ShaderRecompiler::IR::ResourceKind::Image; resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Sampled;
resource.numeric_class = Prospero::TextureNumericClass::Float;
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
resource.read = true; resource.read = true;
resource.depth_compare = true; resource.depth_compare = true;
@@ -22289,7 +22314,7 @@ void CheckSampledDepthResource() {
static_cast<vk::ImageViewType>(VK_IMAGE_VIEW_TYPE_MAX_ENUM), static_cast<vk::ImageViewType>(VK_IMAGE_VIEW_TYPE_MAX_ENUM),
"array shader resource accepted a non-array descriptor view"); "array shader resource accepted a non-array descriptor view");
resource = basic; resource = basic;
resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; resource.numeric_class = Prospero::TextureNumericClass::Uint;
Require("SampledDepthResource", "integer read", Require("SampledDepthResource", "integer read",
IsSupportedSampledDepthResource(resource), IsSupportedSampledDepthResource(resource),
"read-only uint depth resource was rejected"); "read-only uint depth resource was rejected");
@@ -22310,7 +22335,8 @@ void CheckSampledVideoOutView(RenderContext &renderer) {
auto &context = renderer.GetGraphics(); auto &context = renderer.GetGraphics();
CommandScheduler scheduler(renderer, context); CommandScheduler scheduler(renderer, context);
ShaderRecompiler::IR::ImageResource resource{}; ShaderRecompiler::IR::ImageResource resource{};
resource.kind = ShaderRecompiler::IR::ResourceKind::Image; resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Sampled;
resource.numeric_class = Prospero::TextureNumericClass::Float;
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
resource.read = true; resource.read = true;
@@ -22758,7 +22784,9 @@ void CheckSampledDepthDescriptor(RenderContext &renderer) {
Prospero::ImageType::kColor2D)); Prospero::ImageType::kColor2D));
cube_image.usage.depth_target = true; cube_image.usage.depth_target = true;
ShaderRecompiler::IR::ImageResource cube_resource{}; ShaderRecompiler::IR::ImageResource cube_resource{};
cube_resource.kind = ShaderRecompiler::IR::ResourceKind::Image; cube_resource.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
cube_resource.numeric_class = Prospero::TextureNumericClass::Float;
cube_resource.dimension = cube_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray; ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
cube_resource.read = true; cube_resource.read = true;
@@ -22845,7 +22873,8 @@ void CheckSampledDepthDescriptor(RenderContext &renderer) {
ShaderRecompiler::IR::ImageResource BasicStorageTextureResource() { ShaderRecompiler::IR::ImageResource BasicStorageTextureResource() {
ShaderRecompiler::IR::ImageResource resource{}; ShaderRecompiler::IR::ImageResource resource{};
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImage; resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Storage;
resource.numeric_class = Prospero::TextureNumericClass::Float;
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim3D; resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim3D;
resource.read = true; resource.read = true;
resource.written = true; resource.written = true;
@@ -22903,7 +22932,7 @@ ShaderTextureResource Ppsa01340OverwideMipStorageTextureDescriptor() {
ShaderRecompiler::IR::ImageResource Ppsa01530MaxMipStorageTextureResource() { ShaderRecompiler::IR::ImageResource Ppsa01530MaxMipStorageTextureResource() {
auto resource = BasicBgraStorageTextureResource(); auto resource = BasicBgraStorageTextureResource();
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; resource.numeric_class = Prospero::TextureNumericClass::Uint;
return resource; return resource;
} }
@@ -22940,7 +22969,7 @@ ShaderTextureResource BasicArrayStorageTextureDescriptor() {
ShaderRecompiler::IR::ImageResource BasicUintArrayStorageTextureResource() { ShaderRecompiler::IR::ImageResource BasicUintArrayStorageTextureResource() {
auto resource = BasicArrayStorageTextureResource(); auto resource = BasicArrayStorageTextureResource();
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; resource.numeric_class = Prospero::TextureNumericClass::Uint;
return resource; return resource;
} }
@@ -22975,7 +23004,7 @@ ShaderTextureResource Ppsa10112D16StorageTextureDescriptor() {
ShaderRecompiler::IR::ImageResource BasicUintVolumeStorageTextureResource() { ShaderRecompiler::IR::ImageResource BasicUintVolumeStorageTextureResource() {
auto resource = BasicStorageTextureResource(); auto resource = BasicStorageTextureResource();
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; resource.numeric_class = Prospero::TextureNumericClass::Uint;
resource.read = false; resource.read = false;
return resource; return resource;
} }
@@ -22987,7 +23016,7 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() {
ShaderRecompiler::IR::ImageResource AtomicStorageTextureResource() { ShaderRecompiler::IR::ImageResource AtomicStorageTextureResource() {
auto resource = BasicLinearStorageTextureResource(); auto resource = BasicLinearStorageTextureResource();
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; resource.numeric_class = Prospero::TextureNumericClass::Uint;
resource.read = true; resource.read = true;
resource.atomic = true; resource.atomic = true;
return resource; return resource;
@@ -23346,8 +23375,7 @@ void CheckBasicStorageTextureDescriptor() {
0x00000000u, 0x00000000u,
}}; }};
auto standard256b_resource = BasicBgraStorageTextureResource(); auto standard256b_resource = BasicBgraStorageTextureResource();
standard256b_resource.kind = standard256b_resource.numeric_class = Prospero::TextureNumericClass::Uint;
ShaderRecompiler::IR::ResourceKind::StorageImageUint;
TileSizeAlign standard256b_size{}; TileSizeAlign standard256b_size{};
TileGetTextureTotalSize(standard256b.Format(), standard256b.Width5() + 1u, TileGetTextureTotalSize(standard256b.Format(), standard256b.Width5() + 1u,
standard256b.Height5() + 1u, standard256b.Height5() + 1u,
@@ -23645,7 +23673,8 @@ void CheckImageSamplerSpecialization() {
ShaderRecompiler::IR::Program mixed_sampler_program; ShaderRecompiler::IR::Program mixed_sampler_program;
mixed_sampler_program.resource_tracking_complete = true; mixed_sampler_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource sampled_image; ShaderRecompiler::IR::ImageResource sampled_image;
sampled_image.kind = ShaderRecompiler::IR::ResourceKind::Image; sampled_image.resource_class =
ShaderRecompiler::IR::ImageResourceClass::Sampled;
sampled_image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; sampled_image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
sampled_image.read = true; sampled_image.read = true;
mixed_sampler_program.info.images = {sampled_image, sampled_image, mixed_sampler_program.info.images = {sampled_image, sampled_image,
@@ -23739,7 +23768,8 @@ void CheckShaderRecompilerFatalContracts() {
program.resource_tracking_complete = true; program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource image; ShaderRecompiler::IR::ImageResource image;
image.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; image.resource_class = ShaderRecompiler::IR::ImageResourceClass::Storage;
image.numeric_class = Prospero::TextureNumericClass::Uint;
image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
image.read = true; image.read = true;
image.written = true; image.written = true;
+4 -42
View File
@@ -9792,46 +9792,6 @@ void TestNativeWideValueValidation() {
append_image(program, ValueOpcode::ImageRead, {}); append_image(program, ValueOpcode::ImageRead, {});
check_rejected(program, "buffer kind for image read was accepted"); check_rejected(program, "buffer kind for image read was accepted");
} }
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageRead, {});
check_rejected(program, "storage kind for image read was accepted");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImageUint;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageQueryDimensions, {});
check_rejected(program, "storage uint kind for image query was accepted");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::Image;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageWrite, {});
check_rejected(program, "sampled image kind for image write was accepted");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageSampleRaw, {});
check_rejected(program, "storage kind for image sample was accepted");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageAtomicIAdd32, {});
check_rejected(program, "untyped storage kind for image atomic was accepted");
}
{ {
auto program = make_program(); auto program = make_program();
IREmitter ir(program.blocks.front()); IREmitter ir(program.blocks.front());
@@ -10435,8 +10395,10 @@ void TestNewShaderRecompilerNativeBindingPlan() {
uint32_t float_storage = UINT32_MAX; uint32_t float_storage = UINT32_MAX;
for (uint32_t i = 0; i < result.program.info.images.size(); i++) { for (uint32_t i = 0; i < result.program.info.images.size(); i++) {
if (result.program.info.images[i].kind == const auto &image = result.program.info.images[i];
ShaderRecompiler::IR::ResourceKind::StorageImage) { if (image.resource_class ==
ShaderRecompiler::IR::ImageResourceClass::Storage &&
image.numeric_class == Prospero::TextureNumericClass::Float) {
float_storage = i; float_storage = i;
break; break;
} }