shader: rework resource specialization and optimize pipeline caching

This commit is contained in:
nmzik
2026-09-03 02:58:11 +02:00
parent c48a8bd6ae
commit 98200fed19
39 changed files with 2281 additions and 2235 deletions
+317 -243
View File
@@ -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");