refactor(shader): centralize typed resource metadata

This commit is contained in:
nmzik 2026-08-19 11:33:05 +02:00
parent 87166eed94
commit 85cf965d54
18 changed files with 455 additions and 314 deletions

View File

@ -13,51 +13,6 @@ using ShaderError::Fail;
namespace {
bool ImageBinding(const IR::ImageResource& image, IR::DescriptorBindingKind& kind) {
using Kind = IR::DescriptorBindingKind;
using Dim = Decoder::ImageDimension;
if (image.kind == IR::ResourceKind::Image || image.kind == IR::ResourceKind::ImageUint) {
const bool integer = image.kind == IR::ResourceKind::ImageUint;
switch (image.dimension) {
case Dim::Dim1D: kind = integer ? Kind::SampledUint1D : Kind::Sampled1D; return true;
case Dim::Dim1DArray:
kind = integer ? Kind::SampledUint1DArray : Kind::Sampled1DArray;
return true;
case Dim::Dim2D: kind = integer ? Kind::SampledUint2D : Kind::Sampled2D; return true;
case Dim::Dim2DMsaa:
kind = integer ? Kind::SampledUint2DMsaa : Kind::Sampled2DMsaa;
return true;
case Dim::Dim3D: kind = integer ? Kind::SampledUint3D : Kind::Sampled3D; return true;
case Dim::Dim2DArray:
kind = integer ? Kind::SampledUint2DArray : Kind::Sampled2DArray;
return true;
case Dim::Dim2DMsaaArray:
kind = integer ? Kind::SampledUint2DMsaaArray : Kind::Sampled2DMsaaArray;
return true;
case Dim::Unknown: return false;
}
}
const bool uint_image = image.kind == IR::ResourceKind::StorageImageUint;
if (image.kind != IR::ResourceKind::StorageImage && !uint_image) {
return false;
}
switch (image.dimension) {
case Dim::Dim1D: kind = uint_image ? Kind::StorageUint1D : Kind::Storage1D; return true;
case Dim::Dim1DArray:
kind = uint_image ? Kind::StorageUint1DArray : Kind::Storage1DArray;
return true;
case Dim::Dim2D: kind = uint_image ? Kind::StorageUint2D : Kind::Storage2D; return true;
case Dim::Dim3D: kind = uint_image ? Kind::StorageUint3D : Kind::Storage3D; return true;
case Dim::Dim2DArray:
kind = uint_image ? Kind::StorageUint2DArray : Kind::Storage2DArray;
return true;
case Dim::Dim2DMsaa:
case Dim::Dim2DMsaaArray: return false;
case Dim::Unknown: return false;
}
return false;
}
bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
using Kind = IR::DescriptorBindingKind;
constexpr auto KindCount = static_cast<size_t>(Kind::Count);
@ -79,18 +34,18 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
Expect(Kind::Buffers, Dense(program.info.buffers.size()));
}
for (uint32_t i = 0; i < program.info.images.size(); i++) {
Kind kind;
if (!ImageBinding(program.info.images[i], kind)) {
const auto kind = IR::DescriptorBindingForImage(program.info.images[i]);
if (!kind.has_value()) {
return Fail(error, "native shader plan has an invalid image class");
}
present[static_cast<size_t>(kind)] = true;
present[static_cast<size_t>(*kind)] = true;
const auto dynamic = program.info.images[i].mip_mode == IR::ImageMipMode::DynamicStorage;
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
return Fail(error, "native shader plan has an invalid image mip descriptor count");
}
expected[static_cast<size_t>(kind)].insert(expected[static_cast<size_t>(kind)].end(), count,
i);
expected[static_cast<size_t>(*kind)].insert(expected[static_cast<size_t>(*kind)].end(),
count, i);
}
if (!program.info.samplers.empty()) {
Expect(Kind::Samplers, Dense(program.info.samplers.size()));

View File

@ -520,13 +520,6 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::FindUMsb64:
ctx.Define(inst, EmitFindMsb64(state, ctx.Arg(inst, 0)));
return true;
case IR::ValueOpcode::FindSMsb32: {
const auto value =
EmitExt(state, TypeI32(state), 74u,
{NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0))});
ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value));
return true;
}
case IR::ValueOpcode::SMin32:
ctx.Define(inst, EmitMinMaxI32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false));
return true;
@ -591,26 +584,6 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpUGreaterThan, OpUGreaterThan));
return true;
case IR::ValueOpcode::SGreaterThan64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSGreaterThan, OpUGreaterThan));
return true;
case IR::ValueOpcode::ULessThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpULessThan, OpULessThanEqual));
return true;
case IR::ValueOpcode::SLessThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSLessThan, OpULessThanEqual));
return true;
case IR::ValueOpcode::UGreaterThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpUGreaterThan, OpUGreaterThanEqual));
return true;
case IR::ValueOpcode::SGreaterThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSGreaterThan, OpUGreaterThanEqual));
return true;
case IR::ValueOpcode::LogicalOr: return binary(OpLogicalOr, IR::Type::U1);
case IR::ValueOpcode::LogicalAnd: return binary(OpLogicalAnd, IR::Type::U1);
case IR::ValueOpcode::LogicalXor: return binary(OpLogicalNotEqual, IR::Type::U1);

View File

@ -363,8 +363,7 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) {
bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) {
auto& state = ctx.state;
switch (inst.GetOpcode()) {
case IR::ValueOpcode::Identity:
case IR::ValueOpcode::ConditionRef: ctx.Define(inst, ctx.Arg(inst, 0)); return true;
case IR::ValueOpcode::Identity: ctx.Define(inst, ctx.Arg(inst, 0)); return true;
case IR::ValueOpcode::Void:
case IR::ValueOpcode::Reference:
case IR::ValueOpcode::ReferenceU32:

View File

@ -345,8 +345,9 @@ uint32_t ImageAtomicOpcode(IR::ValueOpcode opcode) {
} // namespace
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto op = inst.GetOpcode();
if (op < IR::ValueOpcode::ImageQueryDimensions || op > IR::ValueOpcode::ImageAtomicXor32) {
const auto op = inst.GetOpcode();
const auto image_info = IR::ImageOpcodeInfoOf(op);
if (image_info.access == IR::ImageAccess::None) {
return false;
}
auto& state = ctx.state;
@ -354,10 +355,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto pc = inst.Flags<IR::MemoryFlags>().pc;
const auto image_arg = inst.Arg(0);
ctx.ResourceIndex(image_arg, IR::ValueOpcode::GetImageResource);
const bool sampled = op == IR::ValueOpcode::ImageQueryLod ||
op == IR::ValueOpcode::ImageSampleRaw ||
op == IR::ValueOpcode::ImageGatherRaw;
const auto* address = ctx.ImageAddress(inst.Arg(sampled ? 2 : 1));
const auto* address = ctx.ImageAddress(inst.Arg(image_info.needs_sampler ? 2 : 1));
if (address == nullptr) return true;
if (op == IR::ValueOpcode::ImageQueryDimensions) {
state.builder.RequireCapability(CapabilityImageQuery);
@ -392,7 +390,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
ctx.Define(
inst,
EmitValueOrDefaultIfCondition(
state, condition, TypeU32Vector(state, 4), ConstantU32CompositeZero(state, 4), [&]() {
state, condition, TypeU32Vector(state, 4), ConstantU32CompositeZero(state, 4),
[&]() {
const auto image = LoadSampledImageDescriptor(state, mem, pc, view);
const auto color = state.builder.AllocateId();
const auto coord = CoordU32(ctx, mem, *address, view);

View File

@ -867,10 +867,9 @@ uint32_t LoadWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co
const auto base = ByteAddress(ctx, inst, mem);
std::array<uint32_t, 4> values {};
for (uint32_t component = 0; component < components; component++) {
const auto address = component == 0u
? base
: Binary(state, OpIAdd, TypeU32(state), base,
ConstantU32(state, component * 4u));
const auto address = component == 0u ? base
: Binary(state, OpIAdd, TypeU32(state), base,
ConstantU32(state, component * 4u));
const auto raw_index = Binary(state, OpShiftRightLogical, TypeU32(state), address,
ConstantU32(state, 2));
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
@ -889,10 +888,9 @@ void StoreWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
const auto resource = PrepareMemoryResourceAccess(state, mem);
const auto base = ByteAddress(ctx, inst, mem);
for (uint32_t component = 0; component < components; component++) {
const auto address = component == 0u
? base
: Binary(state, OpIAdd, TypeU32(state), base,
ConstantU32(state, component * 4u));
const auto address = component == 0u ? base
: Binary(state, OpIAdd, TypeU32(state), base,
ConstantU32(state, component * 4u));
const auto raw_index =
Binary(state, OpShiftRightLogical, TypeU32(state), address, ConstantU32(state, 2));
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
@ -963,13 +961,12 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
}));
return true;
}
const bool load_address = op == IR::ValueOpcode::LoadAddressU8 ||
op == IR::ValueOpcode::LoadAddressU16 ||
op == IR::ValueOpcode::LoadAddressU32;
const auto address_info = IR::AddressOpcodeInfoOf(op);
const bool load_address = address_info.access == IR::AddressAccess::Read;
const bool load_buffer = op == IR::ValueOpcode::LoadBufferU8 ||
op == IR::ValueOpcode::LoadBufferU16 ||
op == IR::ValueOpcode::LoadBufferU32;
const bool load_shared = IR::SharedAccessOf(op) == IR::SharedAccess::Read;
const bool load_shared = IR::SharedAccessOf(op) == IR::SharedAccess::Read;
if (load_address || load_buffer || load_shared) {
const auto mem = ctx.Memory(inst);
uint32_t value = 0;
@ -986,13 +983,11 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
ctx.Define(inst, value);
return true;
}
const bool store_address = op == IR::ValueOpcode::StoreAddressU8 ||
op == IR::ValueOpcode::StoreAddressU16 ||
op == IR::ValueOpcode::StoreAddressU32;
const bool store_address = address_info.access == IR::AddressAccess::Write;
const bool store_buffer = op == IR::ValueOpcode::StoreBufferU8 ||
op == IR::ValueOpcode::StoreBufferU16 ||
op == IR::ValueOpcode::StoreBufferU32;
const bool store_shared = IR::SharedAccessOf(op) == IR::SharedAccess::Write;
const bool store_shared = IR::SharedAccessOf(op) == IR::SharedAccess::Write;
if (store_address || store_buffer || store_shared) {
const auto mem = ctx.Memory(inst);
if (op == IR::ValueOpcode::StoreBufferU32 && mem.formatted)

View File

@ -7,6 +7,7 @@
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/opcodes/Opcodes.h"
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.h"
#include "graphics/shader/shader.h"
#include <array>
@ -74,6 +75,24 @@ enum class ResourceKind {
Sampler
};
[[nodiscard]] constexpr bool IsAddressResourceKind(ResourceKind kind) {
return kind == ResourceKind::ScalarAddress || kind == ResourceKind::Flat ||
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;
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 {
ResourceKind kind = ResourceKind::None;
uint32_t resource = 0;
@ -309,6 +328,56 @@ enum class DescriptorBindingKind {
Count,
};
[[nodiscard]] inline std::optional<DescriptorBindingKind>
DescriptorBindingForImage(const ImageResource& image) {
using Dimension = Decoder::ImageDimension;
using Kind = DescriptorBindingKind;
switch (image.kind) {
case ResourceKind::Image:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::Sampled1D;
case Dimension::Dim1DArray: return Kind::Sampled1DArray;
case Dimension::Dim2D: return Kind::Sampled2D;
case Dimension::Dim2DArray: return Kind::Sampled2DArray;
case Dimension::Dim2DMsaa: return Kind::Sampled2DMsaa;
case Dimension::Dim2DMsaaArray: return Kind::Sampled2DMsaaArray;
case Dimension::Dim3D: return Kind::Sampled3D;
default: return std::nullopt;
}
case ResourceKind::ImageUint:
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::StorageImage:
switch (image.dimension) {
case Dimension::Dim1D: return Kind::Storage1D;
case Dimension::Dim1DArray: return Kind::Storage1DArray;
case Dimension::Dim2D: return Kind::Storage2D;
case Dimension::Dim2DArray: return Kind::Storage2DArray;
case Dimension::Dim3D: return Kind::Storage3D;
default: return std::nullopt;
}
case ResourceKind::StorageImageUint:
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;
}
default: return std::nullopt;
}
}
struct DescriptorBinding {
DescriptorBindingKind kind = DescriptorBindingKind::Buffers;
uint32_t binding = 0;

View File

@ -24,8 +24,6 @@ std::string TypeName(Type type) {
"U32x3",
"U32x4",
"F32x2",
"F32x3",
"F32x4",
"SrtResource",
"BufferResource",
"AddressResource",

View File

@ -21,14 +21,12 @@ enum class Type : uint32_t {
U32x3 = 1u << 11u,
U32x4 = 1u << 12u,
F32x2 = 1u << 13u,
F32x3 = 1u << 14u,
F32x4 = 1u << 15u,
SrtResource = 1u << 16u,
BufferResource = 1u << 17u,
AddressResource = 1u << 18u,
ImageResource = 1u << 19u,
SamplerResource = 1u << 20u,
ImageAddress = 1u << 21u,
SrtResource = 1u << 14u,
BufferResource = 1u << 15u,
AddressResource = 1u << 16u,
ImageResource = 1u << 17u,
SamplerResource = 1u << 18u,
ImageAddress = 1u << 19u,
};
constexpr Type operator|(Type lhs, Type rhs) {

View File

@ -44,7 +44,8 @@ bool IsRegisterStatePseudo(ValueOpcode opcode) {
}
bool IsRuntimeRead(ValueOpcode opcode) {
return opcode == ValueOpcode::LoadAddressU32 || opcode == ValueOpcode::ReadConstBuffer;
return opcode == ValueOpcode::ReadConstBuffer ||
AddressOpcodeInfoOf(opcode).access == AddressAccess::Read;
}
bool EquivalentValue(const ValueProgram& program, Value left, Value right,
@ -324,31 +325,52 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
ValueOpcodeName(inst.GetOpcode()), inst.NumArgs(),
NumArgsOf(inst.GetOpcode())));
}
if (IsRuntimeRead(inst.GetOpcode())) {
if (inst.GetOpcode() == ValueOpcode::ReadConstBuffer) {
const auto memory_index = inst.Flags<MemoryFlags>().index;
if (memory_index >= program.memory_info.size()) {
return Fail(error, fmt::format("{} has an invalid memory-info index",
ValueOpcodeName(inst.GetOpcode())));
}
const auto& memory = program.memory_info[memory_index];
const bool scalar_buffer = inst.GetOpcode() == ValueOpcode::ReadConstBuffer;
const bool scalar_address = memory.kind == ResourceKind::ScalarAddress;
if ((scalar_buffer && memory.kind != ResourceKind::ScalarBuffer) ||
(!scalar_buffer && !scalar_address && memory.kind != ResourceKind::Flat &&
memory.kind != ResourceKind::Global && memory.kind != ResourceKind::Scratch)) {
const auto& memory = program.memory_info[memory_index];
if (memory.kind != ResourceKind::ScalarBuffer) {
return Fail(error, fmt::format("{} has an invalid scalar-memory resource kind",
ValueOpcodeName(inst.GetOpcode())));
}
const bool scalar_memory = scalar_buffer || scalar_address;
const bool valid_group_width =
scalar_memory
memory.component_count == 1u || memory.component_count == 2u ||
memory.component_count == 4u || memory.component_count == 8u ||
memory.component_count == 16u;
if (memory.data_bits != 32u || memory.data_dwords != 1u || !valid_group_width ||
memory.component_index >= memory.component_count) {
return Fail(error, fmt::format("{} has inconsistent scalar-memory metadata",
ValueOpcodeName(inst.GetOpcode())));
}
}
const auto address_info = AddressOpcodeInfoOf(inst.GetOpcode());
if (address_info.access != AddressAccess::None) {
const auto memory_index = inst.Flags<MemoryFlags>().index;
if (memory_index >= program.memory_info.size()) {
return Fail(error, fmt::format("{} has an invalid memory-info index",
ValueOpcodeName(inst.GetOpcode())));
}
const auto& memory = program.memory_info[memory_index];
if (!IsAddressResourceKind(memory.kind) ||
(memory.kind == ResourceKind::ScalarAddress &&
inst.GetOpcode() != ValueOpcode::LoadAddressU32)) {
return Fail(error, fmt::format("{} has an invalid address resource kind",
ValueOpcodeName(inst.GetOpcode())));
}
const bool scalar_address = memory.kind == ResourceKind::ScalarAddress;
const bool valid_group_width =
scalar_address
? memory.component_count == 1u || memory.component_count == 2u ||
memory.component_count == 4u || memory.component_count == 8u ||
memory.component_count == 16u
: memory.component_count >= 1u && memory.component_count <= 4u;
if (memory.data_bits != 32u || memory.data_dwords != 1u || !valid_group_width ||
memory.component_index >= memory.component_count) {
return Fail(error, fmt::format("{} has inconsistent scalar-memory metadata",
if (memory.data_bits != address_info.data_bits || memory.data_dwords != 1u ||
!valid_group_width || memory.component_index >= memory.component_count ||
memory.sampler != 0u) {
return Fail(error, fmt::format("{} has inconsistent address-memory metadata",
ValueOpcodeName(inst.GetOpcode())));
}
}
@ -407,6 +429,23 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
ValueOpcodeName(inst.GetOpcode())));
}
}
const auto image_info = ImageOpcodeInfoOf(inst.GetOpcode());
if (image_info.access != ImageAccess::None) {
const auto memory_index = inst.Flags<MemoryFlags>().index;
if (memory_index >= program.memory_info.size()) {
return Fail(error, fmt::format("{} has an invalid memory-info index",
ValueOpcodeName(inst.GetOpcode())));
}
const auto& memory = program.memory_info[memory_index];
if (!ImageResourceKindMatches(memory.kind, image_info.resource_class)) {
return Fail(error, fmt::format("{} has invalid image-memory metadata",
ValueOpcodeName(inst.GetOpcode())));
}
}
if (inst.GetOpcode() == ValueOpcode::SetAttribute &&
inst.Flags<ExportFlags>().index >= program.export_info.size()) {
return Fail(error, "SetAttribute has an invalid export-info index");
}
uint32_t composite_components = 0u;
switch (inst.GetOpcode()) {
case ValueOpcode::CompositeExtractU64: composite_components = 2u; break;

View File

@ -79,19 +79,16 @@ bool HasSideEffects(ValueOpcode opcode) {
shared_access == SharedAccess::Append || shared_access == SharedAccess::Consume) {
return true;
}
if (AddressOpcodeInfoOf(opcode).access == AddressAccess::Write) {
return true;
}
const auto image_info = ImageOpcodeInfoOf(opcode);
if (image_info.access == ImageAccess::Write || image_info.access == ImageAccess::Atomic) {
return true;
}
switch (opcode) {
case ValueOpcode::Reference:
case ValueOpcode::ReferenceU32:
case ValueOpcode::StoreAddressU8:
case ValueOpcode::StoreAddressU16:
case ValueOpcode::StoreAddressU32:
case ValueOpcode::ImageAtomicIAdd32:
case ValueOpcode::ImageAtomicUMin32:
case ValueOpcode::ImageAtomicUMax32:
case ValueOpcode::ImageAtomicAnd32:
case ValueOpcode::ImageAtomicOr32:
case ValueOpcode::ImageAtomicXor32:
case ValueOpcode::ImageWrite:
case ValueOpcode::SetAttribute:
case ValueOpcode::Barrier: return true;
default: return false;
@ -185,4 +182,37 @@ uint32_t SharedComponentCount(ValueOpcode opcode) {
}
}
AddressOpcodeInfo AddressOpcodeInfoOf(ValueOpcode opcode) {
switch (opcode) {
case ValueOpcode::LoadAddressU8: return {AddressAccess::Read, 8u};
case ValueOpcode::LoadAddressU16: return {AddressAccess::Read, 16u};
case ValueOpcode::LoadAddressU32: return {AddressAccess::Read, 32u};
case ValueOpcode::StoreAddressU8: return {AddressAccess::Write, 8u};
case ValueOpcode::StoreAddressU16: return {AddressAccess::Write, 16u};
case ValueOpcode::StoreAddressU32: return {AddressAccess::Write, 32u};
default: return {};
}
}
ImageOpcodeInfo ImageOpcodeInfoOf(ValueOpcode opcode) {
switch (opcode) {
case ValueOpcode::ImageQueryDimensions:
case ValueOpcode::ImageRead: return {ImageAccess::Read, ImageResourceClass::Sampled, false};
case ValueOpcode::ImageQueryLod:
case ValueOpcode::ImageSampleRaw:
case ValueOpcode::ImageGatherRaw:
return {ImageAccess::Read, ImageResourceClass::Sampled, true};
case ValueOpcode::ImageWrite:
return {ImageAccess::Write, ImageResourceClass::Storage, false};
case ValueOpcode::ImageAtomicIAdd32:
case ValueOpcode::ImageAtomicUMin32:
case ValueOpcode::ImageAtomicUMax32:
case ValueOpcode::ImageAtomicAnd32:
case ValueOpcode::ImageAtomicOr32:
case ValueOpcode::ImageAtomicXor32:
return {ImageAccess::Atomic, ImageResourceClass::StorageUint, false};
default: return {};
}
}
} // namespace Libs::Graphics::ShaderRecompiler::IR

View File

@ -18,6 +18,20 @@ enum class ValueOpcode {
enum class BufferAccess { None, Read, Write, Atomic };
enum class SharedAccess { None, Read, Write, Atomic, Append, Consume };
enum class AddressAccess { None, Read, Write };
enum class ImageAccess { None, Read, Write, Atomic };
enum class ImageResourceClass { None, Sampled, Storage, StorageUint };
struct AddressOpcodeInfo {
AddressAccess access = AddressAccess::None;
uint32_t data_bits = 0;
};
struct ImageOpcodeInfo {
ImageAccess access = ImageAccess::None;
ImageResourceClass resource_class = ImageResourceClass::None;
bool needs_sampler = false;
};
struct DppMoveFlags {
uint16_t control = 0;
@ -51,14 +65,16 @@ struct ExportFlags {
static_assert(sizeof(ExportFlags) == sizeof(uint64_t));
static_assert(std::is_trivially_copyable_v<ExportFlags>);
[[nodiscard]] Type TypeOf(ValueOpcode opcode);
[[nodiscard]] Type ArgTypeOf(ValueOpcode opcode, size_t index);
[[nodiscard]] size_t NumArgsOf(ValueOpcode opcode);
[[nodiscard]] bool HasSideEffects(ValueOpcode opcode);
[[nodiscard]] BufferAccess BufferAccessOf(ValueOpcode opcode);
[[nodiscard]] uint32_t BufferComponentCount(ValueOpcode opcode);
[[nodiscard]] SharedAccess SharedAccessOf(ValueOpcode opcode);
[[nodiscard]] uint32_t SharedComponentCount(ValueOpcode opcode);
[[nodiscard]] std::string_view ValueOpcodeName(ValueOpcode opcode);
[[nodiscard]] Type TypeOf(ValueOpcode opcode);
[[nodiscard]] Type ArgTypeOf(ValueOpcode opcode, size_t index);
[[nodiscard]] size_t NumArgsOf(ValueOpcode opcode);
[[nodiscard]] bool HasSideEffects(ValueOpcode opcode);
[[nodiscard]] BufferAccess BufferAccessOf(ValueOpcode opcode);
[[nodiscard]] uint32_t BufferComponentCount(ValueOpcode opcode);
[[nodiscard]] SharedAccess SharedAccessOf(ValueOpcode opcode);
[[nodiscard]] uint32_t SharedComponentCount(ValueOpcode opcode);
[[nodiscard]] AddressOpcodeInfo AddressOpcodeInfoOf(ValueOpcode opcode);
[[nodiscard]] ImageOpcodeInfo ImageOpcodeInfoOf(ValueOpcode opcode);
[[nodiscard]] std::string_view ValueOpcodeName(ValueOpcode opcode);
} // namespace Libs::Graphics::ShaderRecompiler::IR

View File

@ -2,7 +2,6 @@
VALUE_OPCODE(Phi, Opaque)
VALUE_OPCODE(Identity, Opaque, Opaque)
VALUE_OPCODE(Void, Void)
VALUE_OPCODE(ConditionRef, U1, U1)
VALUE_OPCODE(Reference, Void, U1)
VALUE_OPCODE(ReferenceU32, Void, U32)
@ -112,7 +111,6 @@ VALUE_OPCODE(BitCount32, U32, U32)
VALUE_OPCODE(BitCount64, U32, U64)
VALUE_OPCODE(FindUMsb32, U32, U32)
VALUE_OPCODE(FindUMsb64, U32, U64)
VALUE_OPCODE(FindSMsb32, U32, U32)
VALUE_OPCODE(FindILsb32, U32, U32)
VALUE_OPCODE(SMin32, U32, U32, U32)
VALUE_OPCODE(UMin32, U32, U32, U32)
@ -131,19 +129,14 @@ VALUE_OPCODE(ULessThan64, U1, U64, U64)
VALUE_OPCODE(IEqual32, U1, U32, U32)
VALUE_OPCODE(IEqual64, U1, U64, U64)
VALUE_OPCODE(SLessThanEqual32, U1, U32, U32)
VALUE_OPCODE(SLessThanEqual64, U1, U64, U64)
VALUE_OPCODE(ULessThanEqual32, U1, U32, U32)
VALUE_OPCODE(ULessThanEqual64, U1, U64, U64)
VALUE_OPCODE(SGreaterThan32, U1, U32, U32)
VALUE_OPCODE(SGreaterThan64, U1, U64, U64)
VALUE_OPCODE(UGreaterThan32, U1, U32, U32)
VALUE_OPCODE(UGreaterThan64, U1, U64, U64)
VALUE_OPCODE(INotEqual32, U1, U32, U32)
VALUE_OPCODE(INotEqual64, U1, U64, U64)
VALUE_OPCODE(SGreaterThanEqual32, U1, U32, U32)
VALUE_OPCODE(SGreaterThanEqual64, U1, U64, U64)
VALUE_OPCODE(UGreaterThanEqual32, U1, U32, U32)
VALUE_OPCODE(UGreaterThanEqual64, U1, U64, U64)
VALUE_OPCODE(LogicalOr, U1, U1, U1)
VALUE_OPCODE(LogicalAnd, U1, U1, U1)
VALUE_OPCODE(LogicalXor, U1, U1, U1)

View File

@ -39,55 +39,6 @@ constexpr std::array ImageBindingKinds = {
DescriptorBindingKind::StorageUint3D,
};
bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) {
using Dimension = Decoder::ImageDimension;
using Kind = DescriptorBindingKind;
switch (image.kind) {
case ResourceKind::Image:
switch (image.dimension) {
case Dimension::Dim1D: result = Kind::Sampled1D; return true;
case Dimension::Dim1DArray: result = Kind::Sampled1DArray; return true;
case Dimension::Dim2D: result = Kind::Sampled2D; return true;
case Dimension::Dim2DArray: result = Kind::Sampled2DArray; return true;
case Dimension::Dim2DMsaa: result = Kind::Sampled2DMsaa; return true;
case Dimension::Dim2DMsaaArray: result = Kind::Sampled2DMsaaArray; return true;
case Dimension::Dim3D: result = Kind::Sampled3D; return true;
default: return false;
}
case ResourceKind::ImageUint:
switch (image.dimension) {
case Dimension::Dim1D: result = Kind::SampledUint1D; return true;
case Dimension::Dim1DArray: result = Kind::SampledUint1DArray; return true;
case Dimension::Dim2D: result = Kind::SampledUint2D; return true;
case Dimension::Dim2DArray: result = Kind::SampledUint2DArray; return true;
case Dimension::Dim2DMsaa: result = Kind::SampledUint2DMsaa; return true;
case Dimension::Dim2DMsaaArray: result = Kind::SampledUint2DMsaaArray; return true;
case Dimension::Dim3D: result = Kind::SampledUint3D; return true;
default: return false;
}
case ResourceKind::StorageImage:
switch (image.dimension) {
case Dimension::Dim1D: result = Kind::Storage1D; return true;
case Dimension::Dim1DArray: result = Kind::Storage1DArray; return true;
case Dimension::Dim2D: result = Kind::Storage2D; return true;
case Dimension::Dim2DArray: result = Kind::Storage2DArray; return true;
case Dimension::Dim3D: result = Kind::Storage3D; return true;
default: return false;
}
case ResourceKind::StorageImageUint:
switch (image.dimension) {
case Dimension::Dim1D: result = Kind::StorageUint1D; return true;
case Dimension::Dim1DArray: result = Kind::StorageUint1DArray; return true;
case Dimension::Dim2D: result = Kind::StorageUint2D; return true;
case Dimension::Dim2DArray: result = Kind::StorageUint2DArray; return true;
case Dimension::Dim3D: result = Kind::StorageUint3D; return true;
default: return false;
}
default: return false;
}
}
bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
std::set<uint32_t> registers;
for (const auto* block: program.values->blocks) {
@ -187,14 +138,14 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
std::array<std::vector<uint32_t>, ImageBindingKinds.size()> image_groups;
for (uint32_t i = 0; i < program.info.images.size(); i++) {
DescriptorBindingKind kind;
if (!ImageBinding(program.info.images[i], kind)) {
const auto kind = DescriptorBindingForImage(program.info.images[i]);
if (!kind.has_value()) {
if (error != nullptr) {
*error = "shader info contains an invalid image binding class";
}
return false;
}
const auto group = std::find(ImageBindingKinds.begin(), ImageBindingKinds.end(), kind);
const auto group = std::find(ImageBindingKinds.begin(), ImageBindingKinds.end(), *kind);
if (group == ImageBindingKinds.end()) {
if (error != nullptr) {
*error = "shader info contains an unmapped image binding class";

View File

@ -525,28 +525,12 @@ void FoldInstruction(Inst& inst) {
case ValueOpcode::ULessThan64:
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a < b; });
return;
case ValueOpcode::ULessThanEqual64:
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a <= b; });
return;
case ValueOpcode::UGreaterThan64:
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a > b; });
return;
case ValueOpcode::UGreaterThanEqual64:
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a >= b; });
return;
case ValueOpcode::SLessThan64:
case ValueOpcode::SLessThanEqual64:
case ValueOpcode::SGreaterThan64:
case ValueOpcode::SGreaterThanEqual64:
FoldU64Compare(inst, [opcode = inst.GetOpcode()](uint64_t a, uint64_t b) {
const auto lhs = std::bit_cast<int64_t>(a);
const auto rhs = std::bit_cast<int64_t>(b);
switch (opcode) {
case ValueOpcode::SLessThan64: return lhs < rhs;
case ValueOpcode::SLessThanEqual64: return lhs <= rhs;
case ValueOpcode::SGreaterThan64: return lhs > rhs;
default: return lhs >= rhs;
}
FoldU64Compare(inst, [](uint64_t a, uint64_t b) {
return std::bit_cast<int64_t>(a) < std::bit_cast<int64_t>(b);
});
return;
case ValueOpcode::LogicalAnd:

View File

@ -264,24 +264,6 @@ uint64_t AddressSpecialization(const AddressResource& resource,
: snapshot.guest_base - snapshot.binding_base;
}
bool IsImageOp(ValueOpcode op) {
switch (op) {
case ValueOpcode::ImageQueryDimensions:
case ValueOpcode::ImageQueryLod:
case ValueOpcode::ImageRead:
case ValueOpcode::ImageWrite:
case ValueOpcode::ImageSampleRaw:
case ValueOpcode::ImageGatherRaw:
case ValueOpcode::ImageAtomicIAdd32:
case ValueOpcode::ImageAtomicUMin32:
case ValueOpcode::ImageAtomicUMax32:
case ValueOpcode::ImageAtomicAnd32:
case ValueOpcode::ImageAtomicOr32:
case ValueOpcode::ImageAtomicXor32: return true;
default: return false;
}
}
size_t FlattenedRuntimeDwords(const Program& program) {
size_t size = program.values != nullptr ? program.values->srt_reads.size() : 0u;
for (uint32_t resource = 0; resource < program.info.images.size(); resource++) {
@ -915,7 +897,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
auto memory_info = program.values->memory_info;
for (const auto* block: program.values->blocks) {
for (const auto& inst: *block) {
if (!IsImageOp(inst.GetOpcode())) {
if (ImageOpcodeInfoOf(inst.GetOpcode()).access == ImageAccess::None) {
continue;
}
const auto index = inst.Flags<MemoryFlags>().index;

View File

@ -25,60 +25,6 @@ const char* StageName(ShaderType stage) {
}
}
bool IsAddressStore(ValueOpcode op) {
switch (op) {
case ValueOpcode::StoreAddressU8:
case ValueOpcode::StoreAddressU16:
case ValueOpcode::StoreAddressU32: return true;
default: return false;
}
}
bool IsAddress(ValueOpcode op) {
switch (op) {
case ValueOpcode::LoadAddressU8:
case ValueOpcode::LoadAddressU16:
case ValueOpcode::LoadAddressU32:
case ValueOpcode::StoreAddressU8:
case ValueOpcode::StoreAddressU16:
case ValueOpcode::StoreAddressU32: return true;
default: return false;
}
}
bool IsImageAtomic(ValueOpcode op) {
switch (op) {
case ValueOpcode::ImageAtomicIAdd32:
case ValueOpcode::ImageAtomicUMin32:
case ValueOpcode::ImageAtomicUMax32:
case ValueOpcode::ImageAtomicAnd32:
case ValueOpcode::ImageAtomicOr32:
case ValueOpcode::ImageAtomicXor32: return true;
default: return false;
}
}
bool IsImageStore(ValueOpcode op) {
return op == ValueOpcode::ImageWrite || IsImageAtomic(op);
}
bool IsImage(ValueOpcode op) {
switch (op) {
case ValueOpcode::ImageQueryDimensions:
case ValueOpcode::ImageQueryLod:
case ValueOpcode::ImageRead:
case ValueOpcode::ImageWrite:
case ValueOpcode::ImageSampleRaw:
case ValueOpcode::ImageGatherRaw: return true;
default: return IsImageAtomic(op);
}
}
bool NeedsSampler(ValueOpcode op) {
return op == ValueOpcode::ImageQueryLod || op == ValueOpcode::ImageSampleRaw ||
op == ValueOpcode::ImageGatherRaw;
}
uint32_t ByteExtent(const MemoryInfo& memory) {
const auto bytes = std::max((memory.data_bits + 7u) / 8u, 1u);
const auto count = std::max(memory.data_dwords, 1u);
@ -353,7 +299,9 @@ private:
for (const auto* block: m_values.blocks) {
for (const auto& inst: *block) {
const auto op = inst.GetOpcode();
if ((BufferAccessOf(op) == BufferAccess::None && !IsAddress(op) && !IsImage(op)) ||
if ((BufferAccessOf(op) == BufferAccess::None &&
AddressOpcodeInfoOf(op).access == AddressAccess::None &&
ImageOpcodeInfoOf(op).access == ImageAccess::None) ||
&inst == &owner) {
continue;
}
@ -534,7 +482,8 @@ private:
void PlanIndirectImages() {
for (auto* block: m_values.blocks) {
for (auto& inst: *block) {
if (!IsImage(inst.GetOpcode()) || inst.NumArgs() == 0u) {
if (ImageOpcodeInfoOf(inst.GetOpcode()).access == ImageAccess::None ||
inst.NumArgs() == 0u) {
continue;
}
auto* handle = inst.Arg(0).Resolve().TryInstruction();
@ -671,8 +620,9 @@ private:
}
static void Merge(ImageResource& image, ValueOpcode op, uint32_t pc) {
const bool atomic = IsImageAtomic(op);
const bool write = IsImageStore(op);
const auto access = ImageOpcodeInfoOf(op).access;
const bool atomic = access == ImageAccess::Atomic;
const bool write = access == ImageAccess::Write || atomic;
image.first_use_pc = std::min(image.first_use_pc, pc);
image.read = image.read || !write || atomic;
image.written = image.written || write;
@ -693,8 +643,8 @@ private:
return static_cast<uint32_t>(m_info.samplers.size() - 1);
}
uint32_t AddAddress(uint32_t source, bool unbased, const MemoryInfo& memory, ValueOpcode op,
uint32_t pc) {
uint32_t AddAddress(uint32_t source, bool unbased, const MemoryInfo& memory,
AddressAccess access, uint32_t pc) {
auto immediate = static_cast<int32_t>(memory.offset);
if (memory.kind == ResourceKind::ScalarAddress) {
immediate = static_cast<int32_t>(static_cast<uint32_t>(immediate) & ~3u);
@ -706,8 +656,8 @@ private:
address.kind == memory.kind) {
address.first_use_pc = std::min(address.first_use_pc, pc);
address.min_offset = std::min(address.min_offset, min_offset);
address.read = address.read || !IsAddressStore(op);
address.written = address.written || IsAddressStore(op);
address.read = address.read || access == AddressAccess::Read;
address.written = address.written || access == AddressAccess::Write;
return i;
}
}
@ -720,8 +670,8 @@ private:
address.kind = memory.kind;
address.min_offset = min_offset;
address.unbased = unbased;
address.read = !IsAddressStore(op);
address.written = IsAddressStore(op);
address.read = access == AddressAccess::Read;
address.written = access == AddressAccess::Write;
m_info.addresses.push_back(address);
return static_cast<uint32_t>(m_info.addresses.size() - 1);
}
@ -774,8 +724,12 @@ private:
}
bool Collect(Inst& inst, std::string* error) {
const auto op = inst.GetOpcode();
if (BufferAccessOf(op) == BufferAccess::None && !IsAddress(op) && !IsImage(op)) {
const auto op = inst.GetOpcode();
const auto buffer = BufferAccessOf(op);
const auto address_info = AddressOpcodeInfoOf(op);
const auto image_info = ImageOpcodeInfoOf(op);
if (buffer == BufferAccess::None && address_info.access == AddressAccess::None &&
image_info.access == ImageAccess::None) {
return true;
}
const auto flags = inst.Flags<MemoryFlags>();
@ -794,7 +748,7 @@ private:
uint32_t source = 0;
uint32_t resource = 0;
if (BufferAccessOf(op) != BufferAccess::None) {
if (buffer != BufferAccess::None) {
if (!GetHandle(inst.Arg(0), ValueOpcode::GetBufferResource, 4, flags.pc, handle, source,
error)) {
return false;
@ -806,12 +760,15 @@ private:
return AddHandlePatch(handle, resource, flags.pc, error) &&
AddMemoryPatch(flags.index, resource, 0, false, flags.pc, error);
}
if (IsAddress(op)) {
if (address_info.access != AddressAccess::None) {
if (!IsAddressResourceKind(memory.kind)) {
return Fail(flags.pc, error, "address operation has invalid resource kind");
}
bool unbased = false;
if (!GetAddressHandle(inst, inst.Arg(0), flags.pc, handle, source, unbased, error)) {
return false;
}
resource = AddAddress(source, unbased, memory, op, flags.pc);
resource = AddAddress(source, unbased, memory, address_info.access, flags.pc);
if (resource == UINT32_MAX) {
return Fail(flags.pc, error, "address resource limit exceeded");
}
@ -819,6 +776,9 @@ private:
AddMemoryPatch(flags.index, resource, 0, false, flags.pc, error);
}
if (!ImageResourceKindMatches(memory.kind, image_info.resource_class)) {
return Fail(flags.pc, error, "image operation has invalid resource kind");
}
handle = inst.Arg(0).Resolve().TryInstruction();
const auto* indirect = handle != nullptr ? FindIndirectImage(*handle) : nullptr;
if (indirect != nullptr) {
@ -835,7 +795,7 @@ private:
return false;
}
uint32_t sampler = 0;
if (NeedsSampler(op)) {
if (image_info.needs_sampler) {
if (inst.NumArgs() < 2) {
return Fail(flags.pc, error, "sampled image operation has no sampler handle");
}
@ -854,7 +814,8 @@ private:
return false;
}
}
return AddMemoryPatch(flags.index, resource, sampler, NeedsSampler(op), flags.pc, error);
return AddMemoryPatch(flags.index, resource, sampler, image_info.needs_sampler, flags.pc,
error);
}
const DescriptorSource* Source(uint32_t source) const {

View File

@ -597,7 +597,7 @@ void TestImagesSamplersAndAliases() {
const auto storage = fixture.Image(image_words, 16);
MemoryInfo storage_memory;
storage_memory.kind = ResourceKind::StorageImage;
storage_memory.kind = ResourceKind::StorageImageUint;
storage_memory.image_dimension = Decoder::ImageDimension::Dim2D;
fixture.Emit(ValueOpcode::ImageAtomicIAdd32,
{storage, image_address, Value(1u), Value(true)},
@ -1146,6 +1146,79 @@ void TestResourceLimitIsTransactional() {
"resource-limit failure partially mutated typed resource state");
}
void TestMalformedMemoryKindsRejected() {
{
Fixture fixture;
const auto address = fixture.Address(Value(0u), Value(0u), 4);
MemoryInfo memory;
memory.kind = ResourceKind::Buffer;
fixture.Emit(ValueOpcode::StoreAddressU32,
{address, Value(0u), Value(0u), Value(1u), Value(true)},
fixture.AddMemory(memory, 4));
std::string error;
Check(BuildSrtPlan(fixture.program, &error) &&
!TrackResources(fixture.program, &error) &&
error.find("address operation has invalid resource kind") !=
std::string::npos,
"resource tracking accepted an address opcode with buffer metadata");
}
{
Fixture fixture;
const auto image =
fixture.Image({Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
Value(0u), Value(0u), Value(0u)},
8);
MemoryInfo memory;
memory.kind = ResourceKind::Flat;
fixture.Emit(ValueOpcode::ImageRead,
{image, fixture.ImageAddress(), Value(true)},
fixture.AddMemory(memory, 8));
std::string error;
Check(BuildSrtPlan(fixture.program, &error) &&
!TrackResources(fixture.program, &error) &&
error.find("image operation has invalid resource kind") !=
std::string::npos,
"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));
std::string error;
Check(BuildSrtPlan(fixture.program, &error) &&
!TrackResources(fixture.program, &error) &&
error.find("image operation has invalid resource kind") !=
std::string::npos,
"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));
std::string error;
Check(
BuildSrtPlan(fixture.program, &error) &&
!TrackResources(fixture.program, &error) &&
error.find("image operation has invalid resource kind") !=
std::string::npos,
"resource tracking accepted a uint atomic with float storage metadata");
}
}
} // namespace
int main() {
@ -1173,6 +1246,7 @@ int main() {
Run("buffer swizzle specialization", TestBufferSwizzleSpecialization);
Run("shader info and bindings", TestShaderInfoAndBindingLayout);
Run("resource limit", TestResourceLimitIsTransactional);
Run("malformed memory kinds", TestMalformedMemoryKindsRejected);
} catch (const std::exception &exception) {
std::cerr << "resource tracking test failed: " << exception.what() << '\n';
return 1;

View File

@ -9118,7 +9118,7 @@ void TestNativeWideValueValidation() {
memory.data_bits = 16;
program.memory_info.push_back(memory);
append_scalar_read(program, ValueOpcode::LoadAddressU32, {});
check_rejected(program, "inconsistent scalar-memory metadata");
check_rejected(program, "inconsistent address-memory metadata");
}
{
auto program = make_program();
@ -9146,7 +9146,7 @@ void TestNativeWideValueValidation() {
memory.component_index = 4;
program.memory_info.push_back(memory);
append_scalar_read(program, ValueOpcode::LoadAddressU32, {});
check_rejected(program, "inconsistent scalar-memory metadata");
check_rejected(program, "inconsistent address-memory metadata");
}
{
@ -9248,6 +9248,131 @@ void TestNativeWideValueValidation() {
append_shared(program, ValueOpcode::LoadSharedU32x3, {});
check_rejected(program, "inconsistent shared-memory width");
}
const auto append_address = [](ValueProgram &program, ValueOpcode opcode,
MemoryFlags flags) {
IREmitter ir(program.blocks.front());
const auto resource =
ir.Emit(ValueOpcode::GetAddressResource, {Value(0u), Value(0u)});
if (opcode == ValueOpcode::StoreAddressU32) {
ir.Emit(opcode, {resource, Value(0u), Value(0u), Value(0u), Value(true)},
flags);
} else {
ir.Emit(opcode, {resource, Value(0u), Value(0u), Value(true)}, flags);
}
};
{
auto program = make_program();
append_address(program, ValueOpcode::StoreAddressU32,
MemoryFlags{.index = 1});
check_rejected(program, "invalid memory-info index");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::Buffer;
program.memory_info.push_back(memory);
append_address(program, ValueOpcode::StoreAddressU32, {});
check_rejected(program, "invalid address resource kind");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::Global;
program.memory_info.push_back(memory);
append_address(program, ValueOpcode::LoadAddressU8, {});
check_rejected(program, "inconsistent address-memory metadata");
}
const auto append_image = [](ValueProgram &program, ValueOpcode opcode,
MemoryFlags flags) {
IREmitter ir(program.blocks.front());
const auto image = ir.Emit(ValueOpcode::GetImageResource,
{Value(0u), Value(0u), Value(0u), Value(0u),
Value(0u), Value(0u), Value(0u), Value(0u)});
const auto address =
ir.Emit(ValueOpcode::MakeImageAddress,
{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)});
const auto image_info = ImageOpcodeInfoOf(opcode);
if (opcode == ValueOpcode::ImageWrite) {
const auto data = ir.Emit(ValueOpcode::CompositeConstructU32x4,
{Value(0u), Value(0u), Value(0u), Value(0u)});
ir.Emit(opcode, {image, address, data, Value(true)}, flags);
} else if (image_info.access == ImageAccess::Atomic) {
ir.Emit(opcode, {image, address, Value(0u), Value(true)}, flags);
} else if (image_info.needs_sampler) {
const auto sampler =
ir.Emit(ValueOpcode::GetSamplerResource,
{Value(0u), Value(0u), Value(0u), Value(0u)});
ir.Emit(opcode, {image, sampler, address}, flags);
} else if (opcode == ValueOpcode::ImageQueryDimensions) {
ir.Emit(opcode, {image, address}, flags);
} else {
ir.Emit(opcode, {image, address, Value(true)}, flags);
}
};
{
auto program = make_program();
append_image(program, ValueOpcode::ImageRead, MemoryFlags{.index = 1});
check_rejected(program, "invalid memory-info index");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::Buffer;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageRead, {});
check_rejected(program, "invalid image-memory metadata");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageRead, {});
check_rejected(program, "invalid image-memory metadata");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImageUint;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageQueryDimensions, {});
check_rejected(program, "invalid image-memory metadata");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::Image;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageWrite, {});
check_rejected(program, "invalid image-memory metadata");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageSampleRaw, {});
check_rejected(program, "invalid image-memory metadata");
}
{
auto program = make_program();
MemoryInfo memory;
memory.kind = ResourceKind::StorageImage;
program.memory_info.push_back(memory);
append_image(program, ValueOpcode::ImageAtomicIAdd32, {});
check_rejected(program, "invalid image-memory metadata");
}
{
auto program = make_program();
IREmitter ir(program.blocks.front());
const auto data = ir.Emit(ValueOpcode::CompositeConstructU32x4,
{Value(0u), Value(0u), Value(0u), Value(0u)});
ir.Emit(ValueOpcode::SetAttribute, {data, Value(true)},
ExportFlags{.index = 1});
check_rejected(program, "invalid export-info index");
}
}
void TestNewShaderRecompilerZeroInitialRegisterState() {