graphics: separate scalar address bindings

This commit is contained in:
nmzik
2026-08-16 08:04:07 +02:00
parent 83ee040b4d
commit 443bbcbf90
12 changed files with 229 additions and 32 deletions
+4 -4
View File
@@ -171,14 +171,14 @@ MakeIndirectImageFixture(bool malformed, uint32_t material_immediate = 0,
const auto pointer_address =
fixture->Address(fixture->UserData(9), fixture->UserData(10), 0x10b0);
MemoryInfo pointer_word;
pointer_word.kind = ResourceKind::ScalarBuffer;
pointer_word.kind = ResourceKind::ScalarAddress;
const auto pointer =
fixture->Emit(ValueOpcode::LoadAddressU32,
{pointer_address, Value(0u), Value(0u), Value(true)},
fixture->AddMemory(pointer_word, 0x10b0));
const auto address = fixture->Address(pointer, Value(0u), 0x10c0);
MemoryInfo descriptor_word;
descriptor_word.kind = ResourceKind::ScalarBuffer;
descriptor_word.kind = ResourceKind::ScalarAddress;
material_words[0] =
fixture->Emit(ValueOpcode::LoadAddressU32,
{address, Value(0u), Value(0u), Value(true)},
@@ -642,7 +642,7 @@ void TestSrtFlatteningAndRuntimeMemoization() {
const auto base =
fixture.Address(fixture.UserData(0), fixture.UserData(1), 4);
MemoryInfo scalar;
scalar.kind = ResourceKind::ScalarBuffer;
scalar.kind = ResourceKind::ScalarAddress;
scalar.offset = 4;
const auto read0 = fixture.Emit(ValueOpcode::LoadAddressU32,
{base, Value(0u), Value(0u), Value(true)},
@@ -710,7 +710,7 @@ void TestDynamicSrtReadRemainsExplicit() {
const auto base =
fixture.Address(fixture.UserData(0), fixture.UserData(1), 4);
MemoryInfo scalar;
scalar.kind = ResourceKind::ScalarBuffer;
scalar.kind = ResourceKind::ScalarAddress;
const auto read =
fixture.Emit(ValueOpcode::LoadAddressU32,
{base, fixture.UserData(2), Value(0u), Value(true)},
+50 -4
View File
@@ -94,7 +94,7 @@ Value RawRead(Fixture &fixture, Value address, Value offset, uint32_t memory,
void TestImmediateFlatteningAndGvn() {
Fixture fixture;
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer, 0x20);
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress, 0x20);
const auto first = RawRead(
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
const auto second = RawRead(
@@ -119,9 +119,53 @@ void TestImmediateFlatteningAndGvn() {
"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() {
Fixture fixture;
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer);
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress);
const auto offset = fixture.Emit(ValueOpcode::GetUserData,
{Value(static_cast<ScalarReg>(2))});
const auto read = RawRead(
@@ -137,7 +181,7 @@ void TestDynamicReadRemainsTyped() {
void TestNestedSrtWalk() {
Fixture fixture;
const auto memory = fixture.AddMemory(ResourceKind::ScalarBuffer);
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress);
const auto pointer = RawRead(
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
const auto value =
@@ -242,7 +286,7 @@ void TestInvariantAndDivergentPhi() {
void TestControlDependentStandaloneLoadStaysTyped() {
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);
base.SetFlags(Type::U32);
base.AddPhiOperand(&fixture.BlockAt(0), Value(0x1000u));
@@ -390,6 +434,8 @@ void DbgExit(int) { std::abort(); }
int main() {
try {
TestImmediateFlatteningAndGvn();
TestRawScalarComponentAlignment();
TestScalarMemoryDomainMismatchFails();
TestDynamicReadRemainsTyped();
TestNestedSrtWalk();
TestShaderBaseAndUserData();
+1 -1
View File
@@ -42,7 +42,7 @@ SrtProgram(uint64_t address) {
auto &value_block = AddValueBlock(program);
MemoryInfo memory;
memory.kind = ResourceKind::ScalarBuffer;
memory.kind = ResourceKind::ScalarAddress;
memory.planning_only = true;
program.values->memory_info.push_back(memory);
const auto low = Value(static_cast<uint32_t>(address));
+127
View File
@@ -3582,6 +3582,132 @@ void TestNewShaderRecompilerMemoryFamilyLowering() {
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() {
const uint32_t shader[] = {
EncodeMimg0(0x60, 0x3),
@@ -9014,6 +9140,7 @@ int main() {
TestNormalizedImageContracts();
TestNativeSubgroupPolicy();
TestNewShaderRecompilerSMovB32();
TestNewShaderRecompilerScalarMemoryBindingDomains();
// Opcode semantics and optimized direct SPIR-V are exercised by
// ShaderRecompilerComputeTests. The pre-SSA register-IR shape checks above
// remain as historical decoder fixtures only.