mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-25 04:16:55 +01:00
shader: resolve descriptors before resource planning
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ReadLaneElimination.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceTracking.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -71,7 +72,7 @@ struct Fixture {
|
||||
return static_cast<uint32_t>(program.memory_info.size() - 1u);
|
||||
}
|
||||
|
||||
void Plan() { BuildSrtPlan(program); }
|
||||
void Plan() { TrackResources(program, {}, {}); }
|
||||
};
|
||||
|
||||
struct TestMemory {
|
||||
@@ -102,6 +103,16 @@ Value RawRead(Fixture &fixture, Value address, Value offset, uint32_t memory,
|
||||
0x80, block);
|
||||
}
|
||||
|
||||
void LoadBuffer(Fixture &fixture, std::array<Value, 4> words) {
|
||||
const auto handle = fixture.Emit(ValueOpcode::GetBufferResource,
|
||||
{words[0], words[1], words[2], words[3]});
|
||||
const auto loaded = fixture.EmitMemory(
|
||||
ValueOpcode::LoadBufferU32,
|
||||
{handle, Value(0u), Value(0u), Value(0u), Value(true)},
|
||||
fixture.AddMemory(ResourceKind::Buffer));
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {loaded});
|
||||
}
|
||||
|
||||
void TestImmediateFlatteningAndGvn() {
|
||||
Fixture fixture;
|
||||
const auto memory = fixture.AddMemory(ResourceKind::ScalarAddress, 0x20);
|
||||
@@ -109,14 +120,11 @@ void TestImmediateFlatteningAndGvn() {
|
||||
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
||||
const auto second = RawRead(
|
||||
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
||||
fixture.Emit(ValueOpcode::GetBufferResource,
|
||||
{first, second, Value(16u), Value(0u)});
|
||||
LoadBuffer(fixture, {first, second, Value(16u), Value(0u)});
|
||||
|
||||
fixture.Plan();
|
||||
Check(fixture.program.srt_reads.size() == 1,
|
||||
"equivalent typed scalar reads were not coalesced");
|
||||
Check(fixture.program.dynamic_reads.empty(),
|
||||
"immediate scalar read was classified as dynamic");
|
||||
Check(fixture.program.memory_info[memory].planning_only,
|
||||
"flattened raw read was not kept as a planning-only root");
|
||||
|
||||
@@ -133,8 +141,7 @@ void TestRawScalarComponentAlignment() {
|
||||
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)});
|
||||
LoadBuffer(fixture, {read, Value(0u), Value(16u), Value(0u)});
|
||||
fixture.Plan();
|
||||
|
||||
TestMemory memory_image{{{0x1000u, 0x12345678u}}};
|
||||
@@ -150,7 +157,7 @@ void TestScalarMemoryDomainMismatchFails() {
|
||||
Fixture raw;
|
||||
const auto raw_memory = raw.AddMemory(ResourceKind::ScalarBuffer);
|
||||
RawRead(raw, Address(raw, Value(0x1000u), Value(0u)), Value(0u), raw_memory);
|
||||
CheckFatal([&] { BuildSrtPlan(raw.program); },
|
||||
CheckFatal([&] { TrackResources(raw.program, {}, {}); },
|
||||
"incompatible scalar memory metadata",
|
||||
"raw scalar load accepted descriptor-buffer metadata");
|
||||
|
||||
@@ -161,7 +168,7 @@ void TestScalarMemoryDomainMismatchFails() {
|
||||
{Value(0x1000u), Value(0u), Value(16u), Value(0u)});
|
||||
buffer.EmitMemory(ValueOpcode::ReadConstBuffer, {resource, Value(0u)},
|
||||
buffer_memory);
|
||||
CheckFatal([&] { BuildSrtPlan(buffer.program); },
|
||||
CheckFatal([&] { TrackResources(buffer.program, {}, {}); },
|
||||
"incompatible scalar memory metadata",
|
||||
"descriptor scalar load accepted raw-address metadata");
|
||||
}
|
||||
@@ -173,12 +180,12 @@ void TestDynamicReadRemainsTyped() {
|
||||
{Value(static_cast<ScalarReg>(2))});
|
||||
const auto read = RawRead(
|
||||
fixture, Address(fixture, Value(0x1000u), Value(0u)), offset, memory);
|
||||
fixture.Emit(ValueOpcode::GetBufferResource,
|
||||
{read, Value(0u), Value(16u), Value(0u)});
|
||||
LoadBuffer(fixture, {read, Value(0u), Value(16u), Value(0u)});
|
||||
|
||||
fixture.Plan();
|
||||
Check(fixture.program.srt_reads.empty() &&
|
||||
fixture.program.dynamic_reads == std::vector<Value>{read},
|
||||
read.ResolveInstruction()->GetOpcode() == ValueOpcode::LoadAddressU32 &&
|
||||
!fixture.program.memory_info[memory].planning_only,
|
||||
"dynamic scalar read received a fake flattened slot");
|
||||
}
|
||||
|
||||
@@ -189,8 +196,7 @@ void TestNestedSrtWalk() {
|
||||
fixture, Address(fixture, Value(0x1000u), Value(0u)), Value(0u), memory);
|
||||
const auto value =
|
||||
RawRead(fixture, Address(fixture, pointer, Value(0u)), Value(0u), memory);
|
||||
fixture.Emit(ValueOpcode::GetBufferResource,
|
||||
{value, Value(0u), Value(16u), Value(0u)});
|
||||
LoadBuffer(fixture, {value, Value(0u), Value(16u), Value(0u)});
|
||||
fixture.Plan();
|
||||
|
||||
TestMemory memory_image{{{0x1000u, 0x2000u}, {0x2000u, 0xabcdef01u}}};
|
||||
@@ -211,7 +217,7 @@ void TestShaderBaseAndUserData() {
|
||||
const auto user = fixture.Emit(ValueOpcode::GetUserData,
|
||||
{Value(static_cast<ScalarReg>(2))});
|
||||
const auto sum = fixture.Emit(ValueOpcode::IAdd32, {user, Value(4u)});
|
||||
fixture.Plan();
|
||||
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high, sum}, .dword_count = 3});
|
||||
|
||||
@@ -239,7 +245,7 @@ void TestCarryAndBitFields() {
|
||||
{Value(0u), Value(0x89abcdefu), Value(0u), Value(32u)});
|
||||
const auto sign = fixture.Emit(ValueOpcode::BitFieldSExtract,
|
||||
{Value(0x000000f0u), Value(4u), Value(4u)});
|
||||
fixture.Plan();
|
||||
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high, inserted, sign}, .dword_count = 4});
|
||||
|
||||
@@ -261,7 +267,7 @@ void TestInvariantAndDivergentPhi() {
|
||||
divergent.SetFlags(Type::U32);
|
||||
divergent.AddPhiOperand(&fixture.BlockAt(0), Value(7u));
|
||||
divergent.AddPhiOperand(&fixture.BlockAt(1), Value(9u));
|
||||
fixture.Plan();
|
||||
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {Value(&invariant)}, .dword_count = 1});
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
@@ -285,6 +291,7 @@ void TestControlDependentStandaloneLoadStaysTyped() {
|
||||
const auto read =
|
||||
RawRead(fixture, Address(fixture, Value(&base), Value(0u), 2), Value(0u),
|
||||
memory, 2);
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {read}, 0, 2);
|
||||
fixture.Plan();
|
||||
Check(fixture.program.srt_reads.empty() &&
|
||||
read.ResolveInstruction()->GetOpcode() ==
|
||||
@@ -307,7 +314,7 @@ void TestRuntime64BitDescriptorOps() {
|
||||
fixture.Emit(ValueOpcode::CompositeExtractU64, {combined, Value(0u)});
|
||||
const auto high =
|
||||
fixture.Emit(ValueOpcode::CompositeExtractU64, {combined, Value(1u)});
|
||||
fixture.Plan();
|
||||
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high}, .dword_count = 2});
|
||||
|
||||
@@ -374,7 +381,6 @@ void TestUniformFirstLaneSamplerLod() {
|
||||
{packed, active});
|
||||
const auto divergent = fixture.Emit(
|
||||
ValueOpcode::ReadFirstLane, {stale, active});
|
||||
fixture.Plan();
|
||||
|
||||
Check(ValidateRuntimeValue(fixture.program, first),
|
||||
"uniform sampler LOD construction was rejected");
|
||||
@@ -425,7 +431,7 @@ void TestFloatComparisonDescriptorInputs() {
|
||||
{greater_equal, Value(1u), Value(0u)});
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high}, .dword_count = 2});
|
||||
fixture.Plan();
|
||||
|
||||
for (size_t index = 0; index < inputs.size(); index++) {
|
||||
const auto &input = inputs[index];
|
||||
const std::array user_data{input.bits};
|
||||
@@ -489,8 +495,7 @@ void TestConstantBufferBounds() {
|
||||
{Value(0x3000u), Value(0u), Value(16u), Value(0u)});
|
||||
const auto read = fixture.EmitMemory(ValueOpcode::ReadConstBuffer,
|
||||
{buffer, Value(12u)}, memory);
|
||||
fixture.Emit(ValueOpcode::GetBufferResource,
|
||||
{read, Value(0u), Value(16u), Value(0u)});
|
||||
LoadBuffer(fixture, {read, Value(0u), Value(16u), Value(0u)});
|
||||
fixture.Plan();
|
||||
|
||||
TestMemory memory_image{{{0x300cu, 0xa5a5a5a5u}}};
|
||||
@@ -508,8 +513,7 @@ void TestConstantBufferBounds() {
|
||||
const auto overflow_read =
|
||||
overflow.EmitMemory(ValueOpcode::ReadConstBuffer,
|
||||
{overflow_buffer, Value(16u)}, overflow_memory);
|
||||
overflow.Emit(ValueOpcode::GetBufferResource,
|
||||
{overflow_read, Value(0u), Value(16u), Value(0u)});
|
||||
LoadBuffer(overflow, {overflow_read, Value(0u), Value(16u), Value(0u)});
|
||||
overflow.Plan();
|
||||
Check(!SrtWalker(overflow.program, runtime).RefreshFlatBuffer(flat),
|
||||
"out-of-bounds constant-buffer walk was accepted");
|
||||
@@ -595,7 +599,7 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
|
||||
void TestUndefinedRuntimeValueFails() {
|
||||
Fixture fixture;
|
||||
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
|
||||
fixture.Plan();
|
||||
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {undef}, .dword_count = 1});
|
||||
DescriptorValue result;
|
||||
@@ -655,6 +659,7 @@ int main() {
|
||||
#include "../src/graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Type.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Value.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp"
|
||||
|
||||
Reference in New Issue
Block a user