shader: compare real nested descriptor pointer instructions

This commit is contained in:
nmzik
2026-10-15 00:16:47 +02:00
parent bb2d6dbeb3
commit 65a6f30f51
3 changed files with 38 additions and 4 deletions
@@ -21,6 +21,7 @@ class Value {
public: public:
Value() = default; Value() = default;
explicit Value(Inst* value); explicit Value(Inst* value);
explicit Value(const Inst* value) = delete;
explicit Value(ScalarReg value); explicit Value(ScalarReg value);
explicit Value(VectorReg value); explicit Value(VectorReg value);
explicit Value(bool value); explicit Value(bool value);
@@ -2768,7 +2768,7 @@ private:
IndirectDescriptorPlan& plan) { IndirectDescriptorPlan& plan) {
if (handle.GetOpcode() != ValueOpcode::GetImageResource && if (handle.GetOpcode() != ValueOpcode::GetImageResource &&
handle.GetOpcode() != ValueOpcode::GetSamplerResource) return false; handle.GetOpcode() != ValueOpcode::GetSamplerResource) return false;
const Inst* pointer = nullptr; Inst* pointer = nullptr;
uint32_t descriptor_offset = 0; uint32_t descriptor_offset = 0;
for (uint32_t word = 0; word < handle.NumArgs(); ++word) { for (uint32_t word = 0; word < handle.NumArgs(); ++word) {
const auto* read = descriptor.dwords[word].Resolve().TryInstruction(); const auto* read = descriptor.dwords[word].Resolve().TryInstruction();
@@ -2777,7 +2777,7 @@ private:
if (memory == nullptr || memory->kind != ResourceKind::ScalarAddress || if (memory == nullptr || memory->kind != ResourceKind::ScalarAddress ||
memory->offset > INT32_MAX || !ImmediateU32(read->Arg(1), offset) || memory->offset > INT32_MAX || !ImmediateU32(read->Arg(1), offset) ||
!AvailableBefore(*read, handle)) return false; !AvailableBefore(*read, handle)) return false;
const auto* address = read->Arg(0).Resolve().TryInstruction(); auto* address = read->Arg(0).Resolve().TryInstruction();
const uint64_t current_offset = uint64_t {offset & ~3u} + (memory->offset & ~3u); const uint64_t current_offset = uint64_t {offset & ~3u} + (memory->offset & ~3u);
if (address == nullptr || address->GetOpcode() != ValueOpcode::GetAddressResource || if (address == nullptr || address->GetOpcode() != ValueOpcode::GetAddressResource ||
address->NumArgs() != 2u || current_offset > UINT32_MAX || address->NumArgs() != 2u || current_offset > UINT32_MAX ||
+35 -2
View File
@@ -16,6 +16,7 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <type_traits>
#include <vector> #include <vector>
namespace { namespace {
@@ -25,6 +26,10 @@ using Libs::Graphics::ShaderComputeInputInfo;
using Libs::Graphics::ShaderType; using Libs::Graphics::ShaderType;
namespace Decoder = Libs::Graphics::ShaderRecompiler::Decoder; namespace Decoder = Libs::Graphics::ShaderRecompiler::Decoder;
static_assert(std::is_constructible_v<Value, Inst*> &&
std::is_constructible_v<Value, bool> &&
!std::is_constructible_v<Value, const Inst*>);
void Check(bool condition, const char *message) { void Check(bool condition, const char *message) {
if (!condition) { if (!condition) {
throw std::runtime_error(message); throw std::runtime_error(message);
@@ -692,7 +697,8 @@ void TestWaterfallImageTable() {
void TestNestedPointerImageTable() { void TestNestedPointerImageTable() {
namespace CFG = Libs::Graphics::ShaderRecompiler::CFG; namespace CFG = Libs::Graphics::ShaderRecompiler::CFG;
const auto make = [](uint32_t variant = 0u) { enum class Address { Shared, Equivalent, ImageMismatch, SamplerMismatch };
const auto make = [](uint32_t variant = 0u, Address choice = Address::Shared) {
auto fixture = std::make_unique<Fixture>(); auto fixture = std::make_unique<Fixture>();
std::array<Value, 4> table_words, key_words; std::array<Value, 4> table_words, key_words;
for (uint32_t word = 0; word < 4u; ++word) { for (uint32_t word = 0; word < 4u; ++word) {
@@ -749,6 +755,19 @@ void TestNestedPointerImageTable() {
fixture->AddMemory(memory, 0x1f4u)); fixture->AddMemory(memory, 0x1f4u));
} }
const auto address = fixture->Address(pointer[0], pointer[1]); const auto address = fixture->Address(pointer[0], pointer[1]);
auto other_address = address;
if (choice == Address::Equivalent) {
other_address = fixture->Address(pointer[0], pointer[1]);
} else if (choice != Address::Shared) {
for (uint32_t word = 0; word < 2u; ++word) {
MemoryInfo memory;
memory.kind = ResourceKind::ScalarBuffer;
memory.offset = 8u + word * 4u;
pointer[word] = fixture->Emit(ValueOpcode::ReadConstBuffer, {table, offset},
fixture->AddMemory(memory, 0x1fcu));
}
other_address = fixture->Address(pointer[0], pointer[1]);
}
const auto load = [&](uint32_t first, uint32_t width) { const auto load = [&](uint32_t first, uint32_t width) {
if (width == 8u && variant >= 7u && variant <= 9u) if (width == 8u && variant >= 7u && variant <= 9u)
first = variant == 7u ? 0x4000u : variant == 8u ? 0x3ff0u : 0x3fe0u; first = variant == 7u ? 0x4000u : variant == 8u ? 0x3ff0u : 0x3fe0u;
@@ -759,8 +778,12 @@ void TestNestedPointerImageTable() {
memory.kind = ResourceKind::ScalarAddress; memory.kind = ResourceKind::ScalarAddress;
memory.offset = first + word * 4u; memory.offset = first + word * 4u;
if (variant == 3u && width == 8u && word == 7u) memory.offset += 4u; if (variant == 3u && width == 8u && word == 7u) memory.offset += 4u;
const bool alternate = word == 1u &&
(choice == Address::Equivalent ||
(choice == Address::ImageMismatch && width == 8u) ||
(choice == Address::SamplerMismatch && width == 4u));
words[word] = fixture->Emit(ValueOpcode::LoadAddressU32, words[word] = fixture->Emit(ValueOpcode::LoadAddressU32,
{address, Value(0u), Value(0u), Value(true)}, {alternate ? other_address : address, Value(0u), Value(0u), Value(true)},
fixture->AddMemory(memory, first == 0x3d0u ? 0x1110u : 0x1118u)); fixture->AddMemory(memory, first == 0x3d0u ? 0x1110u : 0x1118u));
} }
return words; return words;
@@ -820,6 +843,16 @@ void TestNestedPointerImageTable() {
snapshot.samplers[row + 1u].dwords[0] == row, snapshot.samplers[row + 1u].dwords[0] == row,
"nested pointer table mixed rows or pointee members"); "nested pointer table mixed rows or pointee members");
const auto populated = specialization; const auto populated = specialization;
auto equivalent = make(0u, Address::Equivalent);
equivalent->PlanAndTrack();
Check(MaterializeResources(ExtractResourcePlan(equivalent->program), runtime, snapshot, specialization) &&
specialization == populated,
"equivalent nested pointer address instructions changed descriptor selection");
for (const auto choice : {Address::ImageMismatch, Address::SamplerMismatch}) {
auto mismatched = make(0u, choice);
CheckFatal([&] { mismatched->PlanAndTrack(); }, "not a valid runtime value",
"nested pointer planning combined descriptor words from different pointer pairs");
}
const auto reads = memory.reads; const auto reads = memory.reads;
user_data[2] = 0u; user_data[2] = 0u;
Check(MaterializeResources(plan, runtime, snapshot, specialization) && Check(MaterializeResources(plan, runtime, snapshot, specialization) &&