mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-23 20:36:58 +02:00
shader: rework resource specialization and optimize pipeline caching
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
|
||||
void Check(bool value, const char *text) {
|
||||
if (!value) {
|
||||
std::fprintf(stderr, "ResourceMaterializationTests: failed: %s\n", text);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool RejectSpecializationRead(void *userdata, uint64_t, uint32_t *) {
|
||||
++*static_cast<uint32_t *>(userdata);
|
||||
return false;
|
||||
}
|
||||
|
||||
Libs::Graphics::ShaderRecompiler::IR::Block &
|
||||
AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
auto block = std::make_unique<Block>();
|
||||
auto *result = block.get();
|
||||
program.blocks.push_back(result);
|
||||
program.block_info.push_back({.id = 0});
|
||||
program.block_storage.push_back(std::move(block));
|
||||
return *result;
|
||||
}
|
||||
|
||||
Libs::Graphics::ShaderRecompiler::IR::ResourcePlan SrtPlan(uint64_t address) {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
auto &value_block = AddValueBlock(program);
|
||||
|
||||
MemoryInfo memory;
|
||||
memory.kind = ResourceKind::ScalarAddress;
|
||||
memory.planning_only = true;
|
||||
program.memory_info.push_back(memory);
|
||||
const auto low = Value(static_cast<uint32_t>(address));
|
||||
const auto high = Value(static_cast<uint32_t>(address >> 32u));
|
||||
auto &handle =
|
||||
value_block.AppendNewInst(ValueOpcode::GetAddressResource, {low, high});
|
||||
auto &raw = value_block.AppendNewInst(
|
||||
ValueOpcode::LoadAddressU32,
|
||||
{Value(&handle), Value(0u), Value(0u), Value(true)});
|
||||
raw.SetFlags(MemoryFlags{.index = 0, .pc = 0x40});
|
||||
program.srt_reads.push_back({Value(&raw), 0});
|
||||
|
||||
auto &srt = value_block.AppendNewInst(ValueOpcode::GetSrtResource);
|
||||
auto &flat = value_block.AppendNewInst(ValueOpcode::ReadConst,
|
||||
{Value(&srt), Value(0u)});
|
||||
DescriptorSource source;
|
||||
source.dwords[0] = Value(&flat);
|
||||
source.dwords[1] = Value(0u);
|
||||
source.dword_count = 2;
|
||||
program.descriptor_sources.push_back(source);
|
||||
return ExtractResourcePlan(program);
|
||||
}
|
||||
|
||||
Libs::Graphics::ShaderRecompiler::IR::ResourcePlan UnbasedFlatPlan() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
AddValueBlock(program);
|
||||
program.info.uses_dma = true;
|
||||
return ExtractResourcePlan(program);
|
||||
}
|
||||
|
||||
Libs::Graphics::ShaderRecompiler::IR::ResourcePlan UserDataBufferPlan() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
auto &value_block = AddValueBlock(program);
|
||||
|
||||
auto &user_data = value_block.AppendNewInst(
|
||||
ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(0))});
|
||||
DescriptorSource source;
|
||||
source.dwords[0] = Value(&user_data);
|
||||
source.dwords[1] = Value(0u);
|
||||
source.dwords[2] = Value(0u);
|
||||
source.dwords[3] = Value(0u);
|
||||
source.dword_count = 4;
|
||||
program.descriptor_sources.push_back(source);
|
||||
program.info.buffers.push_back({.source = 0});
|
||||
return ExtractResourcePlan(program);
|
||||
}
|
||||
|
||||
Libs::Graphics::ShaderRecompiler::IR::ResourcePlan MixedSamplerPlan() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
AddValueBlock(program);
|
||||
|
||||
const auto AddSource = [&program](uint32_t dword_count, uint32_t first) {
|
||||
DescriptorSource source;
|
||||
source.dword_count = dword_count;
|
||||
source.dwords[0] = Value(first);
|
||||
for (uint32_t i = 1; i < dword_count; i++) {
|
||||
source.dwords[i] = Value(0u);
|
||||
}
|
||||
program.descriptor_sources.push_back(source);
|
||||
return static_cast<uint32_t>(program.descriptor_sources.size() - 1u);
|
||||
};
|
||||
|
||||
const auto image0 = AddSource(8, 0);
|
||||
const auto image1 = AddSource(8, 0);
|
||||
const auto sampler0 = AddSource(4, 0x11111111u);
|
||||
const auto sampler1 = AddSource(4, 0x22222222u);
|
||||
program.info.images.push_back(
|
||||
{.source = image0,
|
||||
.resource_class = ImageResourceClass::Sampled,
|
||||
.numeric_class = Libs::Graphics::Prospero::TextureNumericClass::Float,
|
||||
.dimension =
|
||||
Libs::Graphics::ShaderRecompiler::Decoder::ImageDimension::Dim2D});
|
||||
program.info.images.push_back(
|
||||
{.source = image1,
|
||||
.resource_class = ImageResourceClass::Sampled,
|
||||
.numeric_class = Libs::Graphics::Prospero::TextureNumericClass::Float,
|
||||
.dimension =
|
||||
Libs::Graphics::ShaderRecompiler::Decoder::ImageDimension::Dim2D,
|
||||
.conversion_format =
|
||||
Libs::Graphics::Prospero::BufferFormat::k8_8_8_8UNorm});
|
||||
program.info.samplers.push_back({.source = sampler0});
|
||||
program.info.samplers.push_back({.source = sampler1});
|
||||
program.info.sampled_pairs.push_back({.image = 0, .sampler = 0});
|
||||
program.info.sampled_pairs.push_back({.image = 0, .sampler = 1});
|
||||
program.info.sampled_pairs.push_back({.image = 1, .sampler = 1});
|
||||
return ExtractResourcePlan(program);
|
||||
}
|
||||
|
||||
void TestMappedSrtUsesDirectReaderByDefault() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
const uint32_t dword = 0x12345678;
|
||||
auto plan = SrtPlan(reinterpret_cast<uint64_t>(&dword));
|
||||
uint32_t specialization_reads = 0;
|
||||
const SrtRuntime runtime{.userdata = &specialization_reads,
|
||||
.read_specialization_memory =
|
||||
RejectSpecializationRead};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
"mapped SRT stage materialization failed");
|
||||
Check(specialization_reads == 0,
|
||||
"ordinary SRT read used the specialization reader");
|
||||
Check(snapshot.flattened_srt.size() == 1 &&
|
||||
snapshot.flattened_srt[0] == dword,
|
||||
"cache rematerialization did not use the direct reader by default");
|
||||
}
|
||||
|
||||
void TestUnbasedFlatCacheHitMaterializes() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
auto plan = UnbasedFlatPlan();
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(plan, {}, snapshot, specialization),
|
||||
"unbased FLAT stage materialization failed");
|
||||
Check(snapshot.buffers.empty() && snapshot.images.empty(),
|
||||
"unbased FLAT plan produced unexpected descriptors");
|
||||
}
|
||||
|
||||
void TestFailedMaterializationPreservesPriorStage() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
auto plan = UserDataBufferPlan();
|
||||
ResourceSnapshot snapshot;
|
||||
snapshot.user_data.push_back(0xfeedbeefu);
|
||||
ResourceSpecialization specialization;
|
||||
specialization.buffers.push_back({.packed_stride = 7});
|
||||
Check(!MaterializeResources(plan, {}, snapshot, specialization),
|
||||
"missing runtime user data did not reject the cached stage");
|
||||
Check(snapshot.user_data == std::vector<uint32_t>{0xfeedbeefu} &&
|
||||
specialization.buffers.size() == 1 &&
|
||||
specialization.buffers[0].packed_stride == 7,
|
||||
"failed cache materialization changed its destinations");
|
||||
}
|
||||
|
||||
void TestMixedSamplerDuplicatesTheCorrectSnapshot() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
auto plan = MixedSamplerPlan();
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(plan, {}, snapshot, specialization),
|
||||
"mixed sampler materialization failed");
|
||||
Check(snapshot.samplers.size() == 3,
|
||||
"mixed sampler materialization appended unrelated samplers");
|
||||
Check(snapshot.samplers[2] == snapshot.samplers[1] &&
|
||||
snapshot.samplers[2] != snapshot.samplers[0],
|
||||
"point sampler variant duplicated the wrong runtime descriptor");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Common {
|
||||
|
||||
int DbgExitHandler(const char *, int, std::string_view) { std::abort(); }
|
||||
|
||||
int DbgExitHandler(const char *, int, fmt::text_style, std::string_view) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int DbgExitIfHandler(const char *, const char *, int) { return 1; }
|
||||
|
||||
void DbgExit(int) { std::abort(); }
|
||||
|
||||
} // namespace Common
|
||||
|
||||
int main() {
|
||||
TestMappedSrtUsesDirectReaderByDefault();
|
||||
TestUnbasedFlatCacheHitMaterializes();
|
||||
TestFailedMaterializationPreservesPriorStage();
|
||||
TestMixedSamplerDuplicatesTheCorrectSnapshot();
|
||||
std::puts("ResourceMaterializationTests: all cases passed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Keep this focused standalone target self-contained by amalgamating its small
|
||||
// typed-IR implementation set.
|
||||
#include "graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Type.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Value.cpp"
|
||||
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
|
||||
+184
-122
@@ -8,6 +8,7 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
@@ -29,6 +30,14 @@ void Check(bool condition, const char *message) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SameResourceSnapshot(const ResourceSnapshot &lhs,
|
||||
const ResourceSnapshot &rhs) {
|
||||
return lhs.buffers == rhs.buffers && lhs.images == rhs.images &&
|
||||
lhs.samplers == rhs.samplers &&
|
||||
lhs.flattened_srt == rhs.flattened_srt &&
|
||||
lhs.user_data == rhs.user_data;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void CheckFatal(F &&function, std::string_view expected, const char *message) {
|
||||
try {
|
||||
@@ -250,6 +259,7 @@ MakeIndirectImageFixture(bool malformed, uint32_t material_immediate = 0,
|
||||
void TestInvariantIndirectImageMaterialization() {
|
||||
auto fixture = MakeIndirectImageFixture(false);
|
||||
fixture->PlanAndTrack();
|
||||
auto resource_plan = ExtractResourcePlan(fixture->program);
|
||||
EliminateDeadCode(fixture->program.blocks);
|
||||
ValidateProgram(fixture->program, true);
|
||||
|
||||
@@ -298,24 +308,20 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
.userdata = &memory,
|
||||
.read_specialization_memory = ReadLinearTestMemory};
|
||||
ResourceSnapshot snapshot;
|
||||
const auto same_snapshot = [](const ResourceSnapshot &lhs,
|
||||
const ResourceSnapshot &rhs) {
|
||||
return lhs.buffers == rhs.buffers && lhs.images == rhs.images &&
|
||||
lhs.samplers == rhs.samplers &&
|
||||
lhs.flattened_srt == rhs.flattened_srt &&
|
||||
lhs.user_data == rhs.user_data &&
|
||||
lhs.indirect_images.empty() == rhs.indirect_images.empty();
|
||||
};
|
||||
Check(MaterializeResources(fixture->program, runtime, snapshot) &&
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, snapshot, specialization) &&
|
||||
snapshot.images.size() == 1 &&
|
||||
std::equal(image_descriptor.begin(), image_descriptor.end(),
|
||||
snapshot.images[0].dwords.begin()),
|
||||
"invariant indirect image table did not materialize");
|
||||
|
||||
const auto prior_snapshot = snapshot;
|
||||
const auto prior_specialization = specialization;
|
||||
memory.fail_address = 0x1004u;
|
||||
Check(!MaterializeResources(fixture->program, runtime, snapshot) &&
|
||||
same_snapshot(snapshot, prior_snapshot),
|
||||
Check(!MaterializeResources(resource_plan, runtime, snapshot,
|
||||
specialization) &&
|
||||
SameResourceSnapshot(snapshot, prior_snapshot) &&
|
||||
specialization == prior_specialization,
|
||||
"rejected planning memory read mutated the snapshot");
|
||||
memory.fail_address = UINT64_MAX;
|
||||
|
||||
@@ -330,8 +336,9 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
memory.words[(0x2020u - memory.base) / 4u + 3u] =
|
||||
image_descriptor[3] ^ (1u << 28u);
|
||||
ResourceSnapshot null_snapshot;
|
||||
Check(MaterializeResources(fixture->program, runtime, null_snapshot) &&
|
||||
null_snapshot.indirect_images.empty() &&
|
||||
ResourceSpecialization null_specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, null_snapshot,
|
||||
null_specialization) &&
|
||||
std::ranges::all_of(null_snapshot.images[0].dwords,
|
||||
[](uint32_t dword) { return dword == 0u; }),
|
||||
"stale typed null image descriptors were not canonicalized");
|
||||
@@ -345,19 +352,25 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
memory.words[(0x2020u - memory.base) / 4u] ^= 1u;
|
||||
memory.words[(0x1000u - memory.base + 36u) / 4u] = 1u;
|
||||
ResourceSnapshot dynamic_snapshot;
|
||||
Check(MaterializeResources(fixture->program, runtime, dynamic_snapshot) &&
|
||||
dynamic_snapshot.images.size() == 1 &&
|
||||
dynamic_snapshot.indirect_images.size() == 1 &&
|
||||
dynamic_snapshot.indirect_images[0].descriptors.size() == 2,
|
||||
ResourceSpecialization dynamic_specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, dynamic_snapshot,
|
||||
dynamic_specialization) &&
|
||||
dynamic_snapshot.images.size() == 2 &&
|
||||
dynamic_specialization.images.size() == 2,
|
||||
"dynamic indirect image table did not materialize");
|
||||
SpecializeResources(fixture->program, dynamic_snapshot);
|
||||
ApplyResourceSpecialization(fixture->program, dynamic_specialization);
|
||||
Check(fixture->program.info.images.size() == 2 &&
|
||||
fixture->program.info.images[0].indirect_root == 0 &&
|
||||
fixture->program.info.images[0].indirect_mapping_capacity != 0 &&
|
||||
fixture->program.info.images[0].indirect_search_iterations != 0 &&
|
||||
fixture->program.info.images[0].indirect_resources.size() == 2 &&
|
||||
dynamic_snapshot.images.size() == 2 &&
|
||||
dynamic_snapshot.indirect_images.empty(),
|
||||
dynamic_snapshot.images.size() == 2,
|
||||
"dynamic indirect image table was not specialized transactionally");
|
||||
const auto &mapping = dynamic_specialization.images[0];
|
||||
const auto key_count = dynamic_snapshot.flattened_srt[mapping.indirect_mapping_offset];
|
||||
Check(mapping.indirect_search_iterations == std::bit_width(key_count) &&
|
||||
mapping.indirect_mapping_offset + 1u + key_count * 2u ==
|
||||
dynamic_snapshot.flattened_srt.size(),
|
||||
"indirect image mapping retained worst-case padding");
|
||||
|
||||
for (uint32_t dword = 0; dword < image_descriptor.size(); dword++) {
|
||||
memory.words[(0x2000u - memory.base) / 4u + dword] =
|
||||
@@ -368,22 +381,25 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
memory.words[(0x2000u - memory.base) / 4u] += 0x100u;
|
||||
memory.words[(0x2020u - memory.base) / 4u] += 0x101u;
|
||||
ResourceSnapshot rebound_snapshot;
|
||||
Check(MaterializeResources(fixture->program, runtime, rebound_snapshot) &&
|
||||
ValidateResourceSpecialization(fixture->program, rebound_snapshot),
|
||||
ResourceSpecialization rebound_specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, rebound_snapshot,
|
||||
rebound_specialization) &&
|
||||
rebound_specialization == dynamic_specialization,
|
||||
"stable indirect key mapping did not accept changed image addresses");
|
||||
memory.words[(0x2020u - memory.base) / 4u] =
|
||||
memory.words[(0x2000u - memory.base) / 4u];
|
||||
Check(MaterializeResources(fixture->program, runtime, rebound_snapshot) &&
|
||||
ValidateResourceSpecialization(fixture->program, rebound_snapshot),
|
||||
"runtime indirect key mapping did not accept collapsed candidates");
|
||||
const auto collapsed_snapshot = rebound_snapshot;
|
||||
Check(MaterializeResources(resource_plan, runtime, rebound_snapshot,
|
||||
rebound_specialization) &&
|
||||
rebound_specialization != dynamic_specialization,
|
||||
"collapsed indirect candidates did not select a new specialization");
|
||||
const auto collapsed_specialization = rebound_specialization;
|
||||
ResourceSnapshot capacity_snapshot;
|
||||
ResourceSpecialization capacity_specialization;
|
||||
for (const uint32_t records : {1u, 3u}) {
|
||||
user_data[2] = records;
|
||||
Check(
|
||||
MaterializeResources(fixture->program, runtime, capacity_snapshot) &&
|
||||
ValidateResourceSpecialization(fixture->program, capacity_snapshot),
|
||||
"runtime indirect key mapping rejected a fitting material-table size");
|
||||
Check(MaterializeResources(resource_plan, runtime, capacity_snapshot,
|
||||
capacity_specialization),
|
||||
"runtime indirect key mapping rejected a valid material-table size");
|
||||
}
|
||||
user_data[2] = 2u;
|
||||
memory.words[(0x2020u - memory.base) / 4u] =
|
||||
@@ -395,13 +411,14 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
image_descriptor[dword];
|
||||
}
|
||||
memory.words[(0x1000u - memory.base + 68u) / 4u] = 2u;
|
||||
Check(
|
||||
!MaterializeResources(fixture->program, runtime, rebound_snapshot) &&
|
||||
same_snapshot(rebound_snapshot, collapsed_snapshot),
|
||||
"larger indirect candidate topology reused or mutated a cached snapshot");
|
||||
Check(MaterializeResources(resource_plan, runtime, rebound_snapshot,
|
||||
rebound_specialization) &&
|
||||
rebound_specialization != collapsed_specialization,
|
||||
"larger indirect candidate topology reused the old specialization");
|
||||
|
||||
auto memory_backed = MakeIndirectImageFixture(false, 0u, true);
|
||||
memory_backed->PlanAndTrack();
|
||||
auto memory_backed_plan = ExtractResourcePlan(memory_backed->program);
|
||||
EliminateDeadCode(memory_backed->program.blocks);
|
||||
std::array<uint32_t, 11> memory_backed_user_data{0x1000u, 224u << 16u, 2u, 0u,
|
||||
0x2000u, 16u << 16u, 4u, 0u,
|
||||
@@ -413,9 +430,12 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
.userdata = &memory,
|
||||
.read_specialization_memory =
|
||||
ReadLinearTestMemory};
|
||||
Check(!MaterializeResources(memory_backed->program, memory_backed_runtime,
|
||||
snapshot) &&
|
||||
same_snapshot(snapshot, prior_snapshot),
|
||||
const auto memory_backed_prior_snapshot = snapshot;
|
||||
const auto memory_backed_prior_specialization = specialization;
|
||||
Check(!MaterializeResources(memory_backed_plan, memory_backed_runtime,
|
||||
snapshot, specialization) &&
|
||||
SameResourceSnapshot(snapshot, memory_backed_prior_snapshot) &&
|
||||
specialization == memory_backed_prior_specialization,
|
||||
"rejected indirect table descriptor read mutated the snapshot");
|
||||
memory.fail_address = UINT64_MAX;
|
||||
|
||||
@@ -728,6 +748,7 @@ void TestDynamicStorageMipTracking() {
|
||||
const auto mip2 = AddStore(12, true, Value(2u));
|
||||
const auto dynamic = AddStore(16, true, fixture.UserData(8));
|
||||
fixture.PlanAndTrack();
|
||||
auto resource_plan = ExtractResourcePlan(fixture.program);
|
||||
|
||||
const auto &images = fixture.program.info.images;
|
||||
Check(images.size() == 2 && images[0].mip_mode == ImageMipMode::None &&
|
||||
@@ -758,15 +779,23 @@ void TestDynamicStorageMipTracking() {
|
||||
<< 28u);
|
||||
descriptor.dwords[5] = 3u << 4u;
|
||||
descriptor.dword_count = 8;
|
||||
std::array<uint32_t, 9> user_data{};
|
||||
std::copy(descriptor.dwords.begin(), descriptor.dwords.end(),
|
||||
user_data.begin());
|
||||
user_data[8] = 2u;
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
snapshot.images.assign(images.size(), descriptor);
|
||||
SpecializeResources(fixture.program, snapshot);
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, snapshot,
|
||||
specialization),
|
||||
"dynamic storage resources did not materialize");
|
||||
ApplyResourceSpecialization(fixture.program, specialization);
|
||||
Check(fixture.program.info.images[1].mip_count == 3 &&
|
||||
ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
snapshot.images.size() == fixture.program.info.images.size(),
|
||||
"base-1 through last-3 dynamic storage range was not specialized");
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program, 0);
|
||||
AllocateBindings(fixture.program);
|
||||
const auto storage_kind = DescriptorBindingForImage(images[0]);
|
||||
Check(storage_kind.has_value(), "storage image has no descriptor binding");
|
||||
const auto *storage_binding =
|
||||
@@ -775,32 +804,58 @@ void TestDynamicStorageMipTracking() {
|
||||
storage_binding->resources == std::vector<uint32_t>({0, 1, 1, 1}),
|
||||
"dynamic storage mip descriptors were not expanded consecutively");
|
||||
|
||||
Program null_program;
|
||||
null_program.resource_tracking_complete = true;
|
||||
ImageResource null_image;
|
||||
null_image.resource_class = ImageResourceClass::Storage;
|
||||
null_image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
null_image.mip_mode = ImageMipMode::DynamicStorage;
|
||||
null_image.written = true;
|
||||
null_program.info.images.push_back(null_image);
|
||||
Fixture null_fixture;
|
||||
const auto null_handle = null_fixture.Image(
|
||||
{Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u)});
|
||||
const auto null_address = null_fixture.Emit(
|
||||
ValueOpcode::MakeImageAddress,
|
||||
{Value(0u), Value(0u), null_fixture.UserData(0), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u)});
|
||||
MemoryInfo null_memory;
|
||||
null_memory.kind = ResourceKind::Image;
|
||||
null_memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||
null_memory.image_address_components = 3u;
|
||||
null_memory.image_has_mip = true;
|
||||
const auto null_data = null_fixture.Emit(
|
||||
ValueOpcode::CompositeConstructU32x4,
|
||||
{Value(1u), Value(2u), Value(3u), Value(4u)});
|
||||
null_fixture.Emit(ValueOpcode::ImageWrite,
|
||||
{null_handle, null_address, null_data, Value(true)},
|
||||
null_fixture.AddMemory(null_memory, 4));
|
||||
null_fixture.PlanAndTrack();
|
||||
auto null_plan = ExtractResourcePlan(null_fixture.program);
|
||||
ResourceSnapshot null_snapshot;
|
||||
DescriptorValue null_descriptor{};
|
||||
null_descriptor.dword_count = 8;
|
||||
null_snapshot.images.push_back(null_descriptor);
|
||||
SpecializeResources(null_program, null_snapshot);
|
||||
Check(null_program.info.images[0].mip_count == 1 &&
|
||||
ValidateResourceSpecialization(null_program, null_snapshot),
|
||||
ResourceSpecialization null_specialization;
|
||||
const std::array<uint32_t, 1> null_user_data{0u};
|
||||
Check(MaterializeResources(null_plan, {.user_data = null_user_data},
|
||||
null_snapshot, null_specialization),
|
||||
"canonical null dynamic storage image did not materialize");
|
||||
ApplyResourceSpecialization(null_fixture.program, null_specialization);
|
||||
Check(null_fixture.program.info.images[0].mip_count == 1 &&
|
||||
null_snapshot.images.size() == 1,
|
||||
"canonical null dynamic storage image did not retain one descriptor");
|
||||
|
||||
snapshot.images[1].dwords[3] =
|
||||
(snapshot.images[1].dwords[3] & ~(0xfu << 16u)) | (2u << 16u);
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"a changed dynamic storage mip count reused the specialization");
|
||||
snapshot.images[1].dwords[3] =
|
||||
(snapshot.images[1].dwords[3] & ~((0xfu << 12u) | (0xfu << 16u))) |
|
||||
auto changed_user_data = user_data;
|
||||
changed_user_data[3] =
|
||||
(changed_user_data[3] & ~(0xfu << 16u)) | (2u << 16u);
|
||||
ResourceSnapshot changed_snapshot;
|
||||
ResourceSpecialization changed_specialization;
|
||||
Check(MaterializeResources(resource_plan, {.user_data = changed_user_data},
|
||||
changed_snapshot, changed_specialization) &&
|
||||
changed_specialization != specialization,
|
||||
"a changed dynamic storage mip count reused the specialization key");
|
||||
changed_user_data[3] =
|
||||
(changed_user_data[3] & ~((0xfu << 12u) | (0xfu << 16u))) |
|
||||
(4u << 12u) | (3u << 16u);
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"an inverted dynamic storage mip range was accepted");
|
||||
const auto valid_snapshot = changed_snapshot;
|
||||
const auto valid_specialization = changed_specialization;
|
||||
Check(!MaterializeResources(resource_plan, {.user_data = changed_user_data},
|
||||
changed_snapshot, changed_specialization) &&
|
||||
SameResourceSnapshot(changed_snapshot, valid_snapshot) &&
|
||||
changed_specialization == valid_specialization,
|
||||
"an inverted dynamic storage mip range was accepted or mutated output");
|
||||
}
|
||||
|
||||
void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
@@ -843,7 +898,7 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
.userdata = &memory};
|
||||
std::vector<DescriptorValue> descriptors;
|
||||
std::vector<uint32_t> flat;
|
||||
const DescriptorSourceRequest request{fixture.program.info.buffers[0].source};
|
||||
const uint32_t request = fixture.program.info.buffers[0].source;
|
||||
Check(EvaluateRuntimeSources(fixture.program, std::span{&request, 1}, runtime,
|
||||
descriptors, flat, {}),
|
||||
"typed runtime source evaluation failed");
|
||||
@@ -863,7 +918,7 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program, 0);
|
||||
AllocateBindings(fixture.program);
|
||||
Check(FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) != nullptr,
|
||||
"flattened typed SRT reads did not receive a binding");
|
||||
@@ -906,7 +961,7 @@ void TestDynamicSrtReadRemainsExplicit() {
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program, 0);
|
||||
AllocateBindings(fixture.program);
|
||||
Check(FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) == nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
@@ -1023,15 +1078,18 @@ void TestDmaAddressMaterialization() {
|
||||
{unbased, Value(0u), Value(0u), Value(9u), Value(true)},
|
||||
fixture.AddMemory(flat, 8));
|
||||
fixture.PlanAndTrack();
|
||||
auto resource_plan = ExtractResourcePlan(fixture.program);
|
||||
|
||||
Check(fixture.program.info.uses_dma,
|
||||
"typed address operations did not enable DMA");
|
||||
std::array<uint32_t, 2> user_data{0x2008u, 0u};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot),
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, snapshot,
|
||||
specialization),
|
||||
"DMA shader resources did not materialize");
|
||||
SpecializeResources(fixture.program, snapshot);
|
||||
ApplyResourceSpecialization(fixture.program, specialization);
|
||||
}
|
||||
|
||||
void TestDynamicFlatAddressesUseDma() {
|
||||
@@ -1053,13 +1111,16 @@ void TestDynamicFlatAddressesUseDma() {
|
||||
fixture.Emit(ValueOpcode::LoadAddressU8, {address, low, high, active},
|
||||
fixture.AddMemory(flat, 0xa4));
|
||||
fixture.PlanAndTrack();
|
||||
auto resource_plan = ExtractResourcePlan(fixture.program);
|
||||
|
||||
Check(fixture.program.info.uses_dma,
|
||||
"exec-masked FLAT address did not enable DMA");
|
||||
std::array<uint32_t, 3> user_data{0x23456780u, 1u, 1u};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot),
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, snapshot,
|
||||
specialization),
|
||||
"exec-masked FLAT shader resources did not materialize");
|
||||
|
||||
Fixture mismatch;
|
||||
@@ -1095,6 +1156,7 @@ void TestBufferSwizzleSpecialization() {
|
||||
{handle, Value(0u), Value(0u), Value(0u), Value(true)},
|
||||
fixture.AddMemory(memory, 4));
|
||||
fixture.PlanAndTrack();
|
||||
auto resource_plan = ExtractResourcePlan(fixture.program);
|
||||
|
||||
constexpr auto swizzle = Libs::Graphics::DstSel(4, 5, 0, 1);
|
||||
std::array<uint32_t, 4> user_data{
|
||||
@@ -1106,16 +1168,22 @@ void TestBufferSwizzleSpecialization() {
|
||||
(1u << 24u)};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot),
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, snapshot,
|
||||
specialization),
|
||||
"buffer resources did not materialize");
|
||||
SpecializeResources(fixture.program, snapshot);
|
||||
ApplyResourceSpecialization(fixture.program, specialization);
|
||||
Check(fixture.program.info.buffers[0].descriptor_swizzle == swizzle &&
|
||||
ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
specialization.buffers[0].descriptor_swizzle == swizzle,
|
||||
"buffer destination selectors were not specialized");
|
||||
|
||||
snapshot.buffers[0].dwords[3] ^= 1u << 9u;
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"buffer swizzle change did not invalidate specialization");
|
||||
user_data[3] ^= 1u << 9u;
|
||||
ResourceSnapshot changed_snapshot;
|
||||
ResourceSpecialization changed_specialization;
|
||||
Check(MaterializeResources(resource_plan, runtime, changed_snapshot,
|
||||
changed_specialization) &&
|
||||
changed_specialization != specialization,
|
||||
"buffer swizzle change did not select a new specialization key");
|
||||
}
|
||||
|
||||
void TestShaderInfoAndBindingLayout() {
|
||||
@@ -1147,15 +1215,14 @@ void TestShaderInfoAndBindingLayout() {
|
||||
StageInputKind::GlobalInvocationId,
|
||||
"typed shader values were not reflected in shader info");
|
||||
|
||||
AllocateBindings(fixture.program, 0);
|
||||
AllocateBindings(fixture.program);
|
||||
Check(FindBinding(fixture.program.bindings, DescriptorBindingKind::Buffers) !=
|
||||
nullptr &&
|
||||
FindBinding(fixture.program.bindings, DescriptorBindingKind::Gds) !=
|
||||
nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::UserData) == nullptr &&
|
||||
fixture.program.bindings.push_constant_size ==
|
||||
fixture.program.bindings.ShaderDataDwords() * sizeof(uint32_t),
|
||||
DescriptorBindingKind::ShaderData) == nullptr &&
|
||||
fixture.program.bindings.UsesPushData(),
|
||||
"typed resources were not assigned native bindings");
|
||||
Check(NativeBinding(ShaderType::Compute, DescriptorBindingKind::Buffers) ==
|
||||
static_cast<uint32_t>(DescriptorBindingKind::Buffers) &&
|
||||
@@ -1180,7 +1247,7 @@ void TestImageBindingAbi() {
|
||||
static_cast<uint32_t>(DescriptorBindingKind::BdaPagetable) == 39u &&
|
||||
static_cast<uint32_t>(DescriptorBindingKind::FaultBuffer) == 40u &&
|
||||
static_cast<uint32_t>(DescriptorBindingKind::FlattenedSrt) == 41u &&
|
||||
static_cast<uint32_t>(DescriptorBindingKind::UserData) == 42u &&
|
||||
static_cast<uint32_t>(DescriptorBindingKind::ShaderData) == 42u &&
|
||||
static_cast<uint32_t>(DescriptorBindingKind::Count) == 43u,
|
||||
"native descriptor binding anchors changed");
|
||||
|
||||
@@ -1284,58 +1351,53 @@ void TestGraphicsPushConstantLayout() {
|
||||
}
|
||||
fixture.program.shader_info_complete = true;
|
||||
};
|
||||
Fixture vertex(ShaderType::Vertex);
|
||||
AddUserData(vertex, 9);
|
||||
AllocateBindings(vertex.program, 0);
|
||||
Check(vertex.program.bindings.push_constant_offset == 0 &&
|
||||
vertex.program.bindings.push_constant_size == 9 * sizeof(uint32_t),
|
||||
"vertex shader was not placed at the start of the graphics push bank");
|
||||
|
||||
const auto pixel_offset = vertex.program.bindings.push_constant_size;
|
||||
uint32_t cursor = 0;
|
||||
Fixture pixel(ShaderType::Pixel);
|
||||
AddUserData(pixel, 4);
|
||||
AllocateBindings(pixel.program, pixel_offset);
|
||||
AllocateBindings(pixel.program, cursor);
|
||||
Check(
|
||||
pixel.program.bindings.push_constant_offset == pixel_offset &&
|
||||
pixel.program.bindings.push_constant_size == 4 * sizeof(uint32_t) &&
|
||||
pixel.program.bindings.UsesPushData() &&
|
||||
pixel.program.bindings.push_data_start_dword == 0 &&
|
||||
FindBinding(pixel.program.bindings,
|
||||
DescriptorBindingKind::UserData) == nullptr,
|
||||
"pixel shader did not follow the vertex data in the graphics push bank");
|
||||
DescriptorBindingKind::ShaderData) == nullptr,
|
||||
"pixel shader did not start the shared push-data block");
|
||||
pixel.program.bindings.AdvancePushData(cursor);
|
||||
|
||||
Fixture vertex(ShaderType::Vertex);
|
||||
AddUserData(vertex, 9);
|
||||
AllocateBindings(vertex.program, cursor);
|
||||
Check(vertex.program.bindings.UsesPushData() &&
|
||||
vertex.program.bindings.push_data_start_dword == 4,
|
||||
"vertex shader did not follow pixel data in the shared push-data block");
|
||||
vertex.program.bindings.AdvancePushData(cursor);
|
||||
Check(cursor == 13, "graphics push-data cursor advanced incorrectly");
|
||||
|
||||
Fixture edge(ShaderType::Pixel);
|
||||
AddUserData(edge, 1);
|
||||
AllocateBindings(edge.program, NativePushConstantSize - sizeof(uint32_t));
|
||||
Check(edge.program.bindings.push_constant_size == sizeof(uint32_t),
|
||||
"last aligned push-constant dword did not fit in the graphics bank");
|
||||
AddUserData(edge, NativePushConstantSize / sizeof(uint32_t));
|
||||
AllocateBindings(edge.program);
|
||||
Check(edge.program.bindings.UsesPushData() &&
|
||||
FindBinding(edge.program.bindings,
|
||||
DescriptorBindingKind::ShaderData) == nullptr,
|
||||
"the full shared push-data block did not fit");
|
||||
|
||||
Fixture spill(ShaderType::Pixel);
|
||||
AddUserData(spill, 32);
|
||||
AllocateBindings(spill.program, pixel_offset);
|
||||
AddUserData(spill, 20);
|
||||
AllocateBindings(spill.program, cursor);
|
||||
Check(
|
||||
spill.program.bindings.push_constant_size == 0 &&
|
||||
!spill.program.bindings.UsesPushData() &&
|
||||
spill.program.bindings.push_data_start_dword == PushData::NoStart &&
|
||||
FindBinding(spill.program.bindings,
|
||||
DescriptorBindingKind::UserData) != nullptr,
|
||||
"pixel shader overlapping the vertex push data did not spill to storage");
|
||||
DescriptorBindingKind::ShaderData) != nullptr,
|
||||
"a stage that exceeded the remaining shared push data did not spill to storage");
|
||||
const auto spill_layout = spill.program.bindings;
|
||||
spill.program.bindings.AdvancePushData(cursor);
|
||||
Check(cursor == 13, "a spilled stage consumed shared push-data space");
|
||||
|
||||
Fixture full(ShaderType::Pixel);
|
||||
AddUserData(full, 1);
|
||||
AllocateBindings(full.program, NativePushConstantSize);
|
||||
Check(full.program.bindings.push_constant_size == 0 &&
|
||||
FindBinding(full.program.bindings,
|
||||
DescriptorBindingKind::UserData) != nullptr,
|
||||
"full graphics push bank did not spill pixel user data to storage");
|
||||
|
||||
Fixture invalid(ShaderType::Pixel);
|
||||
AddUserData(invalid, 1);
|
||||
CheckFatal(
|
||||
[&] {
|
||||
AllocateBindings(invalid.program,
|
||||
NativePushConstantSize + sizeof(uint32_t));
|
||||
},
|
||||
"push-constant offset",
|
||||
"push-constant placement beyond the graphics bank was accepted");
|
||||
Check(!invalid.program.binding_layout_complete,
|
||||
"push-constant placement beyond the graphics bank was accepted");
|
||||
Fixture repeated_spill(ShaderType::Pixel);
|
||||
AddUserData(repeated_spill, 20);
|
||||
AllocateBindings(repeated_spill.program, 20);
|
||||
Check(repeated_spill.program.bindings == spill_layout,
|
||||
"storage fallback retained an irrelevant attempted push-data position");
|
||||
}
|
||||
|
||||
void TestResourceLimitIsTransactional() {
|
||||
|
||||
@@ -473,10 +473,6 @@ struct RenderExecutorTestAccess {
|
||||
}
|
||||
}
|
||||
|
||||
static const auto &PushConstants(const RenderExecutor &executor) {
|
||||
return executor.m_push_constants;
|
||||
}
|
||||
|
||||
static void ResolveRenderDepthTarget(RenderExecutor &executor,
|
||||
uint64_t submit_id,
|
||||
CommandBuffer &buffer,
|
||||
@@ -1041,7 +1037,7 @@ struct GraphicsCase {
|
||||
struct CompiledShader {
|
||||
std::vector<u32> spirv;
|
||||
ShaderRecompiler::IR::Program program;
|
||||
std::vector<u32> flattened_srt;
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
std::vector<u32> packed_user_data;
|
||||
};
|
||||
|
||||
@@ -1094,19 +1090,20 @@ size_t CountText(const std::string &text, const std::string &needle) {
|
||||
void CheckRectListShaders() {
|
||||
constexpr const char *name = "RectListShaders";
|
||||
|
||||
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
program->info.inputs.push_back(
|
||||
ShaderRecompiler::IR::CompiledShaderInfo program{};
|
||||
program.info.inputs.push_back(
|
||||
{ShaderRecompiler::IR::StageInputKind::Parameter, 0, 4, "in_param_0"});
|
||||
program->info.inputs.push_back(
|
||||
program.info.inputs.push_back(
|
||||
{ShaderRecompiler::IR::StageInputKind::Parameter, 1, 4, "in_param_1"});
|
||||
|
||||
ShaderVertexInputInfo vertex{};
|
||||
vertex.param_export_mask = 1u;
|
||||
ShaderRecompiler::IR::CompiledShaderInfo vertex_program{.param_export_mask = 1u};
|
||||
vertex.stage.program = &vertex_program;
|
||||
ShaderPixelInputInfo pixel{};
|
||||
pixel.input_num = 2;
|
||||
pixel.interpolator_settings[0] = 0x400u;
|
||||
pixel.interpolator_settings[1] = 0;
|
||||
pixel.stage.program = program;
|
||||
pixel.stage.program = &program;
|
||||
std::vector<uint32_t> perspective_specialization;
|
||||
BuildStageStaticKey(pixel, perspective_specialization);
|
||||
pixel.ps_no_perspective = true;
|
||||
@@ -1223,14 +1220,28 @@ CompiledShader CompileCase(const TestCase &test) {
|
||||
options.dump_ir = true;
|
||||
options.input_info.compute = &test.compute_info;
|
||||
options.user_data = user_data;
|
||||
options.read_memory = ReadTestMemory;
|
||||
options.read_memory_data = const_cast<std::vector<u32> *>(&test.initial);
|
||||
options.scratch_dwords = test.compute_info.scratch_size_dwords;
|
||||
if (test.has_compute_info) {
|
||||
options.wave_size = test.compute_info.wave_size;
|
||||
}
|
||||
|
||||
auto result = ShaderRecompiler::Recompile(test.code, options);
|
||||
auto translated = ShaderRecompiler::TranslateProgram(test.code, options);
|
||||
auto resource_plan =
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(test.code.data()),
|
||||
.read_memory = ReadTestMemory,
|
||||
.userdata = const_cast<std::vector<u32> *>(&test.initial),
|
||||
};
|
||||
Require(test.name, "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
resource_plan, runtime, resources, specialization),
|
||||
"translated resources could not be materialized");
|
||||
auto result = ShaderRecompiler::CompileProgram(
|
||||
std::move(translated), options, specialization);
|
||||
for (const auto &[text, expected] : test.decoded_counts) {
|
||||
const auto actual = CountText(result.decoded_dump, text);
|
||||
Require(test.name, "decoded RDNA2", actual == expected,
|
||||
@@ -1278,17 +1289,17 @@ CompiledShader CompileCase(const TestCase &test) {
|
||||
}
|
||||
result.program.bindings = {};
|
||||
result.program.binding_layout_complete = false;
|
||||
ShaderRecompiler::IR::AllocateBindings(result.program, 0);
|
||||
ShaderRecompiler::IR::AllocateBindings(result.program);
|
||||
const auto *shader_data = ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::UserData);
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::ShaderData);
|
||||
Require(test.name, "binding layout",
|
||||
result.program.bindings.user_data_registers.size() == 33 &&
|
||||
result.program.bindings.push_constant_size == 0 &&
|
||||
!result.program.bindings.UsesPushData() &&
|
||||
shader_data != nullptr,
|
||||
"oversized shader data did not use its storage fallback");
|
||||
result.spirv = ShaderRecompiler::Spirv::EmitProgram(
|
||||
result.program, result.resources, options.input_info);
|
||||
result.spirv = ShaderRecompiler::Spirv::EmitProgram(result.program,
|
||||
options.input_info);
|
||||
}
|
||||
Require(test.name, "SPIR-V emit", !result.spirv.empty(),
|
||||
"recompiler returned empty SPIR-V");
|
||||
@@ -1297,7 +1308,7 @@ CompiledShader CompileCase(const TestCase &test) {
|
||||
std::vector<u32> packed_user_data;
|
||||
for (const auto reg : result.program.bindings.user_data_registers) {
|
||||
packed_user_data.push_back(
|
||||
result.resources.user_data[reg - result.program.user_data_base]);
|
||||
resources.user_data[reg - result.program.user_data_base]);
|
||||
}
|
||||
packed_user_data.resize(result.program.bindings.ShaderDataDwords());
|
||||
const auto buffer_count =
|
||||
@@ -1313,8 +1324,7 @@ CompiledShader CompileCase(const TestCase &test) {
|
||||
offset << ((i % 4u) * 8u);
|
||||
}
|
||||
return {std::move(result.spirv), std::move(result.program),
|
||||
std::move(result.resources.flattened_srt),
|
||||
std::move(packed_user_data)};
|
||||
std::move(resources), std::move(packed_user_data)};
|
||||
}
|
||||
|
||||
std::array<u32, 64> MakeStructuredStorageBufferData(u32 stride_bytes,
|
||||
@@ -1364,19 +1374,33 @@ CompiledShader CompileFragmentCase(const GraphicsCase &test) {
|
||||
options.input_info.pixel = &pixel_info;
|
||||
options.user_data = user_data;
|
||||
|
||||
auto result = ShaderRecompiler::Recompile(test.fragment_code, options);
|
||||
auto translated =
|
||||
ShaderRecompiler::TranslateProgram(test.fragment_code, options);
|
||||
auto resource_plan =
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(test.fragment_code.data()),
|
||||
};
|
||||
Require(test.name, "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
resource_plan, runtime, resources, specialization),
|
||||
"translated resources could not be materialized");
|
||||
auto result = ShaderRecompiler::CompileProgram(
|
||||
std::move(translated), options, specialization);
|
||||
Require(test.name, "SPIR-V emit", !result.spirv.empty(),
|
||||
"recompiler returned empty SPIR-V");
|
||||
ValidateSpirv(test.name, result.spirv);
|
||||
std::vector<u32> packed_user_data;
|
||||
for (const auto reg : result.program.bindings.user_data_registers) {
|
||||
packed_user_data.push_back(
|
||||
result.resources.user_data[reg - result.program.user_data_base]);
|
||||
resources.user_data[reg - result.program.user_data_base]);
|
||||
}
|
||||
packed_user_data.resize(result.program.bindings.ShaderDataDwords());
|
||||
return {std::move(result.spirv), std::move(result.program),
|
||||
std::move(result.resources.flattened_srt),
|
||||
std::move(packed_user_data)};
|
||||
std::move(resources), std::move(packed_user_data)};
|
||||
}
|
||||
|
||||
std::array<u32, 64> MakeSampledTextureData(Prospero::BufferFormat format) {
|
||||
@@ -1591,7 +1615,7 @@ public:
|
||||
}
|
||||
|
||||
void CheckGraphicsPushConstantBank() {
|
||||
constexpr const char *name = "GraphicsPushConstantBank";
|
||||
constexpr const char *name = "GraphicsPushConstantStages";
|
||||
EnsureRuntimeContext();
|
||||
RenderContext context(m_runtime_context);
|
||||
auto &scheduler = context.GetCommandScheduler();
|
||||
@@ -1600,34 +1624,34 @@ public:
|
||||
HW::Shader shaders{};
|
||||
scheduler.Begin(registers, user_config, shaders);
|
||||
|
||||
ShaderRecompiler::IR::Program vertex_program{};
|
||||
ShaderRecompiler::IR::CompiledShaderInfo vertex_program{};
|
||||
vertex_program.stage = ShaderType::Vertex;
|
||||
vertex_program.bindings.push_constant_size = 2 * sizeof(uint32_t);
|
||||
ShaderRecompiler::IR::Program pixel_program{};
|
||||
vertex_program.bindings.push_data_start_dword = 2;
|
||||
vertex_program.bindings.user_data_registers = {0, 1};
|
||||
vertex_program.bindings.memory_offset_dword = 2;
|
||||
ShaderRecompiler::IR::CompiledShaderInfo pixel_program{};
|
||||
pixel_program.stage = ShaderType::Pixel;
|
||||
pixel_program.bindings.push_constant_offset = 2 * sizeof(uint32_t);
|
||||
pixel_program.bindings.push_constant_size = 2 * sizeof(uint32_t);
|
||||
pixel_program.bindings.push_data_start_dword = 0;
|
||||
pixel_program.bindings.user_data_registers = {0, 1};
|
||||
pixel_program.bindings.memory_offset_dword = 2;
|
||||
ShaderRecompiler::IR::ResourceSnapshot snapshot{};
|
||||
PreparedBindings vertex{};
|
||||
vertex.program = &vertex_program;
|
||||
vertex.snapshot = &snapshot;
|
||||
vertex.user_data = {0x11111111u, 0x22222222u};
|
||||
vertex.shader_data = {0x11111111u, 0x22222222u};
|
||||
PreparedBindings pixel{};
|
||||
pixel.program = &pixel_program;
|
||||
pixel.snapshot = &snapshot;
|
||||
pixel.user_data = {0x33333333u, 0x44444444u};
|
||||
pixel.shader_data = {0x33333333u, 0x44444444u};
|
||||
|
||||
const auto pipeline = RenderExecutorTestAccess::CommitBindings(
|
||||
context.GetRenderExecutor(), scheduler.Current(), vertex, pixel);
|
||||
const auto &bank =
|
||||
RenderExecutorTestAccess::PushConstants(context.GetRenderExecutor());
|
||||
Require(name, "packing",
|
||||
bank[0] == 0x11111111u && bank[1] == 0x22222222u &&
|
||||
bank[2] == 0x33333333u && bank[3] == 0x44444444u &&
|
||||
std::ranges::all_of(bank.begin() + 4, bank.end(),
|
||||
[](uint32_t value) { return value == 0; }) &&
|
||||
vertex.committed && pixel.committed,
|
||||
"graphics stages were not packed into one zero-filled push bank");
|
||||
Require(name, "shared push data",
|
||||
vertex.shader_data ==
|
||||
std::vector<uint32_t>{0x11111111u, 0x22222222u} &&
|
||||
pixel.shader_data ==
|
||||
std::vector<uint32_t>{0x33333333u, 0x44444444u},
|
||||
"graphics stages did not commit their shared push data");
|
||||
scheduler.Finish();
|
||||
RenderExecutorTestAccess::DestroyDescriptorPipelines(
|
||||
context.GetRenderExecutor(), std::span {&pipeline, 1u});
|
||||
@@ -3838,14 +3862,14 @@ public:
|
||||
!texture_cache.IsMetaCleared(write_only_meta, 0),
|
||||
"test HTile entries were not registered in their initial state");
|
||||
|
||||
const auto MakeInput = [](uint64_t address, bool read, bool written) {
|
||||
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
program->stage = ShaderType::Compute;
|
||||
const auto MakeInput = [](uint64_t address, bool read, bool written,
|
||||
ShaderRecompiler::IR::CompiledShaderInfo &program) {
|
||||
program.stage = ShaderType::Compute;
|
||||
ShaderRecompiler::IR::BufferResource resource{};
|
||||
resource.read = read;
|
||||
resource.written = written;
|
||||
resource.formatted = true;
|
||||
program->info.buffers.push_back(resource);
|
||||
program.info.buffers.push_back(resource);
|
||||
|
||||
ShaderBufferResource descriptor{};
|
||||
descriptor.UpdateAddress48(address);
|
||||
@@ -3858,18 +3882,16 @@ public:
|
||||
ShaderRecompiler::IR::DescriptorValue value{};
|
||||
value.dword_count = 4;
|
||||
std::copy_n(descriptor.fields, value.dword_count, value.dwords.begin());
|
||||
auto snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
snapshot->buffers.push_back(value);
|
||||
|
||||
ShaderComputeInputInfo input{};
|
||||
input.stage.program = std::move(program);
|
||||
input.stage.resources = std::move(snapshot);
|
||||
input.stage.program = &program;
|
||||
input.stage.resources.buffers.push_back(value);
|
||||
return input;
|
||||
};
|
||||
auto &executor = context.GetRenderExecutor();
|
||||
auto &command = scheduler.Current();
|
||||
const auto read_only_input = MakeInput(read_only_meta, true, false);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo read_only_program{};
|
||||
const auto read_only_input =
|
||||
MakeInput(read_only_meta, true, false, read_only_program);
|
||||
const bool read_only_consumed =
|
||||
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
|
||||
executor, read_only_input, command);
|
||||
@@ -3878,7 +3900,9 @@ public:
|
||||
!texture_cache.IsMetaCleared(read_only_meta, 0),
|
||||
"a metadata read-only dispatch changed logical clear state");
|
||||
|
||||
const auto read_write_input = MakeInput(read_write_meta, true, true);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo read_write_program{};
|
||||
const auto read_write_input =
|
||||
MakeInput(read_write_meta, true, true, read_write_program);
|
||||
const bool read_write_consumed =
|
||||
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
|
||||
executor, read_write_input, command);
|
||||
@@ -3887,7 +3911,9 @@ public:
|
||||
!texture_cache.IsMetaCleared(read_write_meta, 0),
|
||||
"a metadata read/modify/write dispatch was replaced by a clear");
|
||||
|
||||
const auto write_only_input = MakeInput(write_only_meta, false, true);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo write_only_program{};
|
||||
const auto write_only_input =
|
||||
MakeInput(write_only_meta, false, true, write_only_program);
|
||||
const bool write_only_consumed =
|
||||
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
|
||||
executor, write_only_input, command);
|
||||
@@ -7704,7 +7730,7 @@ public:
|
||||
const auto allocate_bindings =
|
||||
[&](ShaderRecompiler::IR::Program &program) {
|
||||
program.shader_info_complete = true;
|
||||
ShaderRecompiler::IR::AllocateBindings(program, 0);
|
||||
ShaderRecompiler::IR::AllocateBindings(program);
|
||||
};
|
||||
|
||||
constexpr auto stencil_format = Prospero::BufferFormat::k8UInt;
|
||||
@@ -7741,13 +7767,20 @@ public:
|
||||
ShaderRecompiler::IR::DescriptorValue null_descriptor{};
|
||||
null_descriptor.dword_count = 8;
|
||||
null_snapshot.images.assign(3, null_descriptor);
|
||||
ShaderRecompiler::IR::SpecializeResources(null_program, null_snapshot);
|
||||
ShaderRecompiler::IR::ResourceSpecialization null_specialization{};
|
||||
null_specialization.images.resize(3);
|
||||
for (auto &image : null_specialization.images) {
|
||||
image.numeric_class = Prospero::TextureNumericClass::Float;
|
||||
image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
}
|
||||
ShaderRecompiler::IR::ApplyResourceSpecialization(
|
||||
null_program, null_specialization);
|
||||
allocate_bindings(null_program);
|
||||
ShaderStageRuntime null_runtime{
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(null_program)),
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(null_snapshot))};
|
||||
ShaderRecompiler::IR::CompiledShaderInfo null_info{};
|
||||
null_info.stage = null_program.stage;
|
||||
null_info.info = std::move(null_program.info);
|
||||
null_info.bindings = std::move(null_program.bindings);
|
||||
ShaderStageRuntime null_runtime{&null_info, std::move(null_snapshot)};
|
||||
auto null_bindings = executor.PrepareBindings(null_runtime);
|
||||
executor.RebindImages(null_bindings);
|
||||
Require(name, "null descriptor count",
|
||||
@@ -7892,19 +7925,18 @@ public:
|
||||
executor, sampled_overwide_resource,
|
||||
overwide_mipped_storage_descriptor);
|
||||
PreparedBindings mipped_prepared{};
|
||||
auto mipped_program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
mipped_program->info.images.push_back(storage_resource);
|
||||
mipped_program->info.images.push_back(mipped_storage_resource);
|
||||
mipped_program->info.images.push_back(storage_resource);
|
||||
mipped_program->info.images.push_back(sampled_overwide_resource);
|
||||
auto mipped_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
mipped_snapshot->images = {mipped_storage_descriptor,
|
||||
mipped_storage_descriptor,
|
||||
overwide_mipped_storage_descriptor,
|
||||
overwide_mipped_storage_descriptor};
|
||||
mipped_prepared.program = mipped_program.get();
|
||||
mipped_prepared.snapshot = mipped_snapshot.get();
|
||||
ShaderRecompiler::IR::CompiledShaderInfo mipped_program{};
|
||||
mipped_program.info.images.push_back(storage_resource);
|
||||
mipped_program.info.images.push_back(mipped_storage_resource);
|
||||
mipped_program.info.images.push_back(storage_resource);
|
||||
mipped_program.info.images.push_back(sampled_overwide_resource);
|
||||
ShaderRecompiler::IR::ResourceSnapshot mipped_snapshot{};
|
||||
mipped_snapshot.images = {mipped_storage_descriptor,
|
||||
mipped_storage_descriptor,
|
||||
overwide_mipped_storage_descriptor,
|
||||
overwide_mipped_storage_descriptor};
|
||||
mipped_prepared.program = &mipped_program;
|
||||
mipped_prepared.snapshot = &mipped_snapshot;
|
||||
mipped_prepared.resources.images.push_back(
|
||||
std::move(plain_mipped_storage_binding));
|
||||
mipped_prepared.resources.images.push_back(
|
||||
@@ -8094,11 +8126,12 @@ public:
|
||||
|
||||
ShaderRecompiler::IR::ResourceSnapshot storage_snapshot{};
|
||||
storage_snapshot.images.push_back(storage_descriptor);
|
||||
ShaderStageRuntime storage_runtime{
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(storage_program)),
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(storage_snapshot))};
|
||||
ShaderRecompiler::IR::CompiledShaderInfo storage_info{};
|
||||
storage_info.stage = storage_program.stage;
|
||||
storage_info.info = std::move(storage_program.info);
|
||||
storage_info.bindings = std::move(storage_program.bindings);
|
||||
ShaderStageRuntime storage_runtime{&storage_info,
|
||||
std::move(storage_snapshot)};
|
||||
auto storage_discovery = executor.PrepareBindings(storage_runtime);
|
||||
const auto storage_id = storage_discovery.resources.images[0].image_id;
|
||||
Require(name, "storage prefetch purity",
|
||||
@@ -8124,11 +8157,12 @@ public:
|
||||
allocate_bindings(sampled_program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot sampled_snapshot{};
|
||||
sampled_snapshot.images.push_back(storage_descriptor);
|
||||
ShaderStageRuntime sampled_runtime{
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(sampled_program)),
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(sampled_snapshot))};
|
||||
ShaderRecompiler::IR::CompiledShaderInfo sampled_info{};
|
||||
sampled_info.stage = sampled_program.stage;
|
||||
sampled_info.info = std::move(sampled_program.info);
|
||||
sampled_info.bindings = std::move(sampled_program.bindings);
|
||||
ShaderStageRuntime sampled_runtime{&sampled_info,
|
||||
std::move(sampled_snapshot)};
|
||||
|
||||
constexpr uint64_t ordered_sampled_address = base + 0x10000;
|
||||
const uint32_t ordered_sampled_value = 0x89abcdefu;
|
||||
@@ -8148,10 +8182,8 @@ public:
|
||||
ordered_descriptor.dword_count = 8;
|
||||
ShaderRecompiler::IR::ResourceSnapshot ordered_snapshot{};
|
||||
ordered_snapshot.images.push_back(ordered_descriptor);
|
||||
ShaderStageRuntime ordered_sampled_runtime{
|
||||
sampled_runtime.program,
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(ordered_snapshot))};
|
||||
ShaderStageRuntime ordered_sampled_runtime{sampled_runtime.program,
|
||||
std::move(ordered_snapshot)};
|
||||
auto ordered_bindings = RenderExecutorTestAccess::PrepareGraphicsBindings(
|
||||
executor, storage_runtime, ordered_sampled_runtime, true);
|
||||
const auto ordered_sampled_id =
|
||||
@@ -8203,21 +8235,28 @@ public:
|
||||
"a later sampled alias dropped an earlier storage write access");
|
||||
RenderExecutorTestAccess::ResetBindings(executor);
|
||||
|
||||
auto vertex_sampled_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
vertex_sampled_program->stage = ShaderType::Vertex;
|
||||
vertex_sampled_program->resource_tracking_complete = true;
|
||||
vertex_sampled_program->info.images.push_back(sampled_resource);
|
||||
allocate_bindings(*vertex_sampled_program);
|
||||
ShaderRecompiler::IR::Program vertex_sampled_program{};
|
||||
vertex_sampled_program.stage = ShaderType::Vertex;
|
||||
vertex_sampled_program.resource_tracking_complete = true;
|
||||
vertex_sampled_program.info.images.push_back(sampled_resource);
|
||||
allocate_bindings(vertex_sampled_program);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo vertex_sampled_info{};
|
||||
vertex_sampled_info.stage = vertex_sampled_program.stage;
|
||||
vertex_sampled_info.info = std::move(vertex_sampled_program.info);
|
||||
vertex_sampled_info.bindings =
|
||||
std::move(vertex_sampled_program.bindings);
|
||||
ShaderStageRuntime vertex_sampled_runtime{
|
||||
std::move(vertex_sampled_program), sampled_runtime.resources};
|
||||
auto pixel_storage_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
pixel_storage_program->stage = ShaderType::Pixel;
|
||||
pixel_storage_program->resource_tracking_complete = true;
|
||||
pixel_storage_program->info.images.push_back(storage_resource);
|
||||
allocate_bindings(*pixel_storage_program);
|
||||
ShaderStageRuntime pixel_storage_runtime{std::move(pixel_storage_program),
|
||||
&vertex_sampled_info, sampled_runtime.resources};
|
||||
ShaderRecompiler::IR::Program pixel_storage_program{};
|
||||
pixel_storage_program.stage = ShaderType::Pixel;
|
||||
pixel_storage_program.resource_tracking_complete = true;
|
||||
pixel_storage_program.info.images.push_back(storage_resource);
|
||||
allocate_bindings(pixel_storage_program);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo pixel_storage_info{};
|
||||
pixel_storage_info.stage = pixel_storage_program.stage;
|
||||
pixel_storage_info.info = std::move(pixel_storage_program.info);
|
||||
pixel_storage_info.bindings = std::move(pixel_storage_program.bindings);
|
||||
ShaderStageRuntime pixel_storage_runtime{&pixel_storage_info,
|
||||
storage_runtime.resources};
|
||||
auto writable_alias_bindings =
|
||||
RenderExecutorTestAccess::PrepareGraphicsBindings(
|
||||
@@ -8286,16 +8325,19 @@ public:
|
||||
split_sampled_desc.view_info.level_count = 1;
|
||||
split_sampled_desc.view_info.usage = vk::ImageUsageFlagBits::eSampled;
|
||||
|
||||
auto split_program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
split_program->stage = ShaderType::Vertex;
|
||||
split_program->resource_tracking_complete = true;
|
||||
split_program->info.images = {storage_resource, sampled_resource};
|
||||
allocate_bindings(*split_program);
|
||||
ShaderRecompiler::IR::Program split_ir{};
|
||||
split_ir.stage = ShaderType::Vertex;
|
||||
split_ir.resource_tracking_complete = true;
|
||||
split_ir.info.images = {storage_resource, sampled_resource};
|
||||
allocate_bindings(split_ir);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo split_program{};
|
||||
split_program.stage = split_ir.stage;
|
||||
split_program.info = std::move(split_ir.info);
|
||||
split_program.bindings = std::move(split_ir.bindings);
|
||||
PreparedBindings split_bindings{};
|
||||
auto split_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
split_bindings.program = split_program.get();
|
||||
split_bindings.snapshot = split_snapshot.get();
|
||||
ShaderRecompiler::IR::ResourceSnapshot split_snapshot{};
|
||||
split_bindings.program = &split_program;
|
||||
split_bindings.snapshot = &split_snapshot;
|
||||
split_bindings.resources.images.push_back(
|
||||
{split_id, texture_cache.FindTexture(split_id, split_storage_desc),
|
||||
split_storage_desc});
|
||||
@@ -8670,9 +8712,8 @@ public:
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor2DArray) << 28u);
|
||||
array_texture.fields[4] = 1;
|
||||
|
||||
ShaderRecompiler::IR::Program array_program{};
|
||||
ShaderRecompiler::IR::CompiledShaderInfo array_program{};
|
||||
array_program.stage = ShaderType::Compute;
|
||||
array_program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource array_resource{};
|
||||
array_resource.resource_class =
|
||||
ShaderRecompiler::IR::ImageResourceClass::Sampled;
|
||||
@@ -8688,11 +8729,8 @@ public:
|
||||
array_descriptor.dword_count = 8;
|
||||
ShaderRecompiler::IR::ResourceSnapshot array_snapshot{};
|
||||
array_snapshot.images.push_back(array_descriptor);
|
||||
ShaderStageRuntime array_runtime{
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(array_program)),
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(array_snapshot))};
|
||||
ShaderStageRuntime array_runtime{&array_program,
|
||||
std::move(array_snapshot)};
|
||||
|
||||
auto array_binding = executor.PrepareBindings(array_runtime);
|
||||
executor.RebindImages(array_binding);
|
||||
@@ -8756,20 +8794,16 @@ public:
|
||||
std::end(colliding_msaa_texture.fields),
|
||||
colliding_msaa_descriptor.dwords.begin());
|
||||
colliding_msaa_descriptor.dword_count = 8;
|
||||
auto colliding_msaa_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
colliding_msaa_program->stage = ShaderType::Compute;
|
||||
colliding_msaa_program->resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::CompiledShaderInfo colliding_msaa_program{};
|
||||
colliding_msaa_program.stage = ShaderType::Compute;
|
||||
auto colliding_msaa_resource = array_resource;
|
||||
colliding_msaa_resource.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
|
||||
colliding_msaa_program->info.images.push_back(colliding_msaa_resource);
|
||||
auto colliding_msaa_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
colliding_msaa_snapshot->images.push_back(colliding_msaa_descriptor);
|
||||
colliding_msaa_program.info.images.push_back(colliding_msaa_resource);
|
||||
ShaderRecompiler::IR::ResourceSnapshot colliding_msaa_snapshot{};
|
||||
colliding_msaa_snapshot.images.push_back(colliding_msaa_descriptor);
|
||||
ShaderStageRuntime colliding_msaa_runtime{
|
||||
std::move(colliding_msaa_program),
|
||||
std::move(colliding_msaa_snapshot)};
|
||||
&colliding_msaa_program, std::move(colliding_msaa_snapshot)};
|
||||
auto colliding_msaa_binding =
|
||||
executor.PrepareBindings(colliding_msaa_runtime);
|
||||
executor.RebindImages(colliding_msaa_binding);
|
||||
@@ -8820,18 +8854,15 @@ public:
|
||||
std::copy(std::begin(msaa_texture.fields), std::end(msaa_texture.fields),
|
||||
msaa_descriptor.dwords.begin());
|
||||
msaa_descriptor.dword_count = 8;
|
||||
auto msaa_program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
msaa_program->stage = ShaderType::Compute;
|
||||
msaa_program->resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::CompiledShaderInfo msaa_program{};
|
||||
msaa_program.stage = ShaderType::Compute;
|
||||
auto msaa_resource = array_resource;
|
||||
msaa_resource.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
|
||||
msaa_program->info.images.push_back(msaa_resource);
|
||||
auto msaa_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
msaa_snapshot->images.push_back(msaa_descriptor);
|
||||
ShaderStageRuntime msaa_runtime{std::move(msaa_program),
|
||||
std::move(msaa_snapshot)};
|
||||
msaa_program.info.images.push_back(msaa_resource);
|
||||
ShaderRecompiler::IR::ResourceSnapshot msaa_snapshot{};
|
||||
msaa_snapshot.images.push_back(msaa_descriptor);
|
||||
ShaderStageRuntime msaa_runtime{&msaa_program, std::move(msaa_snapshot)};
|
||||
auto msaa_binding = executor.PrepareBindings(msaa_runtime);
|
||||
executor.RebindImages(msaa_binding);
|
||||
const auto &resolved_msaa = msaa_binding.resources.images[0];
|
||||
@@ -8862,18 +8893,15 @@ public:
|
||||
std::end(msaa_array_texture.fields),
|
||||
msaa_array_descriptor.dwords.begin());
|
||||
msaa_array_descriptor.dword_count = 8;
|
||||
auto msaa_array_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
msaa_array_program->stage = ShaderType::Compute;
|
||||
msaa_array_program->resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::CompiledShaderInfo msaa_array_program{};
|
||||
msaa_array_program.stage = ShaderType::Compute;
|
||||
auto msaa_array_resource = array_resource;
|
||||
msaa_array_resource.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray;
|
||||
msaa_array_program->info.images.push_back(msaa_array_resource);
|
||||
auto msaa_array_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
msaa_array_snapshot->images.push_back(msaa_array_descriptor);
|
||||
ShaderStageRuntime msaa_array_runtime{std::move(msaa_array_program),
|
||||
msaa_array_program.info.images.push_back(msaa_array_resource);
|
||||
ShaderRecompiler::IR::ResourceSnapshot msaa_array_snapshot{};
|
||||
msaa_array_snapshot.images.push_back(msaa_array_descriptor);
|
||||
ShaderStageRuntime msaa_array_runtime{&msaa_array_program,
|
||||
std::move(msaa_array_snapshot)};
|
||||
auto msaa_array_binding = executor.PrepareBindings(msaa_array_runtime);
|
||||
executor.RebindImages(msaa_array_binding);
|
||||
@@ -9089,9 +9117,8 @@ public:
|
||||
DstSel(4, 5, 6, 7) | (static_cast<uint32_t>(linear) << 20u) |
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor2D) << 28u);
|
||||
|
||||
ShaderRecompiler::IR::Program program{};
|
||||
ShaderRecompiler::IR::CompiledShaderInfo program{};
|
||||
program.stage = ShaderType::Compute;
|
||||
program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource stencil_resource{};
|
||||
stencil_resource.resource_class =
|
||||
ShaderRecompiler::IR::ImageResourceClass::Sampled;
|
||||
@@ -9107,11 +9134,7 @@ public:
|
||||
descriptor.dwords.begin());
|
||||
descriptor.dword_count = 8;
|
||||
snapshot.images.push_back(descriptor);
|
||||
ShaderStageRuntime runtime{
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(program)),
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(snapshot))};
|
||||
ShaderStageRuntime runtime{&program, std::move(snapshot)};
|
||||
|
||||
auto prepared = context.GetRenderExecutor().PrepareBindings(runtime);
|
||||
const auto sampled_stencil_id = prepared.resources.images[0].image_id;
|
||||
@@ -9163,16 +9186,14 @@ public:
|
||||
"RebindImages did not acquire the associated depth owner");
|
||||
RenderExecutorTestAccess::ResetBindings(executor);
|
||||
|
||||
auto stencil_storage_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
stencil_storage_program->stage = ShaderType::Compute;
|
||||
stencil_storage_program->resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::CompiledShaderInfo stencil_storage_program{};
|
||||
stencil_storage_program.stage = ShaderType::Compute;
|
||||
auto stencil_storage_resource = stencil_resource;
|
||||
stencil_storage_resource.resource_class =
|
||||
ShaderRecompiler::IR::ImageResourceClass::Storage;
|
||||
stencil_storage_resource.read = false;
|
||||
stencil_storage_resource.written = true;
|
||||
stencil_storage_program->info.images.push_back(stencil_storage_resource);
|
||||
stencil_storage_program.info.images.push_back(stencil_storage_resource);
|
||||
auto storage_stencil = stencil;
|
||||
storage_stencil.fields[5] = 0x00700000u;
|
||||
ShaderRecompiler::IR::DescriptorValue stencil_storage_descriptor{};
|
||||
@@ -9183,9 +9204,7 @@ public:
|
||||
ShaderRecompiler::IR::ResourceSnapshot stencil_storage_snapshot{};
|
||||
stencil_storage_snapshot.images.push_back(stencil_storage_descriptor);
|
||||
ShaderStageRuntime stencil_storage_runtime{
|
||||
std::move(stencil_storage_program),
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
std::move(stencil_storage_snapshot))};
|
||||
&stencil_storage_program, std::move(stencil_storage_snapshot)};
|
||||
auto storage_redirected =
|
||||
executor.PrepareBindings(stencil_storage_runtime);
|
||||
executor.RebindImages(storage_redirected);
|
||||
@@ -9600,10 +9619,10 @@ public:
|
||||
pipeline_layout_info.setLayoutCount = 1;
|
||||
pipeline_layout_info.pSetLayouts = &descriptor_layout;
|
||||
vk::PushConstantRange push_range{};
|
||||
if (layout.push_constant_size != 0) {
|
||||
if (layout.UsesPushData()) {
|
||||
push_range.stageFlags = vk::ShaderStageFlagBits::eCompute;
|
||||
push_range.offset = 0;
|
||||
push_range.size = layout.push_constant_size;
|
||||
push_range.size = ShaderRecompiler::IR::NativePushConstantSize;
|
||||
pipeline_layout_info.pushConstantRangeCount = 1;
|
||||
pipeline_layout_info.pPushConstantRanges = &push_range;
|
||||
}
|
||||
@@ -9736,8 +9755,9 @@ public:
|
||||
}
|
||||
if (const auto *flattened = Binding(Kind::FlattenedSrt);
|
||||
flattened != nullptr) {
|
||||
flattened_buffer = CreateStorageBuffer(test.name, compiled.flattened_srt,
|
||||
compiled.flattened_srt.size());
|
||||
flattened_buffer =
|
||||
CreateStorageBuffer(test.name, compiled.resources.flattened_srt,
|
||||
compiled.resources.flattened_srt.size());
|
||||
flattened_info = {flattened_buffer.buffer, 0, flattened_buffer.size};
|
||||
vk::WriteDescriptorSet write{};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
@@ -9748,7 +9768,7 @@ public:
|
||||
write.pBufferInfo = &flattened_info;
|
||||
writes.push_back(write);
|
||||
}
|
||||
if (const auto *user = Binding(Kind::UserData); user != nullptr) {
|
||||
if (const auto *user = Binding(Kind::ShaderData); user != nullptr) {
|
||||
user_data_buffer =
|
||||
CreateStorageBuffer(test.name, compiled.packed_user_data,
|
||||
compiled.packed_user_data.size());
|
||||
@@ -9756,7 +9776,7 @@ public:
|
||||
vk::WriteDescriptorSet write{};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstSet = descriptor_set;
|
||||
write.dstBinding = Native(Kind::UserData);
|
||||
write.dstBinding = Native(Kind::ShaderData);
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
write.pBufferInfo = &user_data_info;
|
||||
@@ -9917,14 +9937,13 @@ public:
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline);
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeline_layout, 0,
|
||||
1, &descriptor_set, 0, nullptr);
|
||||
if (layout.push_constant_size != 0) {
|
||||
Require(test.name, "dispatch",
|
||||
compiled.packed_user_data.size() * sizeof(u32) ==
|
||||
layout.push_constant_size,
|
||||
"native user-data size does not match push-constant range");
|
||||
if (layout.UsesPushData()) {
|
||||
ShaderRecompiler::IR::PushData push_data;
|
||||
std::copy(compiled.packed_user_data.begin(),
|
||||
compiled.packed_user_data.end(),
|
||||
push_data.dwords.begin() + layout.push_data_start_dword);
|
||||
cmd.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0,
|
||||
layout.push_constant_size,
|
||||
compiled.packed_user_data.data());
|
||||
sizeof(push_data), push_data.dwords.data());
|
||||
}
|
||||
cmd.dispatch(test.dispatch_x, test.dispatch_y, test.dispatch_z);
|
||||
|
||||
@@ -10011,14 +10030,14 @@ public:
|
||||
|
||||
const auto &fragment_bind = fragment.program.bindings;
|
||||
vk::PushConstantRange push_constant_range{};
|
||||
if (fragment_bind.push_constant_size > 0) {
|
||||
if (fragment_bind.UsesPushData()) {
|
||||
Require(test.name, "graphics",
|
||||
test.push_constants.size() * sizeof(u32) ==
|
||||
fragment_bind.push_constant_size,
|
||||
fragment_bind.ShaderDataDwords() * sizeof(u32),
|
||||
"fragment push constant data size does not match reflection");
|
||||
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eFragment;
|
||||
push_constant_range.offset = 0;
|
||||
push_constant_range.size = fragment_bind.push_constant_size;
|
||||
push_constant_range.size = ShaderRecompiler::IR::NativePushConstantSize;
|
||||
}
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_info{};
|
||||
@@ -10144,9 +10163,11 @@ public:
|
||||
cmd.beginRendering(rendering);
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
if (push_constant_range.size != 0) {
|
||||
ShaderRecompiler::IR::PushData push_data;
|
||||
std::copy(test.push_constants.begin(), test.push_constants.end(),
|
||||
push_data.dwords.begin() + fragment_bind.push_data_start_dword);
|
||||
cmd.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0,
|
||||
fragment_bind.push_constant_size,
|
||||
test.push_constants.data());
|
||||
sizeof(push_data), push_data.dwords.data());
|
||||
}
|
||||
vk::DeviceSize offset = 0;
|
||||
cmd.bindVertexBuffers(0, 1, &vertex_buffer.buffer, &offset);
|
||||
@@ -20500,41 +20521,19 @@ void CheckIndirectImageKeySwitch() {
|
||||
root.read = true;
|
||||
root.indirect_root = 0;
|
||||
root.indirect_mapping_offset = 0;
|
||||
root.indirect_mapping_capacity = mapping_capacity;
|
||||
root.indirect_search_iterations = std::bit_width(mapping_capacity);
|
||||
root.indirect_resources = {0u, 1u};
|
||||
auto candidate = root;
|
||||
candidate.indirect_mapping_capacity = 0;
|
||||
candidate.indirect_search_iterations = 0;
|
||||
candidate.indirect_resources.clear();
|
||||
program.info.images = {root, candidate};
|
||||
program.info.samplers.push_back({1u, 0x10f0u});
|
||||
program.info.sampled_pairs.push_back({0u, 0u, 0x10f0u});
|
||||
|
||||
AllocateBindings(program, 0);
|
||||
ResourceSnapshot snapshot{};
|
||||
DescriptorValue descriptor{};
|
||||
descriptor.dword_count = 8;
|
||||
descriptor.dwords[0] = 0x20u;
|
||||
descriptor.dwords[1] =
|
||||
static_cast<uint32_t>(Prospero::BufferFormat::k32_32_32_32Float) << 20u;
|
||||
descriptor.dwords[2] = 3u | (3u << 14u);
|
||||
descriptor.dwords[3] =
|
||||
DstSel(4, 5, 6, 7) |
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor2D) << 28u);
|
||||
snapshot.images = {descriptor, descriptor};
|
||||
snapshot.images[1].dwords[0] = 0x40u;
|
||||
snapshot.flattened_srt.resize(1u + mapping_capacity * 2u);
|
||||
snapshot.flattened_srt[0] = 2u;
|
||||
snapshot.flattened_srt[1] = 0u;
|
||||
snapshot.flattened_srt[2] = 0u;
|
||||
snapshot.flattened_srt[3] = 1u;
|
||||
snapshot.flattened_srt[4] = 1u;
|
||||
DescriptorValue sampler_descriptor{};
|
||||
sampler_descriptor.dword_count = 4;
|
||||
snapshot.samplers.push_back(sampler_descriptor);
|
||||
|
||||
AllocateBindings(program);
|
||||
ShaderComputeInputInfo compute{};
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program, snapshot,
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program,
|
||||
{.compute = &compute});
|
||||
ValidateSpirv(name, spirv);
|
||||
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2);
|
||||
@@ -21631,19 +21630,40 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
|
||||
options.user_data = user_data;
|
||||
options.input_info.compute = &compute;
|
||||
options.dump_ir = false;
|
||||
auto result = ShaderRecompiler::Recompile(code, options);
|
||||
auto translated = ShaderRecompiler::TranslateProgram(code, options);
|
||||
auto resource_plan =
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(code.data()),
|
||||
};
|
||||
Require("Ps5GameExampleImageClear", "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
resource_plan, runtime, resources, specialization),
|
||||
"runtime clear resources could not be materialized");
|
||||
auto result = ShaderRecompiler::CompileProgram(
|
||||
std::move(translated), options, specialization);
|
||||
ValidateSpirv("Ps5GameExampleImageClear", result.spirv);
|
||||
return result;
|
||||
return CompiledShader{std::move(result.spirv), std::move(result.program),
|
||||
std::move(resources), {}};
|
||||
};
|
||||
|
||||
const auto code = MakeCode();
|
||||
auto positive = Compile(code);
|
||||
|
||||
compute.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(positive.program));
|
||||
compute.stage.resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
positive.resources);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo program{};
|
||||
program.stage = positive.program.stage;
|
||||
program.shader_hash = positive.program.shader_hash;
|
||||
program.wave_size = positive.program.wave_size;
|
||||
program.user_data_base = positive.program.user_data_base;
|
||||
program.user_data_count = positive.program.user_data_count;
|
||||
program.scratch_dwords = positive.program.scratch_dwords;
|
||||
program.info = std::move(positive.program.info);
|
||||
program.bindings = std::move(positive.program.bindings);
|
||||
compute.stage.program = &program;
|
||||
compute.stage.resources = positive.resources;
|
||||
ShaderBufferResource descriptor{};
|
||||
u32 packed_clear = 0;
|
||||
uint64_t size = 0;
|
||||
@@ -21656,16 +21676,12 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
|
||||
|
||||
auto non_repeated = positive.resources;
|
||||
non_repeated.user_data[7] ^= 1u;
|
||||
compute.stage.resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
non_repeated);
|
||||
compute.stage.resources = non_repeated;
|
||||
Require("Ps5GameExampleImageClear", "non-repeated clear",
|
||||
!ResolveComputeImageClear(compute, 64, 1, 1, 0x61u, descriptor,
|
||||
packed_clear, size),
|
||||
"non-uniform uint4 data was replaced with a color clear");
|
||||
compute.stage.resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
positive.resources);
|
||||
compute.stage.resources = positive.resources;
|
||||
compute.dispatch_threads_num[0] = 32;
|
||||
Require("Ps5GameExampleImageClear", "partial dispatch",
|
||||
!ResolveComputeImageClear(compute, 32, 1, 1, 0x61u, descriptor,
|
||||
@@ -21727,25 +21743,37 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
options.user_data = user_data;
|
||||
options.input_info.vertex = &vertex;
|
||||
|
||||
auto result = ShaderRecompiler::Recompile(code, options);
|
||||
auto translated = ShaderRecompiler::TranslateProgram(code, options);
|
||||
auto resource_plan =
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(code.data()),
|
||||
};
|
||||
Require(name, "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
resource_plan, runtime, resources, specialization),
|
||||
"embedded fetch resources could not be materialized");
|
||||
auto result = ShaderRecompiler::CompileProgram(
|
||||
std::move(translated), options, specialization);
|
||||
Require(name, "fetch rewrite",
|
||||
result.program.info.vertex_fetch_components[0] == 4,
|
||||
"encoded fetch sequence was not recognized and rewritten");
|
||||
return result;
|
||||
return CompiledShader{std::move(result.spirv), std::move(result.program),
|
||||
std::move(resources), {}};
|
||||
};
|
||||
|
||||
const auto Resolve = [](const ShaderRecompiler::CompileResult &result,
|
||||
const auto Resolve = [](const CompiledShader &result,
|
||||
u32 index_offset) {
|
||||
ShaderVertexInputInfo vertex;
|
||||
vertex.fetch_embedded = true;
|
||||
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
program->user_data_base = result.program.user_data_base;
|
||||
program->info.vertex_offset_sgpr =
|
||||
result.program.info.vertex_offset_sgpr;
|
||||
vertex.stage.program = std::move(program);
|
||||
vertex.stage.resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
result.resources);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo program{};
|
||||
program.user_data_base = result.program.user_data_base;
|
||||
program.info.vertex_offset_sgpr = result.program.info.vertex_offset_sgpr;
|
||||
vertex.stage.program = &program;
|
||||
vertex.stage.resources = result.resources;
|
||||
return ResolveVertexOffset(index_offset, vertex);
|
||||
};
|
||||
|
||||
@@ -23700,15 +23728,34 @@ void CheckImageSamplerSpecialization() {
|
||||
signed_image_descriptor.dwords[1] =
|
||||
static_cast<uint32_t>(Prospero::BufferFormat::k32SInt) << 20u;
|
||||
|
||||
ShaderRecompiler::IR::ResourceSnapshot mixed_sampler_snapshot;
|
||||
mixed_sampler_snapshot.images = {native_image_descriptor,
|
||||
packed_image_descriptor,
|
||||
signed_image_descriptor};
|
||||
ShaderRecompiler::IR::DescriptorValue sampler_descriptor{};
|
||||
sampler_descriptor.dword_count = 4;
|
||||
mixed_sampler_snapshot.samplers.push_back(sampler_descriptor);
|
||||
ShaderRecompiler::IR::SpecializeResources(mixed_sampler_program,
|
||||
mixed_sampler_snapshot);
|
||||
const std::array descriptors{native_image_descriptor, packed_image_descriptor,
|
||||
signed_image_descriptor, sampler_descriptor};
|
||||
mixed_sampler_program.descriptor_sources.resize(descriptors.size());
|
||||
for (u32 source = 0; source < descriptors.size(); source++) {
|
||||
auto &destination = mixed_sampler_program.descriptor_sources[source];
|
||||
destination.dword_count = descriptors[source].dword_count;
|
||||
for (u32 dword = 0; dword < destination.dword_count; dword++) {
|
||||
destination.dwords[dword] = Value(descriptors[source].dwords[dword]);
|
||||
}
|
||||
}
|
||||
for (u32 image = 0; image < mixed_sampler_program.info.images.size(); image++) {
|
||||
mixed_sampler_program.info.images[image].source = image;
|
||||
}
|
||||
mixed_sampler_program.info.samplers[0].source = 3;
|
||||
mixed_sampler_program.srt_plan_complete = true;
|
||||
auto mixed_sampler_plan =
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(mixed_sampler_program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot mixed_sampler_snapshot;
|
||||
ShaderRecompiler::IR::ResourceSpecialization mixed_sampler_specialization;
|
||||
Require("ImageSamplerSpecialization", "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
mixed_sampler_plan, {}, mixed_sampler_snapshot,
|
||||
mixed_sampler_specialization),
|
||||
"mixed sampler descriptors could not be materialized");
|
||||
ShaderRecompiler::IR::ApplyResourceSpecialization(
|
||||
mixed_sampler_program, mixed_sampler_specialization);
|
||||
Require("ImageSamplerSpecialization", "mixed sampler variant",
|
||||
mixed_sampler_program.info.samplers.size() == 2u &&
|
||||
!mixed_sampler_program.info.samplers[0].force_point_filtering &&
|
||||
@@ -23753,7 +23800,7 @@ void CheckNativeImageDescriptorTypes() {
|
||||
vk::DescriptorType::eStorageBuffer &&
|
||||
NativeDescriptorType(DescriptorBindingKind::FlattenedSrt) ==
|
||||
vk::DescriptorType::eStorageBuffer &&
|
||||
NativeDescriptorType(DescriptorBindingKind::UserData) ==
|
||||
NativeDescriptorType(DescriptorBindingKind::ShaderData) ==
|
||||
vk::DescriptorType::eStorageBuffer,
|
||||
"non-image binding has the wrong Vulkan descriptor type");
|
||||
std::printf("[host] %-32s ok\n", "NativeImageDescriptorTypes");
|
||||
@@ -23771,7 +23818,7 @@ void CheckShaderRecompilerFatalContracts() {
|
||||
(void)CompileCase(test);
|
||||
});
|
||||
|
||||
ExpectFatal("PackedAtomicImageRejection", [] {
|
||||
{
|
||||
ShaderRecompiler::IR::Program program;
|
||||
program.resource_tracking_complete = true;
|
||||
|
||||
@@ -23794,10 +23841,23 @@ void CheckShaderRecompilerFatalContracts() {
|
||||
DstSel(4, 5, 6, 7) |
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor2D) << 28u);
|
||||
|
||||
image.source = 0;
|
||||
program.info.images[0].source = 0;
|
||||
program.descriptor_sources.resize(1);
|
||||
program.descriptor_sources[0].dword_count = descriptor.dword_count;
|
||||
for (u32 dword = 0; dword < descriptor.dword_count; dword++) {
|
||||
program.descriptor_sources[0].dwords[dword] =
|
||||
ShaderRecompiler::IR::Value(descriptor.dwords[dword]);
|
||||
}
|
||||
program.srt_plan_complete = true;
|
||||
auto plan = ShaderRecompiler::IR::ExtractResourcePlan(program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot snapshot;
|
||||
snapshot.images.push_back(descriptor);
|
||||
ShaderRecompiler::IR::SpecializeResources(program, snapshot);
|
||||
});
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
Require("PackedAtomicImageRejection", "resource materialization",
|
||||
!ShaderRecompiler::IR::MaterializeResources(
|
||||
plan, {}, snapshot, specialization),
|
||||
"packed atomic image descriptor was accepted");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24520,7 +24580,21 @@ void CheckEmbeddedFetchLaneSpill() {
|
||||
options.user_data = user_data;
|
||||
options.input_info.vertex = &vertex;
|
||||
|
||||
auto result = ShaderRecompiler::Recompile(code, options);
|
||||
auto translated = ShaderRecompiler::TranslateProgram(code, options);
|
||||
auto resource_plan =
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(code.data()),
|
||||
};
|
||||
Require("EmbeddedFetchLaneSpill", "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
resource_plan, runtime, resources, specialization),
|
||||
"lane-spilled fetch resources could not be materialized");
|
||||
auto result = ShaderRecompiler::CompileProgram(
|
||||
std::move(translated), options, specialization);
|
||||
Require("EmbeddedFetchLaneSpill", "fetch rewrite",
|
||||
result.program.info.vertex_fetch_components[0] == 4,
|
||||
"lane-spilled fetch descriptor was not recognized and rewritten");
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
|
||||
void Check(bool value, const char *text) {
|
||||
if (!value) {
|
||||
std::fprintf(stderr, "ShaderStageRuntimeTests: failed: %s\n", text);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool RejectSpecializationRead(void *userdata, uint64_t, uint32_t *) {
|
||||
++*static_cast<uint32_t *>(userdata);
|
||||
return false;
|
||||
}
|
||||
|
||||
Libs::Graphics::ShaderRecompiler::IR::Block &
|
||||
AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
auto block = std::make_unique<Block>();
|
||||
auto *result = block.get();
|
||||
program.blocks.push_back(result);
|
||||
program.block_info.push_back({.id = 0});
|
||||
program.block_storage.push_back(std::move(block));
|
||||
return *result;
|
||||
}
|
||||
|
||||
std::shared_ptr<const Libs::Graphics::ShaderRecompiler::IR::Program>
|
||||
SrtProgram(uint64_t address) {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
auto &value_block = AddValueBlock(program);
|
||||
|
||||
MemoryInfo memory;
|
||||
memory.kind = ResourceKind::ScalarAddress;
|
||||
memory.planning_only = true;
|
||||
program.memory_info.push_back(memory);
|
||||
const auto low = Value(static_cast<uint32_t>(address));
|
||||
const auto high = Value(static_cast<uint32_t>(address >> 32u));
|
||||
auto &handle =
|
||||
value_block.AppendNewInst(ValueOpcode::GetAddressResource, {low, high});
|
||||
auto &raw = value_block.AppendNewInst(
|
||||
ValueOpcode::LoadAddressU32,
|
||||
{Value(&handle), Value(0u), Value(0u), Value(true)});
|
||||
raw.SetFlags(MemoryFlags{.index = 0, .pc = 0x40});
|
||||
program.srt_reads.push_back({Value(&raw), 0});
|
||||
|
||||
auto &srt = value_block.AppendNewInst(ValueOpcode::GetSrtResource);
|
||||
auto &flat = value_block.AppendNewInst(ValueOpcode::ReadConst,
|
||||
{Value(&srt), Value(0u)});
|
||||
DescriptorSource source;
|
||||
source.dwords[0] = Value(&flat);
|
||||
source.dwords[1] = Value(0u);
|
||||
source.dword_count = 2;
|
||||
program.descriptor_sources.push_back(source);
|
||||
return std::make_shared<const Program>(std::move(program));
|
||||
}
|
||||
|
||||
std::shared_ptr<const Libs::Graphics::ShaderRecompiler::IR::Program>
|
||||
UnbasedFlatProgram() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
AddValueBlock(program);
|
||||
program.info.uses_dma = true;
|
||||
return std::make_shared<const Program>(std::move(program));
|
||||
}
|
||||
|
||||
std::shared_ptr<const Libs::Graphics::ShaderRecompiler::IR::Program>
|
||||
UserDataBufferProgram() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
auto &value_block = AddValueBlock(program);
|
||||
|
||||
auto &user_data = value_block.AppendNewInst(
|
||||
ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(0))});
|
||||
DescriptorSource source;
|
||||
source.dwords[0] = Value(&user_data);
|
||||
source.dwords[1] = Value(0u);
|
||||
source.dwords[2] = Value(0u);
|
||||
source.dwords[3] = Value(0u);
|
||||
source.dword_count = 4;
|
||||
program.descriptor_sources.push_back(source);
|
||||
program.info.buffers.push_back({.source = 0});
|
||||
return std::make_shared<const Program>(std::move(program));
|
||||
}
|
||||
|
||||
void TestMappedSrtUsesDirectReaderByDefault() {
|
||||
using namespace Libs::Graphics;
|
||||
const uint32_t dword = 0x12345678;
|
||||
auto cached_program = SrtProgram(reinterpret_cast<uint64_t>(&dword));
|
||||
ShaderStageRuntime stage;
|
||||
uint32_t specialization_reads = 0;
|
||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage,
|
||||
RejectSpecializationRead,
|
||||
&specialization_reads),
|
||||
"mapped SRT stage materialization failed");
|
||||
Check(specialization_reads == 0,
|
||||
"ordinary SRT read used the specialization reader");
|
||||
Check(stage.program == cached_program && stage.resources != nullptr,
|
||||
"cache rematerialization did not publish the mapped stage");
|
||||
Check(stage.resources->flattened_srt.size() == 1 &&
|
||||
stage.resources->flattened_srt[0] == dword,
|
||||
"cache rematerialization did not use the direct reader by default");
|
||||
}
|
||||
|
||||
void TestUnbasedFlatCacheHitMaterializes() {
|
||||
using namespace Libs::Graphics;
|
||||
auto cached_program = UnbasedFlatProgram();
|
||||
auto prior_program = std::make_shared<const ShaderRecompiler::IR::Program>();
|
||||
auto prior_resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
ShaderStageRuntime stage{prior_program, prior_resources};
|
||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage),
|
||||
"unbased FLAT stage materialization failed");
|
||||
Check(stage.program == cached_program && stage.resources != prior_resources,
|
||||
"unbased FLAT cache hit did not publish its BDA stage snapshot");
|
||||
}
|
||||
|
||||
void TestFailedMaterializationPreservesPriorStage() {
|
||||
using namespace Libs::Graphics;
|
||||
auto cached_program = UserDataBufferProgram();
|
||||
auto prior_program = std::make_shared<const ShaderRecompiler::IR::Program>();
|
||||
auto prior_resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
ShaderStageRuntime stage{prior_program, prior_resources};
|
||||
|
||||
Check(!ShaderMaterializeStageRuntime(cached_program, {}, 0, stage),
|
||||
"missing runtime user data did not reject the cached stage");
|
||||
Check(stage.program == prior_program && stage.resources == prior_resources,
|
||||
"failed cache materialization replaced the prior stage");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Common {
|
||||
|
||||
int DbgExitHandler(const char *, int, std::string_view) { std::abort(); }
|
||||
|
||||
int DbgExitHandler(const char *, int, fmt::text_style, std::string_view) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int DbgExitIfHandler(const char *, const char *, int) { return 1; }
|
||||
|
||||
void DbgExit(int) { std::abort(); }
|
||||
|
||||
} // namespace Common
|
||||
|
||||
int main() {
|
||||
TestMappedSrtUsesDirectReaderByDefault();
|
||||
TestUnbasedFlatCacheHitMaterializes();
|
||||
TestFailedMaterializationPreservesPriorStage();
|
||||
std::puts("ShaderStageRuntimeTests: all cases passed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Keep this focused standalone target self-contained by amalgamating its small
|
||||
// typed-IR implementation set.
|
||||
#include "graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Type.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Value.cpp"
|
||||
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
|
||||
+259
-307
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user