mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 08:13:05 +00:00
graphics: separate scalar address bindings
This commit is contained in:
1 parent
83ee040b4d
commit
443bbcbf90
12 files changed
+229
-32
No files matched your search
@@ -302,9 +302,9 @@ uint32_t EmitBufferByteAddress(EmitterState& state, const IR::Instruction& inst,
|
|||||||
return EmitBufferAddressFromParts(state, inst, index, offset, soffset);
|
return EmitBufferAddressFromParts(state, inst, index, offset, soffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsFlatMemoryKind(IR::ResourceKind kind) {
|
bool IsAddressMemoryKind(IR::ResourceKind kind) {
|
||||||
return kind == IR::ResourceKind::Flat || kind == IR::ResourceKind::Global ||
|
return kind == IR::ResourceKind::ScalarAddress || kind == IR::ResourceKind::Flat ||
|
||||||
kind == IR::ResourceKind::Scratch;
|
kind == IR::ResourceKind::Global || kind == IR::ResourceKind::Scratch;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EmitRelativeAddress(EmitterState& state, const IR::Instruction& inst, uint32_t first_src,
|
uint32_t EmitRelativeAddress(EmitterState& state, const IR::Instruction& inst, uint32_t first_src,
|
||||||
@@ -423,7 +423,7 @@ StorageBufferBindingForMemory(EmitterState& state, const IR::MemoryInfo& mem, ui
|
|||||||
|
|
||||||
uint32_t EmitStorageBufferIndex(EmitterState& state, const IR::MemoryInfo& mem, uint32_t index,
|
uint32_t EmitStorageBufferIndex(EmitterState& state, const IR::MemoryInfo& mem, uint32_t index,
|
||||||
uint32_t use_pc) {
|
uint32_t use_pc) {
|
||||||
if (IsFlatMemoryKind(mem.kind)) {
|
if (IsAddressMemoryKind(mem.kind)) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
const auto binding = StorageBufferBindingForMemory(state, mem, use_pc);
|
const auto binding = StorageBufferBindingForMemory(state, mem, use_pc);
|
||||||
@@ -432,7 +432,7 @@ uint32_t EmitStorageBufferIndex(EmitterState& state, const IR::MemoryInfo& mem,
|
|||||||
|
|
||||||
uint32_t EmitStorageBufferObjectPointer(EmitterState& state, const IR::MemoryInfo& mem,
|
uint32_t EmitStorageBufferObjectPointer(EmitterState& state, const IR::MemoryInfo& mem,
|
||||||
uint32_t use_pc) {
|
uint32_t use_pc) {
|
||||||
if (IsFlatMemoryKind(mem.kind)) {
|
if (IsAddressMemoryKind(mem.kind)) {
|
||||||
if (state.address_memory_variable == 0) {
|
if (state.address_memory_variable == 0) {
|
||||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
||||||
mem.resource, "address memory binding was not emitted");
|
mem.resource, "address memory binding was not emitted");
|
||||||
@@ -467,7 +467,7 @@ uint32_t EmitStorageBufferElementInBounds(EmitterState& state, const IR::MemoryI
|
|||||||
uint32_t EmitStorageBufferElementPointer(EmitterState& state, const IR::MemoryInfo& mem,
|
uint32_t EmitStorageBufferElementPointer(EmitterState& state, const IR::MemoryInfo& mem,
|
||||||
uint32_t index, uint32_t use_pc) {
|
uint32_t index, uint32_t use_pc) {
|
||||||
index = EmitStorageBufferIndex(state, mem, index, use_pc);
|
index = EmitStorageBufferIndex(state, mem, index, use_pc);
|
||||||
if (IsFlatMemoryKind(mem.kind)) {
|
if (IsAddressMemoryKind(mem.kind)) {
|
||||||
if (state.address_memory_variable == 0) {
|
if (state.address_memory_variable == 0) {
|
||||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
||||||
mem.resource, "address memory binding was not emitted");
|
mem.resource, "address memory binding was not emitted");
|
||||||
|
|||||||
@@ -102,8 +102,12 @@ uint32_t AddU64Low(EmitterState& state, uint32_t low, uint32_t high, uint32_t ad
|
|||||||
|
|
||||||
uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||||
uint32_t low, uint32_t high) {
|
uint32_t low, uint32_t high) {
|
||||||
auto& state = ctx.state;
|
auto& state = ctx.state;
|
||||||
const auto immediate = static_cast<int32_t>(mem.offset);
|
auto immediate = static_cast<int32_t>(mem.offset);
|
||||||
|
if (mem.kind == IR::ResourceKind::ScalarAddress) {
|
||||||
|
immediate = static_cast<int32_t>(static_cast<uint32_t>(immediate) & ~3u);
|
||||||
|
low = Binary(state, OpBitwiseAnd, state.uint_type, low, ConstantU32(state, ~3u));
|
||||||
|
}
|
||||||
const auto immediate_low = ConstantU32(state, static_cast<uint32_t>(immediate));
|
const auto immediate_low = ConstantU32(state, static_cast<uint32_t>(immediate));
|
||||||
const auto immediate_high = ConstantU32(state, immediate < 0 ? UINT32_MAX : 0u);
|
const auto immediate_high = ConstantU32(state, immediate < 0 ? UINT32_MAX : 0u);
|
||||||
const auto base = state.program.info.addresses[mem.resource].specialized_base;
|
const auto base = state.program.info.addresses[mem.resource].specialized_base;
|
||||||
@@ -137,11 +141,7 @@ uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const I
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
uint32_t ByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
||||||
if (mem.kind == IR::ResourceKind::Buffer || mem.kind == IR::ResourceKind::ScalarBuffer) {
|
if (mem.kind == IR::ResourceKind::Buffer) {
|
||||||
if (mem.kind == IR::ResourceKind::ScalarBuffer) {
|
|
||||||
return Binary(ctx.state, OpIAdd, ctx.state.uint_type, ctx.Arg(inst, 1),
|
|
||||||
ConstantU32(ctx.state, mem.offset));
|
|
||||||
}
|
|
||||||
return BufferByteAddress(ctx, inst, mem, ctx.Arg(inst, 1), ctx.Arg(inst, 2),
|
return BufferByteAddress(ctx, inst, mem, ctx.Arg(inst, 1), ctx.Arg(inst, 2),
|
||||||
ctx.Arg(inst, 3));
|
ctx.Arg(inst, 3));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ struct Operand {
|
|||||||
enum class ResourceKind {
|
enum class ResourceKind {
|
||||||
None,
|
None,
|
||||||
ScalarBuffer,
|
ScalarBuffer,
|
||||||
|
ScalarAddress,
|
||||||
Buffer,
|
Buffer,
|
||||||
Flat,
|
Flat,
|
||||||
Global,
|
Global,
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ std::string OperandToString(const Operand& operand) {
|
|||||||
std::string ResourceKindToString(ResourceKind kind) {
|
std::string ResourceKindToString(ResourceKind kind) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ResourceKind::ScalarBuffer: return "scalar_buffer";
|
case ResourceKind::ScalarBuffer: return "scalar_buffer";
|
||||||
|
case ResourceKind::ScalarAddress: return "scalar_address";
|
||||||
case ResourceKind::Buffer: return "buffer";
|
case ResourceKind::Buffer: return "buffer";
|
||||||
case ResourceKind::Flat: return "flat";
|
case ResourceKind::Flat: return "flat";
|
||||||
case ResourceKind::Global: return "global";
|
case ResourceKind::Global: return "global";
|
||||||
|
|||||||
@@ -75,14 +75,16 @@ Opcode LoweredImageOpcode(Decoder::Opcode opcode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LowerScalarBufferLoadDword(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
|
bool LowerScalarMemoryLoadDword(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
|
||||||
std::string* error) {
|
std::string* error) {
|
||||||
for (uint32_t i = 0; i < decoded.data_dwords; i++) {
|
for (uint32_t i = 0; i < decoded.data_dwords; i++) {
|
||||||
Instruction inst;
|
Instruction inst;
|
||||||
inst.pc = decoded.pc;
|
inst.pc = decoded.pc;
|
||||||
inst.op = op;
|
inst.op = op;
|
||||||
inst.src_count = 1;
|
inst.src_count = 1;
|
||||||
inst.memory = OffsetMemoryInfo(decoded, ResourceKind::ScalarBuffer, i);
|
const auto kind =
|
||||||
|
op == Opcode::SLoadDword ? ResourceKind::ScalarAddress : ResourceKind::ScalarBuffer;
|
||||||
|
inst.memory = OffsetMemoryInfo(decoded, kind, i);
|
||||||
// Raw s_load uses a two-SGPR pointer base; s_buffer_load uses a four-SGPR
|
// Raw s_load uses a two-SGPR pointer base; s_buffer_load uses a four-SGPR
|
||||||
// descriptor resource index.
|
// descriptor resource index.
|
||||||
inst.memory.resource = op == Opcode::SLoadDword ? RawScalarLoadBase(decoded.src0)
|
inst.memory.resource = op == Opcode::SLoadDword ? RawScalarLoadBase(decoded.src0)
|
||||||
@@ -511,13 +513,13 @@ bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& blo
|
|||||||
case Decoder::Opcode::SLoadDwordx4:
|
case Decoder::Opcode::SLoadDwordx4:
|
||||||
case Decoder::Opcode::SLoadDwordx8:
|
case Decoder::Opcode::SLoadDwordx8:
|
||||||
case Decoder::Opcode::SLoadDwordx16:
|
case Decoder::Opcode::SLoadDwordx16:
|
||||||
return LowerScalarBufferLoadDword(decoded, block, Opcode::SLoadDword, error);
|
return LowerScalarMemoryLoadDword(decoded, block, Opcode::SLoadDword, error);
|
||||||
case Decoder::Opcode::SBufferLoadDword:
|
case Decoder::Opcode::SBufferLoadDword:
|
||||||
case Decoder::Opcode::SBufferLoadDwordx2:
|
case Decoder::Opcode::SBufferLoadDwordx2:
|
||||||
case Decoder::Opcode::SBufferLoadDwordx4:
|
case Decoder::Opcode::SBufferLoadDwordx4:
|
||||||
case Decoder::Opcode::SBufferLoadDwordx8:
|
case Decoder::Opcode::SBufferLoadDwordx8:
|
||||||
case Decoder::Opcode::SBufferLoadDwordx16:
|
case Decoder::Opcode::SBufferLoadDwordx16:
|
||||||
return LowerScalarBufferLoadDword(decoded, block, Opcode::SBufferLoadDword, error);
|
return LowerScalarMemoryLoadDword(decoded, block, Opcode::SBufferLoadDword, error);
|
||||||
case Decoder::Opcode::BufferLoadUbyte:
|
case Decoder::Opcode::BufferLoadUbyte:
|
||||||
case Decoder::Opcode::BufferLoadSbyte:
|
case Decoder::Opcode::BufferLoadSbyte:
|
||||||
case Decoder::Opcode::BufferLoadUshort:
|
case Decoder::Opcode::BufferLoadUshort:
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
|||||||
auto base = (static_cast<uint64_t>(value.dwords[0]) |
|
auto base = (static_cast<uint64_t>(value.dwords[0]) |
|
||||||
static_cast<uint64_t>(value.dwords[1]) << 32u) &
|
static_cast<uint64_t>(value.dwords[1]) << 32u) &
|
||||||
AddressMask;
|
AddressMask;
|
||||||
if (address.kind == ResourceKind::ScalarBuffer) {
|
if (address.kind == ResourceKind::ScalarAddress) {
|
||||||
base &= ~uint64_t {3};
|
base &= ~uint64_t {3};
|
||||||
}
|
}
|
||||||
const auto before = static_cast<uint64_t>(-static_cast<int64_t>(address.min_offset));
|
const auto before = static_cast<uint64_t>(-static_cast<int64_t>(address.min_offset));
|
||||||
|
|||||||
@@ -724,7 +724,7 @@ private:
|
|||||||
uint32_t AddAddress(uint32_t source, bool unbased, const MemoryInfo& memory, ValueOpcode op,
|
uint32_t AddAddress(uint32_t source, bool unbased, const MemoryInfo& memory, ValueOpcode op,
|
||||||
uint32_t pc) {
|
uint32_t pc) {
|
||||||
auto immediate = static_cast<int32_t>(memory.offset);
|
auto immediate = static_cast<int32_t>(memory.offset);
|
||||||
if (memory.kind == ResourceKind::ScalarBuffer) {
|
if (memory.kind == ResourceKind::ScalarAddress) {
|
||||||
immediate = static_cast<int32_t>(static_cast<uint32_t>(immediate) & ~3u);
|
immediate = static_cast<int32_t>(static_cast<uint32_t>(immediate) & ~3u);
|
||||||
}
|
}
|
||||||
const auto min_offset = unbased ? 0 : std::min(immediate, 0);
|
const auto min_offset = unbased ? 0 : std::min(immediate, 0);
|
||||||
|
|||||||
@@ -50,13 +50,17 @@ bool AddSignedAddress(uint64_t base, int64_t offset, uint64_t& result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsRawRead(const ValueProgram& values, const Inst& inst) {
|
bool IsRawRead(const ValueProgram& values, const Inst& inst) {
|
||||||
if (inst.GetOpcode() != ValueOpcode::LoadAddressU32 &&
|
const auto op = inst.GetOpcode();
|
||||||
inst.GetOpcode() != ValueOpcode::ReadConstBuffer) {
|
if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto index = inst.Flags<MemoryFlags>().index;
|
const auto index = inst.Flags<MemoryFlags>().index;
|
||||||
return index < values.memory_info.size() &&
|
if (index >= values.memory_info.size()) {
|
||||||
values.memory_info[index].kind == ResourceKind::ScalarBuffer;
|
return false;
|
||||||
|
}
|
||||||
|
const auto kind = values.memory_info[index].kind;
|
||||||
|
return (op == ValueOpcode::LoadAddressU32 && kind == ResourceKind::ScalarAddress) ||
|
||||||
|
(op == ValueOpcode::ReadConstBuffer && kind == ResourceKind::ScalarBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDescriptorHandle(ValueOpcode opcode) {
|
bool IsDescriptorHandle(ValueOpcode opcode) {
|
||||||
@@ -262,6 +266,22 @@ public:
|
|||||||
m_values.dynamic_reads.clear();
|
m_values.dynamic_reads.clear();
|
||||||
for (auto* block: m_values.blocks) {
|
for (auto* block: m_values.blocks) {
|
||||||
for (auto& inst: *block) {
|
for (auto& inst: *block) {
|
||||||
|
const auto op = inst.GetOpcode();
|
||||||
|
if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
|
||||||
|
const auto flags = inst.Flags<MemoryFlags>();
|
||||||
|
if (flags.index < m_values.memory_info.size()) {
|
||||||
|
const auto kind = m_values.memory_info[flags.index].kind;
|
||||||
|
const bool crosswired = (op == ValueOpcode::LoadAddressU32 &&
|
||||||
|
kind == ResourceKind::ScalarBuffer) ||
|
||||||
|
(op == ValueOpcode::ReadConstBuffer &&
|
||||||
|
kind == ResourceKind::ScalarAddress);
|
||||||
|
if (crosswired) {
|
||||||
|
return Fail(flags.pc, error,
|
||||||
|
fmt::format("{} has incompatible scalar memory metadata",
|
||||||
|
ValueOpcodeName(op)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (IsDescriptorHandle(inst.GetOpcode())) {
|
if (IsDescriptorHandle(inst.GetOpcode())) {
|
||||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||||
if (!Collect(inst.Arg(index), 0, error)) {
|
if (!Collect(inst.Arg(index), 0, error)) {
|
||||||
|
|||||||
@@ -171,14 +171,14 @@ MakeIndirectImageFixture(bool malformed, uint32_t material_immediate = 0,
|
|||||||
const auto pointer_address =
|
const auto pointer_address =
|
||||||
fixture->Address(fixture->UserData(9), fixture->UserData(10), 0x10b0);
|
fixture->Address(fixture->UserData(9), fixture->UserData(10), 0x10b0);
|
||||||
MemoryInfo pointer_word;
|
MemoryInfo pointer_word;
|
||||||
pointer_word.kind = ResourceKind::ScalarBuffer;
|
pointer_word.kind = ResourceKind::ScalarAddress;
|
||||||
const auto pointer =
|
const auto pointer =
|
||||||
fixture->Emit(ValueOpcode::LoadAddressU32,
|
fixture->Emit(ValueOpcode::LoadAddressU32,
|
||||||
{pointer_address, Value(0u), Value(0u), Value(true)},
|
{pointer_address, Value(0u), Value(0u), Value(true)},
|
||||||
fixture->AddMemory(pointer_word, 0x10b0));
|
fixture->AddMemory(pointer_word, 0x10b0));
|
||||||
const auto address = fixture->Address(pointer, Value(0u), 0x10c0);
|
const auto address = fixture->Address(pointer, Value(0u), 0x10c0);
|
||||||
MemoryInfo descriptor_word;
|
MemoryInfo descriptor_word;
|
||||||
descriptor_word.kind = ResourceKind::ScalarBuffer;
|
descriptor_word.kind = ResourceKind::ScalarAddress;
|
||||||
material_words[0] =
|
material_words[0] =
|
||||||
fixture->Emit(ValueOpcode::LoadAddressU32,
|
fixture->Emit(ValueOpcode::LoadAddressU32,
|
||||||
{address, Value(0u), Value(0u), Value(true)},
|
{address, Value(0u), Value(0u), Value(true)},
|
||||||
@@ -642,7 +642,7 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
|||||||
const auto base =
|
const auto base =
|
||||||
fixture.Address(fixture.UserData(0), fixture.UserData(1), 4);
|
fixture.Address(fixture.UserData(0), fixture.UserData(1), 4);
|
||||||
MemoryInfo scalar;
|
MemoryInfo scalar;
|
||||||
scalar.kind = ResourceKind::ScalarBuffer;
|
scalar.kind = ResourceKind::ScalarAddress;
|
||||||
scalar.offset = 4;
|
scalar.offset = 4;
|
||||||
const auto read0 = fixture.Emit(ValueOpcode::LoadAddressU32,
|
const auto read0 = fixture.Emit(ValueOpcode::LoadAddressU32,
|
||||||
{base, Value(0u), Value(0u), Value(true)},
|
{base, Value(0u), Value(0u), Value(true)},
|
||||||
@@ -710,7 +710,7 @@ void TestDynamicSrtReadRemainsExplicit() {
|
|||||||
const auto base =
|
const auto base =
|
||||||
fixture.Address(fixture.UserData(0), fixture.UserData(1), 4);
|
fixture.Address(fixture.UserData(0), fixture.UserData(1), 4);
|
||||||
MemoryInfo scalar;
|
MemoryInfo scalar;
|
||||||
scalar.kind = ResourceKind::ScalarBuffer;
|
scalar.kind = ResourceKind::ScalarAddress;
|
||||||
const auto read =
|
const auto read =
|
||||||
fixture.Emit(ValueOpcode::LoadAddressU32,
|
fixture.Emit(ValueOpcode::LoadAddressU32,
|
||||||
{base, fixture.UserData(2), Value(0u), Value(true)},
|
{base, fixture.UserData(2), Value(0u), Value(true)},
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ Value RawRead(Fixture &fixture, Value address, Value offset, uint32_t memory,
|
|||||||
|
|
||||||
void TestImmediateFlatteningAndGvn() {
|
void TestImmediateFlatteningAndGvn() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer, 0x20);
|
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress, 0x20);
|
||||||
const auto first = RawRead(
|
const auto first = RawRead(
|
||||||
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
||||||
const auto second = RawRead(
|
const auto second = RawRead(
|
||||||
@@ -119,9 +119,53 @@ void TestImmediateFlatteningAndGvn() {
|
|||||||
"flattened SRT did not evaluate its canonical read once");
|
"flattened SRT did not evaluate its canonical read once");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestRawScalarComponentAlignment() {
|
||||||
|
Fixture fixture;
|
||||||
|
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress, 2);
|
||||||
|
const auto read = RawRead(
|
||||||
|
fixture, Address(fixture, Value(0x1003u), Value(0u)), Value(2u), memory);
|
||||||
|
fixture.Emit(ValueOpcode::GetBufferResource,
|
||||||
|
{read, Value(0u), Value(16u), Value(0u)});
|
||||||
|
fixture.Plan();
|
||||||
|
|
||||||
|
TestMemory memory_image{{{0x1000u, 0x12345678u}}};
|
||||||
|
SrtRuntime runtime{.read_memory = ReadMemory, .userdata = &memory_image};
|
||||||
|
std::vector<uint32_t> flat;
|
||||||
|
std::string error;
|
||||||
|
Check(WalkSrt(fixture.program, runtime, flat, &error), error.c_str());
|
||||||
|
Check(flat == std::vector<uint32_t>{0x12345678u} &&
|
||||||
|
memory_image.reads == 1,
|
||||||
|
"raw scalar base, immediate, and offset were not aligned independently");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestScalarMemoryDomainMismatchFails() {
|
||||||
|
Fixture raw;
|
||||||
|
const auto raw_memory = raw.AddMemory(ResourceKind::ScalarBuffer);
|
||||||
|
RawRead(raw, Address(raw, Value(0x1000u), Value(0u)), Value(0u),
|
||||||
|
raw_memory);
|
||||||
|
std::string error;
|
||||||
|
Check(!BuildSrtPlan(raw.program, &error) &&
|
||||||
|
error.find("incompatible scalar memory metadata") !=
|
||||||
|
std::string::npos,
|
||||||
|
"raw scalar load accepted descriptor-buffer metadata");
|
||||||
|
|
||||||
|
Fixture buffer;
|
||||||
|
const auto buffer_memory = buffer.AddMemory(ResourceKind::ScalarAddress);
|
||||||
|
const auto resource = buffer.Emit(ValueOpcode::GetBufferResource,
|
||||||
|
{Value(0x1000u), Value(0u), Value(16u),
|
||||||
|
Value(0u)});
|
||||||
|
buffer.EmitMemory(ValueOpcode::ReadConstBuffer,
|
||||||
|
{resource, Value(0u)}, buffer_memory);
|
||||||
|
error.clear();
|
||||||
|
Check(!BuildSrtPlan(buffer.program, &error) &&
|
||||||
|
error.find("incompatible scalar memory metadata") !=
|
||||||
|
std::string::npos,
|
||||||
|
"descriptor scalar load accepted raw-address metadata");
|
||||||
|
}
|
||||||
|
|
||||||
void TestDynamicReadRemainsTyped() {
|
void TestDynamicReadRemainsTyped() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer);
|
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress);
|
||||||
const auto offset = fixture.Emit(ValueOpcode::GetUserData,
|
const auto offset = fixture.Emit(ValueOpcode::GetUserData,
|
||||||
{Value(static_cast<ScalarReg>(2))});
|
{Value(static_cast<ScalarReg>(2))});
|
||||||
const auto read = RawRead(
|
const auto read = RawRead(
|
||||||
@@ -137,7 +181,7 @@ void TestDynamicReadRemainsTyped() {
|
|||||||
|
|
||||||
void TestNestedSrtWalk() {
|
void TestNestedSrtWalk() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer);
|
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress);
|
||||||
const auto pointer = RawRead(
|
const auto pointer = RawRead(
|
||||||
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
||||||
const auto value =
|
const auto value =
|
||||||
@@ -242,7 +286,7 @@ void TestInvariantAndDivergentPhi() {
|
|||||||
|
|
||||||
void TestControlDependentStandaloneLoadStaysTyped() {
|
void TestControlDependentStandaloneLoadStaysTyped() {
|
||||||
Fixture fixture(3);
|
Fixture fixture(3);
|
||||||
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer);
|
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress);
|
||||||
auto &base = fixture.BlockAt(2).AppendNewInst(ValueOpcode::Phi);
|
auto &base = fixture.BlockAt(2).AppendNewInst(ValueOpcode::Phi);
|
||||||
base.SetFlags(Type::U32);
|
base.SetFlags(Type::U32);
|
||||||
base.AddPhiOperand(&fixture.BlockAt(0), Value(0x1000u));
|
base.AddPhiOperand(&fixture.BlockAt(0), Value(0x1000u));
|
||||||
@@ -390,6 +434,8 @@ void DbgExit(int) { std::abort(); }
|
|||||||
int main() {
|
int main() {
|
||||||
try {
|
try {
|
||||||
TestImmediateFlatteningAndGvn();
|
TestImmediateFlatteningAndGvn();
|
||||||
|
TestRawScalarComponentAlignment();
|
||||||
|
TestScalarMemoryDomainMismatchFails();
|
||||||
TestDynamicReadRemainsTyped();
|
TestDynamicReadRemainsTyped();
|
||||||
TestNestedSrtWalk();
|
TestNestedSrtWalk();
|
||||||
TestShaderBaseAndUserData();
|
TestShaderBaseAndUserData();
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ SrtProgram(uint64_t address) {
|
|||||||
auto &value_block = AddValueBlock(program);
|
auto &value_block = AddValueBlock(program);
|
||||||
|
|
||||||
MemoryInfo memory;
|
MemoryInfo memory;
|
||||||
memory.kind = ResourceKind::ScalarBuffer;
|
memory.kind = ResourceKind::ScalarAddress;
|
||||||
memory.planning_only = true;
|
memory.planning_only = true;
|
||||||
program.values->memory_info.push_back(memory);
|
program.values->memory_info.push_back(memory);
|
||||||
const auto low = Value(static_cast<uint32_t>(address));
|
const auto low = Value(static_cast<uint32_t>(address));
|
||||||
|
|||||||
@@ -3582,6 +3582,132 @@ void TestNewShaderRecompilerMemoryFamilyLowering() {
|
|||||||
CheckSpirvBinaryValidates(result.spirv);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestNewShaderRecompilerScalarMemoryBindingDomains() {
|
||||||
|
const auto count_live_memory_ops =
|
||||||
|
[](const ShaderRecompiler::IR::Program &program,
|
||||||
|
ShaderRecompiler::IR::ValueOpcode opcode,
|
||||||
|
ShaderRecompiler::IR::ResourceKind kind) {
|
||||||
|
size_t count = 0;
|
||||||
|
for (const auto *block : program.values->blocks) {
|
||||||
|
for (const auto &inst : *block) {
|
||||||
|
if (inst.GetOpcode() != opcode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto index =
|
||||||
|
inst.Flags<ShaderRecompiler::IR::MemoryFlags>().index;
|
||||||
|
Check(index < program.values->memory_info.size(),
|
||||||
|
"typed scalar memory operation has invalid metadata");
|
||||||
|
const auto &memory = program.values->memory_info[index];
|
||||||
|
Check(memory.kind == kind && memory.resource == 0u &&
|
||||||
|
!memory.planning_only,
|
||||||
|
"typed scalar memory operation has the wrong live domain");
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t raw_shader[] = {
|
||||||
|
EncodeSmem0(0x00, 12, 4),
|
||||||
|
2u, // s_load_dword s12, s[8:9], s0 offset:2
|
||||||
|
EncodeVop1(0x01, 0, 12),
|
||||||
|
EncodeExp0(0x00, 0x1),
|
||||||
|
EncodeExp1(0, 0, 0, 0),
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
std::array<uint32_t, 12> raw_user_data{};
|
||||||
|
raw_user_data[0] = 2u;
|
||||||
|
raw_user_data[8] = 0x1003u;
|
||||||
|
|
||||||
|
ShaderRecompiler::CompileOptions raw_options;
|
||||||
|
raw_options.stage = ShaderType::Pixel;
|
||||||
|
raw_options.user_data = raw_user_data.data();
|
||||||
|
raw_options.user_data_count = static_cast<uint32_t>(raw_user_data.size());
|
||||||
|
|
||||||
|
ShaderRecompiler::CompileResult raw;
|
||||||
|
std::string error;
|
||||||
|
Check(ShaderRecompiler::TryRecompile(raw_shader, raw_options, raw, &error),
|
||||||
|
error.c_str());
|
||||||
|
const auto *address_binding = ShaderRecompiler::IR::FindBinding(
|
||||||
|
raw.program.bindings,
|
||||||
|
ShaderRecompiler::IR::DescriptorBindingKind::AddressMemory);
|
||||||
|
Check(raw.program.info.addresses.size() == 1u &&
|
||||||
|
raw.program.info.addresses[0].kind ==
|
||||||
|
ShaderRecompiler::IR::ResourceKind::ScalarAddress &&
|
||||||
|
raw.program.info.addresses[0].read &&
|
||||||
|
!raw.program.info.addresses[0].written &&
|
||||||
|
raw.program.info.buffers.empty() && address_binding != nullptr &&
|
||||||
|
address_binding->resources == std::vector<uint32_t>{0u} &&
|
||||||
|
ShaderRecompiler::IR::FindBinding(
|
||||||
|
raw.program.bindings,
|
||||||
|
ShaderRecompiler::IR::DescriptorBindingKind::Buffers) == nullptr &&
|
||||||
|
ShaderRecompiler::IR::FindBinding(
|
||||||
|
raw.program.bindings,
|
||||||
|
ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) ==
|
||||||
|
nullptr &&
|
||||||
|
raw.program.bindings.buffer_offset_count == 0u,
|
||||||
|
"raw scalar load did not use only the address-memory domain");
|
||||||
|
Check(count_live_memory_ops(
|
||||||
|
raw.program, ShaderRecompiler::IR::ValueOpcode::LoadAddressU32,
|
||||||
|
ShaderRecompiler::IR::ResourceKind::ScalarAddress) == 1u,
|
||||||
|
"raw scalar load did not remain a live typed address operation");
|
||||||
|
Check(raw.resources.addresses.size() == 1u &&
|
||||||
|
raw.resources.addresses[0].guest_base == 0x1000u &&
|
||||||
|
raw.resources.addresses[0].binding_base == 0x1000u &&
|
||||||
|
raw.program.info.addresses[0].specialized_base == 0u,
|
||||||
|
"raw scalar address specialization was incorrect");
|
||||||
|
Check(SpirvContainsOpcode(raw.spirv, 199),
|
||||||
|
"raw scalar SOFFSET alignment was not emitted");
|
||||||
|
CheckSpirvBinaryValidates(raw.spirv);
|
||||||
|
|
||||||
|
const uint32_t buffer_shader[] = {
|
||||||
|
EncodeSmem0(0x08, 12, 4),
|
||||||
|
0u, // s_buffer_load_dword s12, s[8:11], s0
|
||||||
|
EncodeVop1(0x01, 0, 12),
|
||||||
|
EncodeExp0(0x00, 0x1),
|
||||||
|
EncodeExp1(0, 0, 0, 0),
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
std::array<uint32_t, 12> buffer_user_data{};
|
||||||
|
buffer_user_data[8] = 0x2000u;
|
||||||
|
buffer_user_data[10] = 4u;
|
||||||
|
|
||||||
|
ShaderRecompiler::CompileOptions buffer_options;
|
||||||
|
buffer_options.stage = ShaderType::Pixel;
|
||||||
|
buffer_options.user_data = buffer_user_data.data();
|
||||||
|
buffer_options.user_data_count =
|
||||||
|
static_cast<uint32_t>(buffer_user_data.size());
|
||||||
|
|
||||||
|
ShaderRecompiler::CompileResult buffer;
|
||||||
|
error.clear();
|
||||||
|
Check(ShaderRecompiler::TryRecompile(buffer_shader, buffer_options, buffer,
|
||||||
|
&error),
|
||||||
|
error.c_str());
|
||||||
|
const auto *buffer_binding = ShaderRecompiler::IR::FindBinding(
|
||||||
|
buffer.program.bindings,
|
||||||
|
ShaderRecompiler::IR::DescriptorBindingKind::Buffers);
|
||||||
|
Check(buffer.program.info.buffers.size() == 1u &&
|
||||||
|
buffer.program.info.buffers[0].scalar &&
|
||||||
|
buffer.program.info.addresses.empty() && buffer_binding != nullptr &&
|
||||||
|
buffer_binding->resources == std::vector<uint32_t>{0u} &&
|
||||||
|
ShaderRecompiler::IR::FindBinding(
|
||||||
|
buffer.program.bindings,
|
||||||
|
ShaderRecompiler::IR::DescriptorBindingKind::AddressMemory) ==
|
||||||
|
nullptr &&
|
||||||
|
ShaderRecompiler::IR::FindBinding(
|
||||||
|
buffer.program.bindings,
|
||||||
|
ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) ==
|
||||||
|
nullptr &&
|
||||||
|
buffer.program.bindings.buffer_offset_count == 1u,
|
||||||
|
"descriptor scalar load did not use only the buffer domain");
|
||||||
|
Check(count_live_memory_ops(
|
||||||
|
buffer.program,
|
||||||
|
ShaderRecompiler::IR::ValueOpcode::ReadConstBuffer,
|
||||||
|
ShaderRecompiler::IR::ResourceKind::ScalarBuffer) == 1u,
|
||||||
|
"descriptor scalar load did not remain a live typed buffer operation");
|
||||||
|
CheckSpirvBinaryValidates(buffer.spirv);
|
||||||
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerImageQueryLowering() {
|
void TestNewShaderRecompilerImageQueryLowering() {
|
||||||
const uint32_t shader[] = {
|
const uint32_t shader[] = {
|
||||||
EncodeMimg0(0x60, 0x3),
|
EncodeMimg0(0x60, 0x3),
|
||||||
@@ -9014,6 +9140,7 @@ int main() {
|
|||||||
TestNormalizedImageContracts();
|
TestNormalizedImageContracts();
|
||||||
TestNativeSubgroupPolicy();
|
TestNativeSubgroupPolicy();
|
||||||
TestNewShaderRecompilerSMovB32();
|
TestNewShaderRecompilerSMovB32();
|
||||||
|
TestNewShaderRecompilerScalarMemoryBindingDomains();
|
||||||
// Opcode semantics and optimized direct SPIR-V are exercised by
|
// Opcode semantics and optimized direct SPIR-V are exercised by
|
||||||
// ShaderRecompilerComputeTests. The pre-SSA register-IR shape checks above
|
// ShaderRecompilerComputeTests. The pre-SSA register-IR shape checks above
|
||||||
// remain as historical decoder fixtures only.
|
// remain as historical decoder fixtures only.
|
||||||
|
|||||||
Reference in new issue
Block a user