mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 05:46:56 +01:00
shader: collect usage once and emit descriptor slots directly
This commit is contained in:
+40
-107
@@ -1,6 +1,5 @@
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
@@ -2009,12 +2008,8 @@ void TestFmaskLoadSpecialization() {
|
||||
"inactive FMASK load did not preserve the execution mask");
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program);
|
||||
const auto kind = DescriptorBindingForImage(fixture.program.info.images[0]);
|
||||
Check(kind.has_value() &&
|
||||
FindBinding(fixture.program.bindings, *kind)->resources ==
|
||||
std::vector<uint32_t>{0},
|
||||
"FMASK allocated an ordinary image descriptor");
|
||||
Check(DescriptorBindingForImage(fixture.program.info.images[0]).has_value(),
|
||||
"FMASK removal lost the ordinary image class");
|
||||
user_data[8] = 0;
|
||||
user_data[1] = static_cast<uint32_t>(Prospero::BufferFormat::k8UInt) << 20u;
|
||||
ResourceSpecialization rebound;
|
||||
@@ -2152,14 +2147,9 @@ void TestDynamicStorageMipTracking() {
|
||||
"base-1 through last-3 dynamic storage range was not specialized");
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
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 =
|
||||
FindBinding(fixture.program.bindings, *storage_kind);
|
||||
Check(storage_binding != nullptr &&
|
||||
storage_binding->resources == std::vector<uint32_t>({0, 1, 1, 1}),
|
||||
"dynamic storage mip descriptors were not expanded consecutively");
|
||||
Check(DescriptorBindingForImage(images[0]) == DescriptorBindingForImage(images[1]) &&
|
||||
images[0].mip_count == 1 && images[1].mip_mode == ImageMipMode::Dynamic,
|
||||
"dynamic storage mip metadata did not retain one homogeneous image class");
|
||||
|
||||
Fixture null_fixture;
|
||||
const auto null_handle = null_fixture.Image(
|
||||
@@ -2284,10 +2274,8 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program);
|
||||
Check(FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) != nullptr,
|
||||
"flattened typed SRT reads did not receive a binding");
|
||||
Check(fixture.program.info.uses_flattened_srt,
|
||||
"flattened typed SRT reads were not collected");
|
||||
}
|
||||
|
||||
void TestDynamicSrtReadRemainsExplicit() {
|
||||
@@ -2327,20 +2315,9 @@ void TestDynamicSrtReadRemainsExplicit() {
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program);
|
||||
Check(FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) == nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::BdaPagetable) != nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FaultBuffer) != nullptr,
|
||||
"dynamic scalar read received the wrong resource bindings");
|
||||
Check(fixture.program.bindings.memory_offset_dword ==
|
||||
fixture.program.bindings.user_data_registers.size() &&
|
||||
fixture.program.bindings.memory_offset_count == 1u &&
|
||||
fixture.program.bindings.ShaderDataDwords() ==
|
||||
fixture.program.bindings.memory_offset_dword + 1u,
|
||||
"unified memory-offset layout is inconsistent");
|
||||
Check(!fixture.program.info.uses_flattened_srt && fixture.program.info.uses_dma &&
|
||||
fixture.program.info.live_buffers == 1u,
|
||||
"dynamic scalar read received the wrong resource usage");
|
||||
}
|
||||
|
||||
void TestWritableDescriptorPhi() {
|
||||
@@ -3240,7 +3217,7 @@ void TestConditionalIndirectImageMaterialization() {
|
||||
"taken indirect image branch did not require its descriptor table");
|
||||
}
|
||||
|
||||
void TestShaderInfoAndBindingLayout() {
|
||||
void TestShaderInfoCollection() {
|
||||
Fixture fixture;
|
||||
const auto handle = fixture.Buffer(
|
||||
{fixture.UserData(3), fixture.UserData(4), Value(64u), Value(0u)}, 4);
|
||||
@@ -3270,15 +3247,9 @@ void TestShaderInfoAndBindingLayout() {
|
||||
StageInputKind::GlobalInvocationId,
|
||||
"typed shader values were not reflected in shader info");
|
||||
|
||||
AllocateBindings(fixture.program);
|
||||
Check(FindBinding(fixture.program.bindings, DescriptorBindingKind::Buffers) !=
|
||||
nullptr &&
|
||||
FindBinding(fixture.program.bindings, DescriptorBindingKind::Gds) !=
|
||||
nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::ShaderData) == nullptr &&
|
||||
fixture.program.bindings.UsesPushData(),
|
||||
"typed resources were not assigned native bindings");
|
||||
Check(fixture.program.info.live_buffers == 1u &&
|
||||
fixture.program.info.uses_gds && !fixture.program.info.uses_lds,
|
||||
"typed resources were not collected in shader info");
|
||||
Check(NativeBinding(ShaderType::Compute, DescriptorBindingKind::Buffers) ==
|
||||
static_cast<uint32_t>(DescriptorBindingKind::Buffers) &&
|
||||
NativeBinding(ShaderType::Vertex, DescriptorBindingKind::Buffers) ==
|
||||
@@ -3287,9 +3258,8 @@ void TestShaderInfoAndBindingLayout() {
|
||||
static_cast<uint32_t>(DescriptorBindingKind::Count) +
|
||||
static_cast<uint32_t>(DescriptorBindingKind::Buffers),
|
||||
"fixed stage binding ranges are inconsistent");
|
||||
Check(fixture.program.bindings.user_data_registers ==
|
||||
std::vector<uint32_t>({3u, 4u}),
|
||||
"binding layout did not collect live typed user-data values");
|
||||
Check(fixture.program.info.user_data_registers == std::vector<uint32_t>({3u, 4u}),
|
||||
"shader info did not collect live typed user-data values");
|
||||
}
|
||||
|
||||
void TestImageBindingAbi() {
|
||||
@@ -3414,60 +3384,27 @@ void TestImageBindingAbi() {
|
||||
Check(Invalid(image), "float atomic image received a descriptor binding");
|
||||
}
|
||||
|
||||
void TestGraphicsPushConstantLayout() {
|
||||
const auto AddUserData = [](Fixture &fixture, uint32_t count) {
|
||||
for (uint32_t index = 0; index < count; index++) {
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {fixture.UserData(index)});
|
||||
}
|
||||
fixture.program.shader_info_complete = true;
|
||||
};
|
||||
uint32_t cursor = 0;
|
||||
Fixture pixel(ShaderType::Pixel);
|
||||
AddUserData(pixel, 4);
|
||||
AllocateBindings(pixel.program, cursor);
|
||||
Check(
|
||||
pixel.program.bindings.UsesPushData() &&
|
||||
pixel.program.bindings.push_data_start_dword == 0 &&
|
||||
FindBinding(pixel.program.bindings,
|
||||
DescriptorBindingKind::ShaderData) == nullptr,
|
||||
"pixel shader did not start the shared push-data block");
|
||||
pixel.program.bindings.AdvancePushData(cursor);
|
||||
void TestLiveShaderDataCollection() {
|
||||
Fixture fixture;
|
||||
fixture.UserData(7);
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {fixture.UserData(5)});
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {fixture.UserData(2)});
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {fixture.UserData(5)});
|
||||
fixture.Emit(ValueOpcode::GetDispatchThreadExtent, {Value(1u)});
|
||||
fixture.PlanAndTrack();
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
Check(fixture.program.info.user_data_registers == std::vector<uint32_t>({2u, 5u}) &&
|
||||
!fixture.program.info.uses_dispatch_threads,
|
||||
"shader data collection retained unused values or duplicated registers");
|
||||
|
||||
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, 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, 20);
|
||||
AllocateBindings(spill.program, cursor);
|
||||
Check(
|
||||
!spill.program.bindings.UsesPushData() &&
|
||||
spill.program.bindings.push_data_start_dword == PushData::NoStart &&
|
||||
FindBinding(spill.program.bindings,
|
||||
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 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");
|
||||
const auto extent = fixture.Emit(ValueOpcode::GetDispatchThreadExtent, {Value(0u)});
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {extent});
|
||||
fixture.program.shader_info_complete = false;
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
Check(fixture.program.info.uses_dispatch_threads &&
|
||||
fixture.program.info.user_data_registers == std::vector<uint32_t>({2u, 5u}),
|
||||
"shader data recollection lost a live dispatch extent or retained stale registers");
|
||||
}
|
||||
|
||||
void TestResourceLimitIsTransactional() {
|
||||
@@ -3489,12 +3426,8 @@ void TestResourceLimitIsTransactional() {
|
||||
"compute shader did not retain all 64 distinct buffers");
|
||||
ShaderComputeInputInfo compute{};
|
||||
CollectShaderInfo(accepted.program, {.compute = &compute});
|
||||
AllocateBindings(accepted.program);
|
||||
const auto *binding = FindBinding(accepted.program.bindings,
|
||||
DescriptorBindingKind::Buffers);
|
||||
Check(binding != nullptr && binding->resources.size() == 64u &&
|
||||
accepted.program.bindings.memory_offset_count == 64u,
|
||||
"compute shader binding layout truncated the 64 buffers");
|
||||
Check(accepted.program.info.live_buffers == UINT64_MAX,
|
||||
"shader info truncated the 64 live buffers");
|
||||
|
||||
Fixture fixture;
|
||||
MemoryInfo memory;
|
||||
@@ -3596,9 +3529,9 @@ int main() {
|
||||
Run("guarded scalar descriptor reads", TestGuardedScalarDescriptorReads);
|
||||
Run("conservative buffer reachability", TestConservativeBufferReachability);
|
||||
Run("conditional indirect image", TestConditionalIndirectImageMaterialization);
|
||||
Run("shader info and bindings", TestShaderInfoAndBindingLayout);
|
||||
Run("shader info collection", TestShaderInfoCollection);
|
||||
Run("image binding ABI", TestImageBindingAbi);
|
||||
Run("graphics push constants", TestGraphicsPushConstantLayout);
|
||||
Run("live shader data collection", TestLiveShaderDataCollection);
|
||||
Run("resource limit", TestResourceLimitIsTransactional);
|
||||
Run("malformed memory kinds", TestMalformedMemoryKindsRejected);
|
||||
} catch (const std::exception &exception) {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
|
||||
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ShaderInfoCollection.h"
|
||||
#include "graphics/shader/rectListShader.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
#include "graphics/shader/shaderCompiler.h"
|
||||
@@ -456,11 +456,12 @@ struct RenderExecutorTestAccess {
|
||||
: program.stage == ShaderType::Pixel
|
||||
? vk::ShaderStageFlagBits::eFragment
|
||||
: vk::ShaderStageFlagBits::eCompute;
|
||||
for (const auto &binding : program.bindings.descriptors) {
|
||||
for (auto mask = program.bindings.descriptor_mask; mask != 0; mask &= mask - 1) {
|
||||
const auto index = std::countr_zero(mask);
|
||||
const auto kind = static_cast<ShaderRecompiler::IR::DescriptorBindingKind>(index);
|
||||
layout_bindings.push_back(
|
||||
{ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind),
|
||||
NativeDescriptorType(binding.kind), NativeDescriptorCount(binding),
|
||||
shader_stage});
|
||||
{ShaderRecompiler::IR::NativeBinding(program.stage, kind),
|
||||
NativeDescriptorType(kind), program.bindings.descriptor_counts[index], shader_stage});
|
||||
}
|
||||
}
|
||||
vk::DescriptorSetLayoutCreateInfo descriptor_info{};
|
||||
@@ -1676,15 +1677,11 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
|
||||
image != result.program.info.images.end() &&
|
||||
image->mip_count == test.expected_mip_descriptors,
|
||||
"runtime descriptor range did not specialize the mip count");
|
||||
const auto resource =
|
||||
static_cast<u32>(image - result.program.info.images.begin());
|
||||
const auto kind = ShaderRecompiler::IR::DescriptorBindingForImage(*image);
|
||||
const auto *binding = kind ? ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings, *kind) : nullptr;
|
||||
Require(test.name, "dynamic mip binding",
|
||||
binding != nullptr &&
|
||||
std::ranges::count(binding->resources, resource) ==
|
||||
test.expected_mip_descriptors,
|
||||
kind && image->descriptor_index != UINT32_MAX &&
|
||||
image->descriptor_index + image->mip_count <=
|
||||
result.program.bindings.descriptor_counts[static_cast<size_t>(*kind)],
|
||||
"dynamic image did not receive one descriptor per mip");
|
||||
}
|
||||
if (test.expand_shader_data_storage) {
|
||||
@@ -1697,49 +1694,45 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
|
||||
block.AppendNewInst(ShaderRecompiler::IR::ValueOpcode::ReferenceU32,
|
||||
{ShaderRecompiler::IR::Value(&user_data)});
|
||||
}
|
||||
result.program.bindings = {};
|
||||
result.program.binding_layout_complete = false;
|
||||
ShaderRecompiler::IR::AllocateBindings(result.program);
|
||||
const auto *shader_data = ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::ShaderData);
|
||||
result.program.shader_info_complete = false;
|
||||
ShaderRecompiler::IR::CollectShaderInfo(result.program, options.input_info);
|
||||
result.spirv = ShaderRecompiler::Spirv::EmitProgram(result.program, options.input_info);
|
||||
const auto shader_data = result.program.bindings.descriptor_counts[
|
||||
static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::ShaderData)];
|
||||
Require(test.name, "binding layout",
|
||||
result.program.bindings.user_data_registers.size() == 33 &&
|
||||
result.program.info.user_data_registers.size() == 33 &&
|
||||
!result.program.bindings.UsesPushData() &&
|
||||
shader_data != nullptr,
|
||||
shader_data != 0,
|
||||
"oversized shader data did not use its storage fallback");
|
||||
result.spirv = ShaderRecompiler::Spirv::EmitProgram(result.program,
|
||||
options.input_info);
|
||||
}
|
||||
Require(test.name, "SPIR-V emit", !result.spirv.empty(),
|
||||
"recompiler returned empty SPIR-V");
|
||||
ValidateSpirv(test.name, result.spirv);
|
||||
CheckSpirvText(test, result.spirv);
|
||||
const auto *buffer_binding = ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::Buffers);
|
||||
if (test.expected_buffer_resources) {
|
||||
std::vector<u32> resources;
|
||||
for (u32 i = 0; i < result.program.info.buffers.size(); ++i) {
|
||||
if (result.program.info.buffers[i].descriptor_index != UINT32_MAX) resources.push_back(i);
|
||||
}
|
||||
Require(test.name, "live buffer bindings",
|
||||
buffer_binding != nullptr &&
|
||||
buffer_binding->resources == *test.expected_buffer_resources,
|
||||
resources == *test.expected_buffer_resources,
|
||||
"dead buffer changed the surviving descriptor order");
|
||||
}
|
||||
std::vector<u32> packed_user_data;
|
||||
for (const auto reg : result.program.bindings.user_data_registers) {
|
||||
for (const auto reg : result.program.info.user_data_registers) {
|
||||
packed_user_data.push_back(
|
||||
resources.user_data[reg - result.program.user_data_base]);
|
||||
}
|
||||
packed_user_data.resize(result.program.bindings.ShaderDataDwords());
|
||||
const auto buffer_count = result.program.bindings.memory_offset_count;
|
||||
for (u32 i = 0; i < buffer_count; i++) {
|
||||
u32 offset = 0;
|
||||
const auto resource = buffer_binding->resources[i];
|
||||
if (resource < test.storage_buffer_offsets.size()) {
|
||||
offset = test.storage_buffer_offsets[resource];
|
||||
}
|
||||
for (u32 resource = 0; resource < result.program.info.buffers.size(); ++resource) {
|
||||
const auto index = result.program.info.buffers[resource].descriptor_index;
|
||||
if (index == UINT32_MAX) continue;
|
||||
const auto offset = resource < test.storage_buffer_offsets.size()
|
||||
? test.storage_buffer_offsets[resource] : 0u;
|
||||
Require(test.name, "shader data", offset < 256,
|
||||
"storage buffer offset is not representable");
|
||||
packed_user_data[result.program.bindings.memory_offset_dword + i / 4u] |=
|
||||
offset << ((i % 4u) * 8u);
|
||||
packed_user_data[result.program.bindings.memory_offset_dword + index / 4u] |=
|
||||
offset << ((index % 4u) * 8u);
|
||||
}
|
||||
return {std::move(result.spirv), std::move(result.program),
|
||||
std::move(resources), std::move(packed_user_data)};
|
||||
@@ -1835,7 +1828,7 @@ CompiledShader CompileFragmentCase(const GraphicsCase &test) {
|
||||
"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) {
|
||||
for (const auto reg : result.program.info.user_data_registers) {
|
||||
packed_user_data.push_back(
|
||||
resources.user_data[reg - result.program.user_data_base]);
|
||||
}
|
||||
@@ -2142,12 +2135,12 @@ public:
|
||||
ShaderRecompiler::IR::CompiledShaderInfo vertex_program{};
|
||||
vertex_program.stage = ShaderType::Vertex;
|
||||
vertex_program.bindings.push_data_start_dword = 2;
|
||||
vertex_program.bindings.user_data_registers = {0, 1};
|
||||
vertex_program.info.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_data_start_dword = 0;
|
||||
pixel_program.bindings.user_data_registers = {0, 1};
|
||||
pixel_program.info.user_data_registers = {0, 1};
|
||||
pixel_program.bindings.memory_offset_dword = 2;
|
||||
ShaderRecompiler::IR::ResourceSnapshot vertex_snapshot;
|
||||
vertex_snapshot.user_data = {0x11111111u, 0x22222222u};
|
||||
@@ -11970,19 +11963,23 @@ public:
|
||||
block_words + 0x10000 / sizeof(uint32_t) - 4),
|
||||
"compressed atlas aliases reloaded stale CPU bytes over GPU-written blocks");
|
||||
|
||||
const auto allocate_bindings =
|
||||
[&](ShaderRecompiler::IR::Program &program) {
|
||||
program.shader_info_complete = true;
|
||||
ShaderRecompiler::IR::AllocateBindings(program);
|
||||
};
|
||||
const auto set_image_bindings = [](ShaderRecompiler::IR::Program &program) {
|
||||
for (auto &image : program.info.images) {
|
||||
const auto kind = *ShaderRecompiler::IR::DescriptorBindingForImage(image);
|
||||
const auto index = static_cast<size_t>(kind);
|
||||
image.descriptor_index = program.bindings.descriptor_counts[index];
|
||||
program.bindings.descriptor_counts[index] += image.mip_count;
|
||||
program.bindings.descriptor_mask |= uint64_t{1} << index;
|
||||
}
|
||||
};
|
||||
const auto make_buffer_program =
|
||||
[](ShaderType stage, ShaderRecompiler::IR::BufferResource resource) {
|
||||
ShaderRecompiler::IR::CompiledShaderInfo program{};
|
||||
program.stage = stage;
|
||||
resource.descriptor_index = 0;
|
||||
program.info.buffers.push_back(resource);
|
||||
program.bindings.descriptors.push_back(
|
||||
{ShaderRecompiler::IR::DescriptorBindingKind::Buffers, {0}});
|
||||
program.bindings.memory_offset_count = 1;
|
||||
program.bindings.descriptor_counts[0] = 1;
|
||||
program.bindings.descriptor_mask = 1;
|
||||
program.bindings.push_data_start_dword = 0;
|
||||
return program;
|
||||
};
|
||||
@@ -12283,7 +12280,7 @@ public:
|
||||
}
|
||||
ShaderRecompiler::IR::ApplyResourceSpecialization(
|
||||
null_program, null_specialization);
|
||||
allocate_bindings(null_program);
|
||||
set_image_bindings(null_program);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo null_info{};
|
||||
null_info.stage = null_program.stage;
|
||||
null_info.info = std::move(null_program.info);
|
||||
@@ -12378,7 +12375,7 @@ public:
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
storage_resource.written = true;
|
||||
storage_program.info.images.push_back(storage_resource);
|
||||
allocate_bindings(storage_program);
|
||||
set_image_bindings(storage_program);
|
||||
ShaderRecompiler::IR::DescriptorValue storage_descriptor{};
|
||||
std::copy(std::begin(storage.fields), std::end(storage.fields),
|
||||
storage_descriptor.dwords.begin());
|
||||
@@ -12914,7 +12911,7 @@ public:
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
sampled_resource.read = true;
|
||||
sampled_program.info.images.push_back(sampled_resource);
|
||||
allocate_bindings(sampled_program);
|
||||
set_image_bindings(sampled_program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot sampled_snapshot{};
|
||||
sampled_snapshot.images.push_back(storage_descriptor);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo sampled_info{};
|
||||
@@ -12996,7 +12993,7 @@ public:
|
||||
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);
|
||||
set_image_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);
|
||||
@@ -13008,7 +13005,7 @@ public:
|
||||
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);
|
||||
set_image_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);
|
||||
@@ -13084,7 +13081,7 @@ public:
|
||||
split_ir.stage = ShaderType::Vertex;
|
||||
split_ir.resource_tracking_complete = true;
|
||||
split_ir.info.images = {storage_resource, sampled_resource};
|
||||
allocate_bindings(split_ir);
|
||||
set_image_bindings(split_ir);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo split_program{};
|
||||
split_program.stage = split_ir.stage;
|
||||
split_program.info = std::move(split_ir.info);
|
||||
@@ -13177,7 +13174,7 @@ public:
|
||||
depth_views_ir.stage = ShaderType::Pixel;
|
||||
depth_views_ir.resource_tracking_complete = true;
|
||||
depth_views_ir.info.images = {sampled_resource, sampled_resource};
|
||||
allocate_bindings(depth_views_ir);
|
||||
set_image_bindings(depth_views_ir);
|
||||
ShaderRecompiler::IR::CompiledShaderInfo depth_views_program{};
|
||||
depth_views_program.stage = depth_views_ir.stage;
|
||||
depth_views_program.info = std::move(depth_views_ir.info);
|
||||
@@ -15261,29 +15258,20 @@ public:
|
||||
std::copy_n(test.compute_info.dispatch_threads_num, 3,
|
||||
shader_data.begin() + layout.dispatch_thread_dword);
|
||||
}
|
||||
auto Binding = [&](Kind kind) {
|
||||
return ShaderRecompiler::IR::FindBinding(layout, kind);
|
||||
auto Count = [&](Kind kind) {
|
||||
return layout.descriptor_counts[static_cast<size_t>(kind)];
|
||||
};
|
||||
auto Native = [](Kind kind) {
|
||||
return ShaderRecompiler::IR::NativeBinding(ShaderType::Compute, kind);
|
||||
};
|
||||
for (const auto &binding : layout.descriptors) {
|
||||
const auto resource_class =
|
||||
ShaderRecompiler::IR::ImageBindingResourceClass(binding.kind);
|
||||
if (resource_class == ShaderRecompiler::IR::ImageResourceClass::None) {
|
||||
continue;
|
||||
}
|
||||
const auto &image =
|
||||
compiled.program.info.images.at(binding.resources.front());
|
||||
for (const auto &image : compiled.program.info.images) {
|
||||
bool supported =
|
||||
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
|
||||
if (resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
if (image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
supported = supported ||
|
||||
image.dimension ==
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||
image.dimension ==
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim1DArray;
|
||||
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray;
|
||||
}
|
||||
Require(test.name, "dispatch", supported,
|
||||
"unsupported image dimension needs a matching Vulkan test view");
|
||||
@@ -15311,11 +15299,10 @@ public:
|
||||
item.stageFlags = vk::ShaderStageFlagBits::eCompute;
|
||||
layout_bindings.push_back(item);
|
||||
};
|
||||
for (const auto &binding : layout.descriptors) {
|
||||
add_layout_binding(ShaderRecompiler::IR::NativeBinding(
|
||||
ShaderType::Compute, binding.kind),
|
||||
NativeDescriptorType(binding.kind),
|
||||
NativeDescriptorCount(binding));
|
||||
for (auto mask = layout.descriptor_mask; mask != 0; mask &= mask - 1) {
|
||||
const auto index = std::countr_zero(mask);
|
||||
const auto kind = static_cast<Kind>(index);
|
||||
add_layout_binding(Native(kind), NativeDescriptorType(kind), layout.descriptor_counts[index]);
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo layout_info{};
|
||||
@@ -15376,9 +15363,9 @@ public:
|
||||
}
|
||||
pool_sizes.push_back({type, count});
|
||||
};
|
||||
for (const auto &binding : layout.descriptors) {
|
||||
add_pool_size(NativeDescriptorType(binding.kind),
|
||||
NativeDescriptorCount(binding));
|
||||
for (auto mask = layout.descriptor_mask; mask != 0; mask &= mask - 1) {
|
||||
const auto index = std::countr_zero(mask);
|
||||
add_pool_size(NativeDescriptorType(static_cast<Kind>(index)), layout.descriptor_counts[index]);
|
||||
}
|
||||
vk::DescriptorPoolCreateInfo pool_info{};
|
||||
pool_info.sType = vk::StructureType::eDescriptorPoolCreateInfo;
|
||||
@@ -15403,11 +15390,8 @@ public:
|
||||
|
||||
std::vector<vk::WriteDescriptorSet> writes;
|
||||
std::vector<vk::DescriptorBufferInfo> buffer_infos;
|
||||
std::vector<vk::DescriptorImageInfo> sampled_infos;
|
||||
std::array<std::vector<vk::DescriptorImageInfo>, ShaderRecompiler::IR::ImageBindingCount> image_infos;
|
||||
std::vector<vk::ImageView> sampled_mip_views;
|
||||
std::vector<vk::DescriptorImageInfo> storage_infos;
|
||||
std::vector<vk::DescriptorImageInfo> storage_uint_infos;
|
||||
std::vector<vk::DescriptorImageInfo> storage_atomic_infos;
|
||||
std::vector<vk::DescriptorImageInfo> sampler_infos;
|
||||
Buffer flattened_buffer;
|
||||
Buffer user_data_buffer;
|
||||
@@ -15419,7 +15403,7 @@ public:
|
||||
vk::DescriptorBufferInfo bda_pagetable_info{};
|
||||
vk::DescriptorBufferInfo fault_buffer_info{};
|
||||
|
||||
if (Binding(Kind::SharedMemory) != nullptr) {
|
||||
if (Count(Kind::SharedMemory) != 0) {
|
||||
const auto bytes = uint64_t{test.compute_info.lds_size_dwords} * sizeof(u32) *
|
||||
test.dispatch_x * test.dispatch_y * test.dispatch_z;
|
||||
shared_buffer = CreateHostBuffer(test.name, bytes, vk::BufferUsageFlagBits::eStorageBuffer, {});
|
||||
@@ -15433,9 +15417,9 @@ public:
|
||||
writes.push_back(write);
|
||||
}
|
||||
|
||||
const bool uses_bda = Binding(Kind::BdaPagetable) != nullptr;
|
||||
const bool uses_bda = Count(Kind::BdaPagetable) != 0;
|
||||
Require(test.name, "dispatch",
|
||||
uses_bda == (Binding(Kind::FaultBuffer) != nullptr),
|
||||
uses_bda == (Count(Kind::FaultBuffer) != 0),
|
||||
"BDA page table and fault buffer must be bound together");
|
||||
if (uses_bda) {
|
||||
EnsureBdaBuffers(test.name);
|
||||
@@ -15458,16 +15442,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const auto *buffers = Binding(Kind::Buffers);
|
||||
if (buffers != nullptr) {
|
||||
buffer_infos.resize(buffers->resources.size());
|
||||
for (u32 i = 0; i < buffer_infos.size(); i++) {
|
||||
auto &info = buffer_infos[i];
|
||||
if (Count(Kind::Buffers) != 0) {
|
||||
buffer_infos.resize(Count(Kind::Buffers));
|
||||
for (u32 resource = 0; resource < compiled.program.info.buffers.size(); ++resource) {
|
||||
const auto index = compiled.program.info.buffers[resource].descriptor_index;
|
||||
if (index == UINT32_MAX) continue;
|
||||
auto &info = buffer_infos[index];
|
||||
info.buffer = buffer.buffer;
|
||||
info.offset = 0;
|
||||
info.range = buffer.size;
|
||||
if (test.storage_buffer_range_bytes != 0) {
|
||||
const auto resource = buffers->resources[i];
|
||||
const auto offset = resource < test.storage_buffer_offsets.size()
|
||||
? test.storage_buffer_offsets[resource]
|
||||
: 0u;
|
||||
@@ -15486,8 +15470,7 @@ public:
|
||||
write.pBufferInfo = buffer_infos.data();
|
||||
writes.push_back(write);
|
||||
}
|
||||
if (const auto *flattened = Binding(Kind::FlattenedSrt);
|
||||
flattened != nullptr) {
|
||||
if (Count(Kind::FlattenedSrt) != 0) {
|
||||
flattened_buffer =
|
||||
CreateStorageBuffer(test.name, compiled.resources.flattened_srt,
|
||||
compiled.resources.flattened_srt.size());
|
||||
@@ -15501,7 +15484,7 @@ public:
|
||||
write.pBufferInfo = &flattened_info;
|
||||
writes.push_back(write);
|
||||
}
|
||||
if (const auto *user = Binding(Kind::ShaderData); user != nullptr) {
|
||||
if (Count(Kind::ShaderData) != 0) {
|
||||
user_data_buffer =
|
||||
CreateStorageBuffer(test.name, shader_data, shader_data.size());
|
||||
user_data_info = {user_data_buffer.buffer, 0, user_data_buffer.size};
|
||||
@@ -15514,7 +15497,7 @@ public:
|
||||
write.pBufferInfo = &user_data_info;
|
||||
writes.push_back(write);
|
||||
}
|
||||
if (const auto *gds = Binding(Kind::Gds); gds != nullptr) {
|
||||
if (Count(Kind::Gds) != 0) {
|
||||
Require(test.name, "dispatch", gds_buffer != nullptr,
|
||||
"GDS descriptor requested but no GDS buffer was provided");
|
||||
gds_info = {gds_buffer->buffer, 0, gds_buffer->size};
|
||||
@@ -15527,51 +15510,27 @@ public:
|
||||
write.pBufferInfo = &gds_info;
|
||||
writes.push_back(write);
|
||||
}
|
||||
std::vector<const ShaderRecompiler::IR::DescriptorBinding *> sampled_bindings;
|
||||
const ShaderRecompiler::IR::DescriptorBinding *storage = nullptr;
|
||||
const ShaderRecompiler::IR::DescriptorBinding *storage_uint = nullptr;
|
||||
const ShaderRecompiler::IR::DescriptorBinding *storage_atomic = nullptr;
|
||||
for (const auto &binding : layout.descriptors) {
|
||||
const auto resource_class =
|
||||
ShaderRecompiler::IR::ImageBindingResourceClass(binding.kind);
|
||||
if (resource_class == ShaderRecompiler::IR::ImageResourceClass::None) {
|
||||
continue;
|
||||
}
|
||||
const auto &image =
|
||||
compiled.program.info.images.at(binding.resources.front());
|
||||
if (resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
sampled_bindings.push_back(&binding);
|
||||
} else if (image.atomic) {
|
||||
storage_atomic = &binding;
|
||||
} else if (image.numeric_class == Prospero::TextureNumericClass::Float) {
|
||||
storage = &binding;
|
||||
} else {
|
||||
storage_uint = &binding;
|
||||
}
|
||||
}
|
||||
size_t sampled_count = 0;
|
||||
for (const auto *binding : sampled_bindings) sampled_count += binding->resources.size();
|
||||
sampled_infos.reserve(sampled_count);
|
||||
Require(test.name, "dispatch", sampled_resources.empty() ||
|
||||
sampled_resources.size() == compiled.program.info.images.size(),
|
||||
"sampled resources must match the logical images");
|
||||
for (const auto *sampled : sampled_bindings) {
|
||||
Require(test.name, "dispatch", sampled_image != nullptr || !sampled_resources.empty(),
|
||||
"sampled image descriptor requested but no sampled image was "
|
||||
"provided");
|
||||
const auto first_info = sampled_infos.size();
|
||||
sampled_infos.resize(first_info + sampled->resources.size());
|
||||
std::vector<u32> mip_indices(compiled.program.info.images.size());
|
||||
for (u32 slot = 0; slot < sampled->resources.size(); slot++) {
|
||||
auto &info = sampled_infos[first_info + slot];
|
||||
const auto resource = sampled->resources[slot];
|
||||
const auto *source_image = sampled_resources.empty() ? sampled_image
|
||||
: &sampled_resources[resource];
|
||||
for (u32 resource = 0; resource < compiled.program.info.images.size(); ++resource) {
|
||||
const auto &image = compiled.program.info.images[resource];
|
||||
const auto kind = *ShaderRecompiler::IR::DescriptorBindingForImage(image);
|
||||
auto &infos = image_infos[ShaderRecompiler::IR::ImageBindingIndex(kind)];
|
||||
infos.resize(Count(kind));
|
||||
const bool sampled = image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled;
|
||||
const auto *source_image = sampled
|
||||
? (sampled_resources.empty() ? sampled_image : &sampled_resources[resource])
|
||||
: (image.numeric_class == Prospero::TextureNumericClass::Float
|
||||
? storage_image : storage_image_uint);
|
||||
Require(test.name, "dispatch", source_image != nullptr,
|
||||
"image descriptor requested but no matching image was provided");
|
||||
for (u32 mip_index = 0; mip_index < image.mip_count; ++mip_index) {
|
||||
auto &info = infos[image.descriptor_index + mip_index];
|
||||
info.imageView = source_image->view;
|
||||
info.imageLayout = source_image->layout;
|
||||
if (compiled.program.info.images[resource].mip_mode ==
|
||||
ShaderRecompiler::IR::ImageMipMode::Dynamic) {
|
||||
const auto mip = test.sampled_image_view_base_mip + mip_indices[resource]++;
|
||||
if (sampled && image.mip_mode == ShaderRecompiler::IR::ImageMipMode::Dynamic) {
|
||||
const auto mip = test.sampled_image_view_base_mip + mip_index;
|
||||
Require(test.name, "dispatch", mip < source_image->mip_levels,
|
||||
"sampled mip descriptor exceeds the supplied image");
|
||||
vk::ImageViewCreateInfo view{};
|
||||
@@ -15590,52 +15549,27 @@ public:
|
||||
sampled_mip_views.push_back(info.imageView);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (u32 group = 0; group < image_infos.size(); ++group) {
|
||||
const auto &infos = image_infos[group];
|
||||
if (infos.empty()) continue;
|
||||
const auto kind = static_cast<Kind>(ShaderRecompiler::IR::FirstImageBinding + group);
|
||||
vk::WriteDescriptorSet write{};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstSet = descriptor_set;
|
||||
write.dstBinding = Native(sampled->kind);
|
||||
write.descriptorCount = static_cast<u32>(sampled->resources.size());
|
||||
write.descriptorType = vk::DescriptorType::eSampledImage;
|
||||
write.pImageInfo = sampled_infos.data() + first_info;
|
||||
write.dstBinding = Native(kind);
|
||||
write.descriptorCount = static_cast<u32>(infos.size());
|
||||
write.descriptorType = NativeDescriptorType(kind);
|
||||
write.pImageInfo = infos.data();
|
||||
writes.push_back(write);
|
||||
}
|
||||
const auto BindStorage =
|
||||
[&](const ShaderRecompiler::IR::DescriptorBinding *binding,
|
||||
const Image *image, std::vector<vk::DescriptorImageInfo> *infos) {
|
||||
if (binding == nullptr) {
|
||||
return;
|
||||
}
|
||||
Require(
|
||||
test.name, "dispatch", image != nullptr,
|
||||
"storage image descriptor requested but no matching image was "
|
||||
"provided");
|
||||
infos->resize(binding->resources.size());
|
||||
for (auto &info : *infos) {
|
||||
info.imageView = image->view;
|
||||
info.imageLayout = image->layout;
|
||||
}
|
||||
vk::WriteDescriptorSet write{};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstSet = descriptor_set;
|
||||
write.dstBinding = Native(binding->kind);
|
||||
write.descriptorCount = static_cast<u32>(infos->size());
|
||||
write.descriptorType = vk::DescriptorType::eStorageImage;
|
||||
write.pImageInfo = infos->data();
|
||||
writes.push_back(write);
|
||||
};
|
||||
BindStorage(storage, storage_image, &storage_infos);
|
||||
BindStorage(storage_uint, storage_image_uint, &storage_uint_infos);
|
||||
BindStorage(storage_atomic, storage_image_uint, &storage_atomic_infos);
|
||||
const auto *samplers = Binding(Kind::Samplers);
|
||||
if (samplers != nullptr) {
|
||||
if (Count(Kind::Samplers) != 0) {
|
||||
Require(test.name, "dispatch", sampler != nullptr,
|
||||
"sampler descriptor requested but no sampler was provided");
|
||||
sampler_infos.resize(samplers->resources.size());
|
||||
sampler_infos.resize(Count(Kind::Samplers));
|
||||
for (auto &info : sampler_infos) {
|
||||
info.sampler = sampler;
|
||||
}
|
||||
vk::WriteDescriptorSet write{};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstSet = descriptor_set;
|
||||
write.dstBinding = Native(Kind::Samplers);
|
||||
write.descriptorCount = static_cast<u32>(sampler_infos.size());
|
||||
@@ -15711,7 +15645,7 @@ public:
|
||||
}
|
||||
cmd.dispatch(test.dispatch_x, test.dispatch_y, test.dispatch_z);
|
||||
|
||||
if (buffers != nullptr) {
|
||||
if (Count(Kind::Buffers) != 0) {
|
||||
vk::BufferMemoryBarrier barrier{};
|
||||
barrier.sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barrier.srcAccessMask =
|
||||
@@ -18886,8 +18820,7 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
|
||||
}
|
||||
using Kind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||
auto Has = [&](Kind kind) {
|
||||
return ShaderRecompiler::IR::FindBinding(compiled.program.bindings, kind) !=
|
||||
nullptr;
|
||||
return compiled.program.bindings.descriptor_counts[static_cast<size_t>(kind)] != 0;
|
||||
};
|
||||
const auto dwords = std::max<size_t>(
|
||||
{test.initial.size(), test.expected.size(), static_cast<size_t>(1)});
|
||||
@@ -18900,9 +18833,8 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
|
||||
vk::Sampler sampler = nullptr;
|
||||
bool needs_sampled_image = false;
|
||||
bool needs_storage_image = false;
|
||||
for (const auto &binding : compiled.program.bindings.descriptors) {
|
||||
const auto resource_class =
|
||||
ShaderRecompiler::IR::ImageBindingResourceClass(binding.kind);
|
||||
for (const auto &image : compiled.program.info.images) {
|
||||
const auto resource_class = image.resource_class;
|
||||
if (resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
needs_sampled_image = true;
|
||||
} else if (resource_class ==
|
||||
@@ -34642,16 +34574,16 @@ void CheckIndirectBufferStore(VulkanHarness &vulkan) {
|
||||
program.info.buffers[0].indirect_search_iterations = 0;
|
||||
program.info.buffers[0].indirect_resources.clear();
|
||||
}
|
||||
program.binding_layout_complete = false;
|
||||
AllocateBindings(program);
|
||||
const auto *buffers = FindBinding(program.bindings, DescriptorBindingKind::Buffers);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
compiled.spirv = ShaderRecompiler::Spirv::EmitProgram(program, {.compute = &compute});
|
||||
Require(test.name, "candidate bindings",
|
||||
buffers != nullptr && buffers->resources ==
|
||||
(null_buffers == 2u ? std::vector<u32>{0u} : std::vector<u32>{0u, 1u}),
|
||||
program.bindings.descriptor_counts[0] == (null_buffers == 2u ? 1u : 2u) &&
|
||||
program.info.buffers[0].descriptor_index == 0u &&
|
||||
(null_buffers == 2u || program.info.buffers[1].descriptor_index == 1u),
|
||||
"indirect store lost a buffer binding");
|
||||
compiled.packed_user_data.resize(program.bindings.ShaderDataDwords());
|
||||
compiled.packed_user_data[program.bindings.memory_offset_dword] = 12u | (108u << 8u);
|
||||
compiled.spirv = ShaderRecompiler::Spirv::EmitProgram(program, {.compute = &compute});
|
||||
ValidateSpirv(test.name, compiled.spirv);
|
||||
if (null_buffers == 2u) {
|
||||
test.required_spirv.clear();
|
||||
@@ -34760,8 +34692,9 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
program.info.samplers.push_back({1u, 0x10f0u});
|
||||
program.info.sampled_pairs.push_back({0u, 0u, 0x10f0u});
|
||||
|
||||
AllocateBindings(program);
|
||||
ShaderComputeInputInfo compute{};
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program,
|
||||
{.compute = &compute});
|
||||
ValidateSpirv(name, spirv);
|
||||
@@ -34789,8 +34722,8 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
"the sample operand lacks its nonuniform decoration");
|
||||
|
||||
program.info.images[0].indirect_search_iterations = 0;
|
||||
program.binding_layout_complete = false;
|
||||
AllocateBindings(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
spirv = ShaderRecompiler::Spirv::EmitProgram(program, {.compute = &compute});
|
||||
ValidateSpirv("DenseIndirectImageTable", spirv);
|
||||
Require(name, "dense image map disassembly", tools.Disassemble(spirv, &text),
|
||||
@@ -34810,8 +34743,8 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
program.info.images.push_back(candidate);
|
||||
}
|
||||
program.info.images[0] = root;
|
||||
program.binding_layout_complete = false;
|
||||
AllocateBindings(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
spirv = ShaderRecompiler::Spirv::EmitProgram(program, {.compute = &compute});
|
||||
ValidateSpirv(name, spirv);
|
||||
Require(name, "large image array disassembly", tools.Disassemble(spirv, &text),
|
||||
@@ -34824,8 +34757,8 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
program.info.images[root.indirect_resources[103]].dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
|
||||
program.info.images[root.indirect_resources[103]].cube = true;
|
||||
program.binding_layout_complete = false;
|
||||
AllocateBindings(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
spirv = ShaderRecompiler::Spirv::EmitProgram(program, {.compute = &compute});
|
||||
ValidateSpirv(name, spirv);
|
||||
Require(name, "mixed contiguous image runs", tools.Disassemble(spirv, &text) &&
|
||||
@@ -34850,8 +34783,8 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
? ShaderRecompiler::Decoder::ImageDimension::Dim2D
|
||||
: ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
|
||||
}
|
||||
program.binding_layout_complete = false;
|
||||
AllocateBindings(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
spirv = ShaderRecompiler::Spirv::EmitProgram(program, {.compute = &compute});
|
||||
ValidateSpirv(name, spirv);
|
||||
Require(name, "mixed SPIR-V disassembly", tools.Disassemble(spirv, &text),
|
||||
@@ -34914,14 +34847,13 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
{Value(&output), Value(0u), Value(&byte_offset), Value(0u), Value(&sample_x), Value(true)});
|
||||
store.SetFlags(MemoryFlags{1u, 0u});
|
||||
program.info.buffers.push_back({.packed_stride = 1, .written = true});
|
||||
program.binding_layout_complete = false;
|
||||
AllocateBindings(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute});
|
||||
|
||||
TestCase test;
|
||||
test.name = "IndirectImageContiguousRuns";
|
||||
CompiledShader compiled;
|
||||
compiled.program = std::move(program);
|
||||
compiled.packed_user_data.resize(compiled.program.bindings.ShaderDataDwords());
|
||||
constexpr std::array ordinals{0u, 1u, 2u, 3u, 4u, 5u, 2u, 0u};
|
||||
compiled.resources.flattened_srt.push_back(ordinals.size());
|
||||
compiled.resources.flattened_srt.insert(compiled.resources.flattened_srt.end(),
|
||||
@@ -34944,6 +34876,7 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
compute.threads_num[1] = compute.threads_num[2] = 1u;
|
||||
compiled.program.wave_size = wave_size;
|
||||
compiled.spirv = ShaderRecompiler::Spirv::EmitProgram(compiled.program, {.compute = &compute});
|
||||
compiled.packed_user_data.resize(compiled.program.bindings.ShaderDataDwords());
|
||||
ValidateSpirv(test.name, compiled.spirv);
|
||||
const auto halves = wave_size > compute.host_subgroup_size ? 2u : 1u;
|
||||
Require(test.name, "native array run samples", tools.Disassemble(compiled.spirv, &text) &&
|
||||
@@ -39491,8 +39424,8 @@ void CheckResourcePlanHandoff() {
|
||||
ValidateSpirv(name, compiled.spirv);
|
||||
const auto &bindings = compiled.program.bindings;
|
||||
Require(name, "GPU data requirements",
|
||||
bindings.user_data_registers.empty() && bindings.memory_offset_count == 1 &&
|
||||
(FindBinding(bindings, DescriptorBindingKind::FlattenedSrt) != nullptr) ==
|
||||
compiled.program.info.user_data_registers.empty() && bindings.descriptor_counts[0] == 1 &&
|
||||
(bindings.descriptor_counts[static_cast<size_t>(DescriptorBindingKind::FlattenedSrt)] != 0) ==
|
||||
numeric_read,
|
||||
"descriptor-only shader retained SRT uploads or live scalar data was removed");
|
||||
|
||||
|
||||
+121
-124
@@ -1294,10 +1294,14 @@ void TestNormalizedImageContracts() {
|
||||
"Vulkan image-view compatibility classes diverged from production");
|
||||
}
|
||||
|
||||
void TestSpirvRequirementsAnalysis() {
|
||||
void TestShaderFeatureCollection() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
|
||||
ShaderPixelInputInfo pixel_info{};
|
||||
ShaderComputeInputInfo compute_info{};
|
||||
ShaderVertexInputInfo vertex_info{};
|
||||
Program program;
|
||||
program.resource_tracking_complete = true;
|
||||
program.stage = ShaderType::Pixel;
|
||||
program.block_storage.push_back(std::make_unique<Block>());
|
||||
auto *block = program.block_storage.back().get();
|
||||
@@ -1318,8 +1322,8 @@ void TestSpirvRequirementsAnalysis() {
|
||||
block->AppendNewInst(ValueOpcode::SetAttribute, {Value(0u), Value(true)});
|
||||
export_value.SetFlags(ExportFlags{.index = 0});
|
||||
|
||||
const auto requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
CollectShaderInfo(program, {.pixel = &pixel_info});
|
||||
const auto& requirements = program.info;
|
||||
Check(requirements.subgroup_ballot && requirements.subgroup_shuffle &&
|
||||
requirements.subgroup_local_invocation_id &&
|
||||
requirements.compute_derivatives &&
|
||||
@@ -1328,8 +1332,9 @@ void TestSpirvRequirementsAnalysis() {
|
||||
"consolidated SPIR-V requirements missed an IR dependency");
|
||||
|
||||
program.stage = ShaderType::Compute;
|
||||
const auto compute_requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.compute = &compute_info});
|
||||
const auto& compute_requirements = program.info;
|
||||
Check(!compute_requirements.function_lds &&
|
||||
!compute_requirements.pixel_valid_mask,
|
||||
"stage-specific SPIR-V requirements leaked into compute");
|
||||
@@ -1339,19 +1344,22 @@ void TestSpirvRequirementsAnalysis() {
|
||||
[&] {
|
||||
program.stage = ShaderType::Pixel;
|
||||
shared.SetFlags(MemoryFlags{.index = 1});
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.pixel = &pixel_info});
|
||||
},
|
||||
"invalid shared-memory requirement metadata did not terminate analysis");
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
program.stage = ShaderType::Pixel;
|
||||
export_value.SetFlags(ExportFlags{.index = 1});
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
program.shader_info_complete = false;
|
||||
CollectShaderInfo(program, {.pixel = &pixel_info});
|
||||
},
|
||||
"invalid export requirement metadata did not terminate analysis");
|
||||
#endif
|
||||
|
||||
Program add_tid;
|
||||
add_tid.resource_tracking_complete = true;
|
||||
add_tid.stage = ShaderType::Compute;
|
||||
add_tid.info.buffers.resize(1);
|
||||
add_tid.info.buffers[0].packed_stride = 1u << 20u;
|
||||
@@ -1367,8 +1375,8 @@ void TestSpirvRequirementsAnalysis() {
|
||||
ValueOpcode::LoadBufferU32,
|
||||
{Value(&buffer), Value(0u), Value(0u), Value(0u), Value(true)});
|
||||
load.SetFlags(MemoryFlags{.index = 0});
|
||||
const auto add_tid_requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(add_tid);
|
||||
CollectShaderInfo(add_tid, {.compute = &compute_info});
|
||||
const auto& add_tid_requirements = add_tid.info;
|
||||
Check(add_tid_requirements.subgroup_local_invocation_id &&
|
||||
!add_tid_requirements.subgroup_ballot &&
|
||||
!add_tid_requirements.subgroup_shuffle,
|
||||
@@ -1378,20 +1386,24 @@ void TestSpirvRequirementsAnalysis() {
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
add_tid.memory_info[0].resource = 1;
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(add_tid);
|
||||
add_tid.shader_info_complete = false;
|
||||
CollectShaderInfo(add_tid, {.compute = &compute_info});
|
||||
},
|
||||
"invalid buffer requirement metadata did not terminate analysis");
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
add_tid.stage = ShaderType::Vertex;
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(add_tid);
|
||||
add_tid.shader_info_complete = false;
|
||||
CollectShaderInfo(add_tid, {.vertex = &vertex_info});
|
||||
},
|
||||
"graphics buffer ADD_TID did not terminate requirements analysis");
|
||||
#endif
|
||||
|
||||
Program empty;
|
||||
const auto empty_requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(empty);
|
||||
empty.stage = ShaderType::Compute;
|
||||
empty.resource_tracking_complete = true;
|
||||
CollectShaderInfo(empty, {.compute = &compute_info});
|
||||
const auto& empty_requirements = empty.info;
|
||||
Check(!empty_requirements.subgroup_ballot &&
|
||||
!empty_requirements.subgroup_shuffle &&
|
||||
!empty_requirements.subgroup_local_invocation_id &&
|
||||
@@ -5209,25 +5221,13 @@ void TestNewShaderRecompilerScalarMemoryBindingDomains() {
|
||||
raw_options.user_data = raw_user_data;
|
||||
|
||||
auto raw = RecompileForTest(raw_shader, raw_options);
|
||||
const auto *address_binding = ShaderRecompiler::IR::FindBinding(
|
||||
raw.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::BdaPagetable);
|
||||
const auto *fault_binding = ShaderRecompiler::IR::FindBinding(
|
||||
raw.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::FaultBuffer);
|
||||
const auto& raw_counts = raw.program.bindings.descriptor_counts;
|
||||
using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||
Check(raw.program.info.uses_dma && raw.program.info.buffers.empty() &&
|
||||
address_binding != nullptr && fault_binding != nullptr &&
|
||||
address_binding->resources.empty() &&
|
||||
fault_binding->resources.empty() &&
|
||||
ShaderRecompiler::IR::FindBinding(
|
||||
raw.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::Buffers) ==
|
||||
nullptr &&
|
||||
ShaderRecompiler::IR::FindBinding(
|
||||
raw.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) ==
|
||||
nullptr &&
|
||||
raw.program.bindings.memory_offset_count == 0u,
|
||||
raw_counts[static_cast<size_t>(BindingKind::BdaPagetable)] == 1 &&
|
||||
raw_counts[static_cast<size_t>(BindingKind::FaultBuffer)] == 1 &&
|
||||
raw_counts[static_cast<size_t>(BindingKind::Buffers)] == 0 &&
|
||||
raw_counts[static_cast<size_t>(BindingKind::FlattenedSrt)] == 0,
|
||||
"raw scalar load did not use only the DMA domain");
|
||||
Check(count_live_memory_ops(
|
||||
raw.program, ShaderRecompiler::IR::ValueOpcode::LoadAddressU32,
|
||||
@@ -5255,23 +5255,14 @@ void TestNewShaderRecompilerScalarMemoryBindingDomains() {
|
||||
buffer_options.user_data = buffer_user_data;
|
||||
|
||||
auto buffer = RecompileForTest(buffer_shader, buffer_options);
|
||||
const auto *buffer_binding = ShaderRecompiler::IR::FindBinding(
|
||||
buffer.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::Buffers);
|
||||
const auto& buffer_counts = buffer.program.bindings.descriptor_counts;
|
||||
Check(buffer.program.info.buffers.size() == 1u &&
|
||||
buffer.program.info.buffers[0].scalar &&
|
||||
buffer.program.info.buffers[0].descriptor_index == 0 &&
|
||||
!buffer.program.info.uses_dma &&
|
||||
buffer_binding != nullptr &&
|
||||
buffer_binding->resources == std::vector<uint32_t>{0u} &&
|
||||
ShaderRecompiler::IR::FindBinding(
|
||||
buffer.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::BdaPagetable) ==
|
||||
nullptr &&
|
||||
ShaderRecompiler::IR::FindBinding(
|
||||
buffer.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) ==
|
||||
nullptr &&
|
||||
buffer.program.bindings.memory_offset_count == 1u,
|
||||
buffer_counts[static_cast<size_t>(BindingKind::Buffers)] == 1 &&
|
||||
buffer_counts[static_cast<size_t>(BindingKind::BdaPagetable)] == 0 &&
|
||||
buffer_counts[static_cast<size_t>(BindingKind::FlattenedSrt)] == 0,
|
||||
"descriptor scalar load did not use only the buffer domain");
|
||||
Check(count_live_memory_ops(
|
||||
buffer.program, ShaderRecompiler::IR::ValueOpcode::ReadConstBuffer,
|
||||
@@ -6066,8 +6057,7 @@ void TestNewShaderRecompilerImageLoad2DMsaa() {
|
||||
const auto binding_kind = ShaderRecompiler::IR::DescriptorBindingForImage(
|
||||
result.program.info.images[0]);
|
||||
Check(binding_kind.has_value() &&
|
||||
ShaderRecompiler::IR::FindBinding(result.program.bindings,
|
||||
*binding_kind) != nullptr,
|
||||
result.program.bindings.descriptor_counts[static_cast<size_t>(*binding_kind)] != 0,
|
||||
"2D-MSAA image did not receive a multisampled descriptor binding");
|
||||
Check(SpirvContainsTypeImage(result.spirv, 1, 0, 1, 1),
|
||||
"SPIR-V binary does not contain a multisampled 2D image type");
|
||||
@@ -7232,14 +7222,8 @@ void TestNewShaderRecompilerUnbasedFlatUsesBda() {
|
||||
|
||||
auto result = RecompileForTest(shader, options);
|
||||
Check(result.program.info.uses_dma &&
|
||||
ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::BdaPagetable) !=
|
||||
nullptr &&
|
||||
ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::FaultBuffer) !=
|
||||
nullptr,
|
||||
result.program.bindings.descriptor_counts[static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::BdaPagetable)] != 0 &&
|
||||
result.program.bindings.descriptor_counts[static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::FaultBuffer)] != 0,
|
||||
"unbased FLAT did not compile through BDA");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
@@ -7978,6 +7962,8 @@ void TestNewShaderRecompilerStructuredU64Phi() {
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU64,
|
||||
{IR::Value(&phi), IR::Value(1u)});
|
||||
|
||||
program.shader_info_complete = false;
|
||||
ShaderRecompiler::IR::CollectShaderInfo(program, options.input_info);
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
const auto before = MeasureSpirv(result.spirv);
|
||||
@@ -9841,6 +9827,8 @@ void TestNewShaderRecompilerDispatcherSpillsU32x3() {
|
||||
IR::IREmitter use(prologue_program.blocks[1]);
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(2u)});
|
||||
|
||||
prologue_program.shader_info_complete = false;
|
||||
IR::CollectShaderInfo(prologue_program, options.input_info);
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(prologue_program,
|
||||
options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
@@ -9866,6 +9854,8 @@ void TestNewShaderRecompilerDispatcherSpillsU32x3() {
|
||||
const auto sum = use.Emit(IR::ValueOpcode::IAdd32, {high, IR::Value(7u)});
|
||||
use.Emit(IR::ValueOpcode::IAdd32, {sum, middle});
|
||||
|
||||
program.shader_info_complete = false;
|
||||
IR::CollectShaderInfo(program, options.input_info);
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program,
|
||||
options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
@@ -9902,6 +9892,8 @@ void TestNewShaderRecompilerPlanningOnlyLoads() {
|
||||
read.Instruction()->SetFlags(IR::MemoryFlags{.index = 1});
|
||||
ir.Emit(IR::ValueOpcode::ReferenceU32, {load});
|
||||
ir.Emit(IR::ValueOpcode::ReferenceU32, {read});
|
||||
program.shader_info_complete = false;
|
||||
IR::CollectShaderInfo(program, options.input_info);
|
||||
const auto spirv = Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
Check(spirv == result.spirv,
|
||||
@@ -10035,6 +10027,8 @@ void TestNewShaderRecompilerNativeU64Translation() {
|
||||
ir.Emit(IR::ValueOpcode::ShiftRightArithmetic64, {base, IR::Value(count)});
|
||||
}
|
||||
|
||||
program.shader_info_complete = false;
|
||||
ShaderRecompiler::IR::CollectShaderInfo(program, options.input_info);
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
const auto source = DisassembleSpirvBinary(spirv);
|
||||
@@ -10072,6 +10066,8 @@ void TestNewShaderRecompilerNativeU64Translation() {
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU64, {vector, IR::Value(1u)});
|
||||
|
||||
spirv.clear();
|
||||
program.shader_info_complete = false;
|
||||
ShaderRecompiler::IR::CollectShaderInfo(program, options.input_info);
|
||||
spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
const auto before = MeasureSpirv(result.spirv);
|
||||
@@ -10432,13 +10428,13 @@ void TestMeshExportStorage() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto &layout = result.program.bindings;
|
||||
Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) &&
|
||||
layout.memory_offset_count == 1,
|
||||
layout.descriptor_counts[static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::Buffers)] == 1,
|
||||
"mesh shader did not retain its user-data placement and buffer offset");
|
||||
const auto reg = std::ranges::find(layout.user_data_registers, 13u);
|
||||
Check(reg != layout.user_data_registers.end(), "mesh shader lost user s13");
|
||||
const auto reg = std::ranges::find(result.program.info.user_data_registers, 13u);
|
||||
Check(reg != result.program.info.user_data_registers.end(), "mesh shader lost user s13");
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
for (const auto dword :
|
||||
{static_cast<uint32_t>(reg - layout.user_data_registers.begin()),
|
||||
{static_cast<uint32_t>(reg - result.program.info.user_data_registers.begin()),
|
||||
layout.memory_offset_dword}) {
|
||||
const auto operand =
|
||||
std::string(layout.UsesPushData() ? "vsharp" : "shader_data") +
|
||||
@@ -13317,72 +13313,55 @@ void TestNewShaderRecompilerNativeBindingPlan() {
|
||||
options.user_data = user_data;
|
||||
|
||||
auto result = RecompileForTest(shader, options);
|
||||
const auto *buffers = ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings, BindingKind::Buffers);
|
||||
const ShaderRecompiler::IR::DescriptorBinding *sampled = nullptr;
|
||||
const ShaderRecompiler::IR::DescriptorBinding *storage = nullptr;
|
||||
const ShaderRecompiler::IR::DescriptorBinding *atomic_storage = nullptr;
|
||||
const auto& counts = result.program.bindings.descriptor_counts;
|
||||
std::optional<BindingKind> sampled;
|
||||
std::optional<BindingKind> storage;
|
||||
std::optional<BindingKind> atomic_storage;
|
||||
for (const auto &image : result.program.info.images) {
|
||||
const auto kind = ShaderRecompiler::IR::DescriptorBindingForImage(image);
|
||||
if (!kind.has_value()) {
|
||||
continue;
|
||||
}
|
||||
const auto *binding =
|
||||
ShaderRecompiler::IR::FindBinding(result.program.bindings, *kind);
|
||||
if (image.resource_class ==
|
||||
ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
sampled = binding;
|
||||
Check(kind.has_value() && image.descriptor_index == 0,
|
||||
"native image did not receive its descriptor slot");
|
||||
if (image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
sampled = kind;
|
||||
} else if (image.atomic) {
|
||||
atomic_storage = binding;
|
||||
atomic_storage = kind;
|
||||
} else {
|
||||
storage = binding;
|
||||
storage = kind;
|
||||
}
|
||||
}
|
||||
const auto *samplers = ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings, BindingKind::Samplers);
|
||||
Check(buffers != nullptr && buffers->resources.size() == 2,
|
||||
"native binding plan did not allocate scalar/vector buffers");
|
||||
Check(sampled != nullptr && sampled->resources.size() == 1,
|
||||
"native binding plan did not allocate the sampled image");
|
||||
Check(storage != nullptr && storage->resources.size() == 1,
|
||||
"native binding plan did not allocate the float storage image");
|
||||
Check(atomic_storage != nullptr && atomic_storage->resources.size() == 1,
|
||||
"native binding plan did not allocate the atomic storage image");
|
||||
Check(samplers != nullptr && samplers->resources.size() == 1,
|
||||
"native binding plan did not allocate the sampler");
|
||||
Check(counts[static_cast<size_t>(BindingKind::Buffers)] == 2 &&
|
||||
result.program.info.buffers[0].descriptor_index == 0 &&
|
||||
result.program.info.buffers[1].descriptor_index == 1,
|
||||
"native emission did not allocate scalar/vector buffer slots");
|
||||
Check(sampled && counts[static_cast<size_t>(*sampled)] == 1,
|
||||
"native emission did not allocate the sampled image");
|
||||
Check(storage && counts[static_cast<size_t>(*storage)] == 1,
|
||||
"native emission did not allocate the float storage image");
|
||||
Check(atomic_storage && counts[static_cast<size_t>(*atomic_storage)] == 1,
|
||||
"native emission did not allocate the atomic storage image");
|
||||
Check(counts[static_cast<size_t>(BindingKind::Samplers)] == 1,
|
||||
"native emission did not allocate the sampler");
|
||||
Check(SpirvContainsOpcode(result.spirv, 86),
|
||||
"SPIR-V binary does not combine separate image/sampler descriptors");
|
||||
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||
ShaderRecompiler::IR::NativeBinding(
|
||||
result.program.stage, buffers->kind)),
|
||||
"SPIR-V lacks storage-buffer Binding decoration");
|
||||
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||
ShaderRecompiler::IR::NativeBinding(
|
||||
result.program.stage, sampled->kind)),
|
||||
"SPIR-V lacks sampled-image Binding decoration");
|
||||
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||
ShaderRecompiler::IR::NativeBinding(
|
||||
result.program.stage, samplers->kind)),
|
||||
"SPIR-V lacks sampler Binding decoration");
|
||||
for (const auto kind : {BindingKind::Buffers, *sampled, BindingKind::Samplers}) {
|
||||
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||
ShaderRecompiler::IR::NativeBinding(result.program.stage, kind)),
|
||||
"SPIR-V lacks a resource Binding decoration");
|
||||
}
|
||||
Check(SpirvDecorationValueCount(result.spirv, 34u, 0u) ==
|
||||
result.program.bindings.descriptors.size(),
|
||||
static_cast<size_t>(std::popcount(result.program.bindings.descriptor_mask)),
|
||||
"SPIR-V resources do not all use descriptor set zero");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
auto malformed = RecompileForTest(shader, options);
|
||||
auto malformed_buffers = std::ranges::find_if(
|
||||
malformed.program.bindings.descriptors, [](const auto &binding) {
|
||||
return binding.kind == BindingKind::Buffers;
|
||||
});
|
||||
malformed_buffers->resources.clear();
|
||||
(void)ShaderRecompiler::Spirv::EmitProgram(malformed.program,
|
||||
options.input_info);
|
||||
},
|
||||
"malformed native binding topology did not terminate SPIR-V emission");
|
||||
const auto bindings = result.program.bindings;
|
||||
result.program.bindings = {};
|
||||
result.program.info.buffers[0].descriptor_index = UINT32_MAX;
|
||||
Check(ShaderRecompiler::Spirv::EmitProgram(result.program, options.input_info) == result.spirv &&
|
||||
result.program.bindings == bindings &&
|
||||
result.program.info.buffers[0].descriptor_index == 0,
|
||||
"repeated emission did not rebuild descriptor slots deterministically");
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
ShaderRecompiler::IR::Inst *buffer_handle = nullptr;
|
||||
for (auto *block : result.program.blocks) {
|
||||
const auto found = std::ranges::find_if(*block, [](const auto &inst) {
|
||||
@@ -14499,12 +14478,32 @@ void TestGraphicsPushConstantPlacement() {
|
||||
RecompileForTest(shader, options, nullptr, nullptr, PushDataStart);
|
||||
Check(placed.program.bindings.UsesPushData() &&
|
||||
placed.program.bindings.push_data_start_dword == PushDataStart &&
|
||||
ShaderRecompiler::IR::FindBinding(placed.program.bindings,
|
||||
BindingKind::ShaderData) == nullptr,
|
||||
placed.program.bindings.descriptor_counts[static_cast<size_t>(BindingKind::ShaderData)] == 0,
|
||||
"pixel shader did not use its assigned shared push-data position");
|
||||
Check(SpirvHasMemberDecorationValue(placed.spirv, OffsetDecoration, 0),
|
||||
"SPIR-V push-data block did not use the canonical zero base");
|
||||
CheckSpirvBinaryValidates(placed.spirv);
|
||||
|
||||
auto exact = RecompileForTest(shader, options, nullptr, nullptr, 28);
|
||||
Check(exact.program.bindings.UsesPushData() &&
|
||||
exact.program.bindings.push_data_start_dword == 28 &&
|
||||
exact.program.bindings.ShaderDataDwords() == 4,
|
||||
"exact-fit shader data did not use the remaining push space");
|
||||
uint32_t cursor = 28;
|
||||
exact.program.bindings.AdvancePushData(cursor);
|
||||
Check(cursor == 32, "exact-fit shader data advanced the cursor incorrectly");
|
||||
CheckSpirvBinaryValidates(exact.spirv);
|
||||
|
||||
auto spill = RecompileForTest(shader, options, nullptr, nullptr, 29);
|
||||
auto full = RecompileForTest(shader, options, nullptr, nullptr, 32);
|
||||
Check(!spill.program.bindings.UsesPushData() &&
|
||||
spill.program.bindings.descriptor_counts[static_cast<size_t>(BindingKind::ShaderData)] == 1 &&
|
||||
spill.program.bindings == full.program.bindings && spill.spirv == full.spirv,
|
||||
"insufficient push space did not produce canonical storage-backed data");
|
||||
cursor = 29;
|
||||
spill.program.bindings.AdvancePushData(cursor);
|
||||
Check(cursor == 29, "spilled shader data consumed push space");
|
||||
CheckSpirvBinaryValidates(spill.spirv);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerUnsupportedMemoryDecode() {
|
||||
@@ -14689,7 +14688,7 @@ void TestCompilerStageInputOwnership() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestSpirvEmissionOwnsRequirements() {
|
||||
void TestSpirvRepeatedEmission() {
|
||||
using namespace ShaderRecompiler;
|
||||
const uint32_t shader[] = {EncodeSopp(0x01)};
|
||||
ShaderComputeInputInfo compute{};
|
||||
@@ -14705,10 +14704,12 @@ void TestSpirvEmissionOwnsRequirements() {
|
||||
const auto first = emitter.Emit(IR::ValueOpcode::ReadFirstLane,
|
||||
{value, IR::Value(true)});
|
||||
emitter.Emit(IR::ValueOpcode::IAdd32, {first, lane});
|
||||
compiled.program.shader_info_complete = false;
|
||||
IR::CollectShaderInfo(compiled.program, options.input_info);
|
||||
const auto binary = Spirv::EmitProgram(compiled.program, options.input_info);
|
||||
CheckSpirvBinaryValidates(binary);
|
||||
Check((DisassembleSpirvBinary(binary).find("BuiltIn SubgroupLocalInvocationId") != std::string::npos),
|
||||
"emission reused requirements from an earlier IR version");
|
||||
"collection missed the subgroup requirement of the added IR");
|
||||
Check(Spirv::EmitProgram(compiled.program, options.input_info) == binary,
|
||||
"repeated emission reused IDs from the previous module");
|
||||
|
||||
@@ -14781,9 +14782,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
|
||||
const auto dead_gds_result =
|
||||
compile("dead-gds", dead_gds,
|
||||
{.words = 64, .instructions = 20, .labels = 4, .branches = 3});
|
||||
Check(ShaderRecompiler::IR::FindBinding(
|
||||
dead_gds_result.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::Gds) == nullptr,
|
||||
Check(dead_gds_result.program.bindings.descriptor_counts[static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::Gds)] == 0,
|
||||
"dead GDS load retained a descriptor binding");
|
||||
|
||||
const uint32_t structured_phi[] = {
|
||||
@@ -14924,9 +14923,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
|
||||
wide_gds_metrics.workgroup_variables == 0u &&
|
||||
wide_gds_metrics.array_lengths == 1u,
|
||||
"native wide GDS fixture did not share one entry-dominating length");
|
||||
Check(ShaderRecompiler::IR::FindBinding(
|
||||
wide_gds_result.program.bindings,
|
||||
ShaderRecompiler::IR::DescriptorBindingKind::Gds) != nullptr,
|
||||
Check(wide_gds_result.program.bindings.descriptor_counts[static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::Gds)] != 0,
|
||||
"live native wide GDS access did not allocate its descriptor binding");
|
||||
const auto count_shared = [](const auto &result,
|
||||
ShaderRecompiler::IR::ValueOpcode opcode) {
|
||||
@@ -15020,11 +15017,11 @@ int main() {
|
||||
TestShaderBufferResourceSize();
|
||||
TestNativeShaderResourceDependencies();
|
||||
TestNormalizedImageContracts();
|
||||
TestSpirvRequirementsAnalysis();
|
||||
TestShaderFeatureCollection();
|
||||
TestTypedSpirvSerialization();
|
||||
TestDeferredSpirvPhiPatching();
|
||||
TestCompilerStageInputOwnership();
|
||||
TestSpirvEmissionOwnsRequirements();
|
||||
TestSpirvRepeatedEmission();
|
||||
TestRepeatedExportsHaveOneInterface();
|
||||
TestNewShaderRecompilerSpirvSizeBaselines();
|
||||
TestDemandDrivenSpirvDeclarations();
|
||||
|
||||
Reference in New Issue
Block a user