mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
shader: size expanded sampler bindings independently of native roots
This commit is contained in:
@@ -196,21 +196,27 @@ static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipe
|
||||
const auto& limits = graphics.GetPhysicalDeviceProperties().limits;
|
||||
uint64_t descriptor_count = 0;
|
||||
uint64_t storage_buffers = 0;
|
||||
std::array<uint64_t, 32> stage_buffers {}, stage_resources {};
|
||||
uint64_t samplers = 0;
|
||||
std::array<uint64_t, 32> stage_buffers {}, stage_resources {}, stage_samplers {};
|
||||
for (const auto& binding: bindings) {
|
||||
descriptor_count += binding.descriptorCount;
|
||||
const bool storage = binding.descriptorType == vk::DescriptorType::eStorageBuffer;
|
||||
const bool sampler = binding.descriptorType == vk::DescriptorType::eSampler;
|
||||
if (storage) storage_buffers += binding.descriptorCount;
|
||||
if (sampler) samplers += binding.descriptorCount;
|
||||
for (auto stages = static_cast<uint32_t>(binding.stageFlags); stages != 0; stages &= stages - 1u) {
|
||||
const auto stage = std::countr_zero(stages);
|
||||
if (storage) stage_buffers[stage] += binding.descriptorCount;
|
||||
if (binding.descriptorType != vk::DescriptorType::eSampler)
|
||||
if (sampler) stage_samplers[stage] += binding.descriptorCount;
|
||||
else
|
||||
stage_resources[stage] += binding.descriptorCount;
|
||||
if (stage_buffers[stage] > limits.maxPerStageDescriptorStorageBuffers ||
|
||||
stage_resources[stage] > limits.maxPerStageResources) {
|
||||
stage_resources[stage] > limits.maxPerStageResources ||
|
||||
stage_samplers[stage] > limits.maxPerStageDescriptorSamplers) {
|
||||
EXIT("native descriptor stage %u exceeds device limits: buffers=%" PRIu64
|
||||
"/%u resources=%" PRIu64 "/%u\n", stage, stage_buffers[stage],
|
||||
limits.maxPerStageDescriptorStorageBuffers, stage_resources[stage], limits.maxPerStageResources);
|
||||
"/%u resources=%" PRIu64 "/%u samplers=%" PRIu64 "/%u\n", stage, stage_buffers[stage],
|
||||
limits.maxPerStageDescriptorStorageBuffers, stage_resources[stage], limits.maxPerStageResources,
|
||||
stage_samplers[stage], limits.maxPerStageDescriptorSamplers);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,6 +224,10 @@ static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipe
|
||||
EXIT("native storage-buffer descriptors exceed device limit: %" PRIu64 "/%u\n",
|
||||
storage_buffers, limits.maxDescriptorSetStorageBuffers);
|
||||
}
|
||||
if (samplers > limits.maxDescriptorSetSamplers) {
|
||||
EXIT("native sampler descriptors exceed device limit: %" PRIu64 "/%u\n",
|
||||
samplers, limits.maxDescriptorSetSamplers);
|
||||
}
|
||||
pipeline.uses_push_descriptors = descriptor_count <= graphics.max_push_descriptors;
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo create {};
|
||||
|
||||
@@ -545,7 +545,7 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
ResourceSnapshot& snapshot,
|
||||
std::vector<DescriptorValue>& descriptors,
|
||||
std::vector<Specialization>& specializations,
|
||||
uint32_t maximum_resources, Normalize&& normalize) {
|
||||
Normalize&& normalize) {
|
||||
const auto& indirect = *program.descriptor_sources[source_index].indirect_descriptor;
|
||||
const auto descriptor_bytes = read_dwords * sizeof(uint32_t);
|
||||
const auto& sources = indirect.sources;
|
||||
@@ -557,19 +557,17 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
auto& byte_offsets = program.material_keys;
|
||||
const auto children_begin = descriptors.size();
|
||||
const auto root_resource = specializations[resource_index];
|
||||
const auto intern_candidate = [&](const DescriptorValue& candidate, uint32_t& ordinal) {
|
||||
ordinal = 0u;
|
||||
if (candidate == descriptors[resource_index]) return true;
|
||||
const auto intern_candidate = [&](const DescriptorValue& candidate) {
|
||||
if (candidate == descriptors[resource_index]) return 0u;
|
||||
const auto found = std::find(descriptors.begin() + children_begin, descriptors.end(), candidate);
|
||||
ordinal = static_cast<uint32_t>(found - descriptors.begin() - children_begin + 1u);
|
||||
const auto ordinal = static_cast<uint32_t>(found - descriptors.begin() - children_begin + 1u);
|
||||
if (found == descriptors.end()) {
|
||||
if (descriptors.size() >= maximum_resources) return false;
|
||||
descriptors.push_back(candidate);
|
||||
auto child = root_resource;
|
||||
child.indirect_root = resource_index;
|
||||
specializations.push_back(child);
|
||||
}
|
||||
return true;
|
||||
return ordinal;
|
||||
};
|
||||
uint64_t table_base = 0;
|
||||
uint64_t table_size = UINT64_MAX; // Scalar addresses have no buffer descriptor bounds.
|
||||
@@ -581,7 +579,7 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
"indirect {} resource={} source={} table_source={} words={:08x},{:08x},{:08x},{:08x} "
|
||||
"base={:#x} size={} type={} runtime_stride={} records={} swizzle={} addtid={} oob={} "
|
||||
"maximum={} offset={} immediate={} record_bytes={} child_count={} child_stride={} "
|
||||
"child_offset={} indexed_pointer={} candidates={} capacity={} {}",
|
||||
"child_offset={} indexed_pointer={} candidates={} {}",
|
||||
reason, resource_index, source_index, indirect.table_source,
|
||||
table_value.dwords[0], table_value.dwords[1], table_value.dwords[2], table_value.dwords[3],
|
||||
table_base, table_size, table.Type(), table.Stride(), table.NumRecords(),
|
||||
@@ -590,7 +588,7 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
indirect.table_offset, indirect.table_immediate, indirect.table_record_bytes,
|
||||
pointer != nullptr ? pointer->count : 1u, pointer != nullptr ? pointer->stride : 0u,
|
||||
pointer != nullptr ? pointer->offset : 0u, pointer != nullptr && pointer->indexed,
|
||||
descriptors.size(), maximum_resources, details));
|
||||
descriptors.size(), details));
|
||||
};
|
||||
const auto read_failed = [&](std::string_view reason, uint32_t row, uint32_t child,
|
||||
uint64_t base, uint64_t offset, size_t bytes) {
|
||||
@@ -725,8 +723,7 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
if (!normalize(candidate))
|
||||
return normalization_failed(table_offset, word, step,
|
||||
indirect.table_maximum ? "key" : "probe");
|
||||
uint32_t ordinal;
|
||||
if (!intern_candidate(candidate, ordinal)) return false;
|
||||
const auto ordinal = intern_candidate(candidate);
|
||||
if (ordinal == 0u) continue;
|
||||
const auto mapping_word = image_heap ? table_offset / 4u : word * child_count + child;
|
||||
snapshot.flattened_srt.resize(mapping_offset + 2u + mapping_word, 0u);
|
||||
@@ -813,8 +810,8 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
uint32_t ordinal = 0;
|
||||
if (entry == 0 && !byte_keys) {
|
||||
descriptors[resource_index] = candidate;
|
||||
} else if (!intern_candidate(candidate, ordinal)) {
|
||||
return false;
|
||||
} else {
|
||||
ordinal = intern_candidate(candidate);
|
||||
}
|
||||
snapshot.flattened_srt[mapping_offset + 1u + entry * 2u] = key;
|
||||
snapshot.flattened_srt[mapping_offset + 2u + entry * 2u] = ordinal;
|
||||
@@ -837,9 +834,8 @@ struct SamplerPlan {
|
||||
uint32_t source;
|
||||
SamplerClass type;
|
||||
};
|
||||
std::array<std::array<uint32_t, 2>, ShaderInfo::MaxSamplers> mapping;
|
||||
std::array<Binding, ShaderInfo::MaxSamplers> bindings;
|
||||
uint32_t sampler_count = 0;
|
||||
std::vector<std::array<uint32_t, 2>> mapping;
|
||||
std::vector<Binding> bindings;
|
||||
};
|
||||
|
||||
template <typename T, typename Keep>
|
||||
@@ -1076,11 +1072,9 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
|
||||
}
|
||||
|
||||
bool BuildSamplerPlan(const ShaderInfo& base, SamplerPlan& plan) {
|
||||
if (base.samplers.size() > plan.mapping.size()) {
|
||||
return false;
|
||||
}
|
||||
std::array<uint8_t, ShaderInfo::MaxSamplers> usage {};
|
||||
plan.sampler_count = static_cast<uint32_t>(base.samplers.size());
|
||||
plan.mapping.resize(base.samplers.size());
|
||||
plan.bindings.resize(base.samplers.size());
|
||||
std::vector<uint8_t> usage(base.samplers.size());
|
||||
for (const auto& pair: base.sampled_pairs) {
|
||||
if (pair.image >= base.images.size() || pair.sampler >= base.samplers.size()) {
|
||||
return false;
|
||||
@@ -1102,8 +1096,8 @@ bool BuildSamplerPlan(const ShaderInfo& base, SamplerPlan& plan) {
|
||||
bool first = true;
|
||||
for (uint32_t type = 0; type < mapping.size(); type++) {
|
||||
if ((classes & (1u << type)) == 0u) continue;
|
||||
const auto target = first ? index : plan.sampler_count++;
|
||||
if (target >= ShaderInfo::MaxSamplers) return false;
|
||||
const auto target = first ? index : static_cast<uint32_t>(plan.bindings.size());
|
||||
if (!first) plan.bindings.emplace_back();
|
||||
mapping[type] = target;
|
||||
plan.bindings[target] = {index, static_cast<SamplerClass>(type)};
|
||||
first = false;
|
||||
@@ -1609,9 +1603,9 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
return true;
|
||||
};
|
||||
const auto resource_failed = [&](std::string_view kind, uint32_t resource, uint32_t source,
|
||||
size_t candidates, uint32_t capacity) {
|
||||
return SpecializationFail(fmt::format("indirect {} resource={} source={} candidates={} capacity={}",
|
||||
kind, resource, source, candidates, capacity));
|
||||
size_t candidates) {
|
||||
return SpecializationFail(fmt::format("indirect {} resource={} source={} candidates={}",
|
||||
kind, resource, source, candidates));
|
||||
};
|
||||
snapshot.buffers.resize(program.info.buffers.size());
|
||||
specialization.buffers.assign(program.info.buffers.size(), {});
|
||||
@@ -1628,12 +1622,12 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
if (active.empty() || active[base.source]) {
|
||||
if (!MaterializeIndirectDescriptor(
|
||||
program, base.source, i, 4u, 4u, observed, clean, snapshot,
|
||||
snapshot.buffers, specialization.buffers, UINT32_MAX,
|
||||
snapshot.buffers, specialization.buffers,
|
||||
[&](DescriptorValue& value) {
|
||||
return NormalizeIndirectBuffer(
|
||||
value, source->indirect_descriptor->indexed, base);
|
||||
}))
|
||||
return resource_failed("buffer", i, base.source, snapshot.buffers.size(), UINT32_MAX);
|
||||
return resource_failed("buffer", i, base.source, snapshot.buffers.size());
|
||||
}
|
||||
} else if (!evaluate(base.source, snapshot.buffers[i], base.written)) {
|
||||
return false;
|
||||
@@ -1692,14 +1686,14 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
const bool bounded_table = IsBoundedDescriptorTable(program, indirect);
|
||||
if (!MaterializeIndirectDescriptor(
|
||||
program, image.source, i, 8u, image.r128 ? 4u : 8u, observed, clean, snapshot, snapshot.images,
|
||||
specialization.images, UINT32_MAX, [&](DescriptorValue& value) {
|
||||
specialization.images, [&](DescriptorValue& value) {
|
||||
// A broad heap also contains resources for other typed image operations.
|
||||
if (NullImageDescriptor(value) || !ValidImageDescriptor(value, image.r128) ||
|
||||
(bounded_table && DescriptorDimension(value, image.dimension) != image.dimension))
|
||||
value.dwords.fill(0u);
|
||||
return true;
|
||||
})) {
|
||||
return resource_failed("image", i, image.source, snapshot.images.size(), UINT32_MAX);
|
||||
return resource_failed("image", i, image.source, snapshot.images.size());
|
||||
}
|
||||
} else {
|
||||
if (!evaluate(image.source, snapshot.images[i], image.written)) {
|
||||
@@ -1725,9 +1719,9 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
snapshot.samplers[i] = {.dword_count = 4u};
|
||||
if ((active.empty() || active[source]) && !MaterializeIndirectDescriptor(
|
||||
program, source, i, 4u, 4u, observed, clean, snapshot, snapshot.samplers,
|
||||
specialization.samplers, ShaderInfo::MaxSamplers,
|
||||
specialization.samplers,
|
||||
[](DescriptorValue&) { return true; }))
|
||||
return resource_failed("sampler", i, source, snapshot.samplers.size(), ShaderInfo::MaxSamplers);
|
||||
return resource_failed("sampler", i, source, snapshot.samplers.size());
|
||||
} else {
|
||||
if (indirect != nullptr) source = indirect->sources[0];
|
||||
if (!evaluate(source, snapshot.samplers[i])) return false;
|
||||
@@ -1835,8 +1829,8 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
|
||||
SamplerPlan sampler_plan;
|
||||
EXIT_IF(!BuildSamplerPlan(program.info, sampler_plan));
|
||||
const auto original_sampler_count = samplers.size();
|
||||
samplers.reserve(sampler_plan.sampler_count);
|
||||
for (uint32_t index = 0; index < sampler_plan.sampler_count; index++) {
|
||||
samplers.reserve(sampler_plan.bindings.size());
|
||||
for (uint32_t index = 0; index < sampler_plan.bindings.size(); index++) {
|
||||
const auto& binding = sampler_plan.bindings[index];
|
||||
if (index >= original_sampler_count) {
|
||||
samplers.push_back(samplers[binding.source]);
|
||||
@@ -1845,7 +1839,7 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
|
||||
samplers[index].force_point_filtering = binding.type == SamplerClass::Integer;
|
||||
samplers[index].integer_border = binding.type != SamplerClass::Float;
|
||||
}
|
||||
for (uint32_t index = 0; index < sampler_plan.sampler_count; index++) {
|
||||
for (uint32_t index = 0; index < sampler_plan.bindings.size(); index++) {
|
||||
const auto& binding = sampler_plan.bindings[index];
|
||||
for (auto& candidate: samplers[index].indirect_resources) {
|
||||
candidate = sampler_plan.mapping[candidate][static_cast<uint32_t>(binding.type)];
|
||||
|
||||
@@ -890,7 +890,7 @@ void TestNestedPointerImageTable() {
|
||||
"nested pointer descriptors lost their row geometry or captured the GPU selector payload");
|
||||
}
|
||||
|
||||
constexpr uint32_t table = 0x2000u, objects = 0x4000u, stride = 592u, count = 6u;
|
||||
constexpr uint32_t table = 0x2000u, objects = 0x10000u, stride = 592u, count = 59u;
|
||||
LinearTestMemory memory;
|
||||
memory.words.resize((objects + count * 0x430u - memory.base) / 4u);
|
||||
memory.fail_address = 0x1800u; // The GPU selector's contents are unavailable to the CPU.
|
||||
@@ -899,7 +899,8 @@ void TestNestedPointerImageTable() {
|
||||
const auto object = objects + row * 0x430u;
|
||||
*at(table + row * stride) = object;
|
||||
const std::array<uint32_t, 8> image{10u + row,
|
||||
uint32_t(Libs::Graphics::Prospero::BufferFormat::k32Float) << 20u, 0u,
|
||||
uint32_t(row == count - 1u ? Libs::Graphics::Prospero::BufferFormat::k32UInt
|
||||
: Libs::Graphics::Prospero::BufferFormat::k32Float) << 20u, 0u,
|
||||
Libs::Graphics::DstSel(4, 0, 0, 1) | (9u << 28u), 0u, 0u, 0u, 0u};
|
||||
std::copy(image.begin(), image.end(), at(object + 0x3d0u));
|
||||
const std::array<uint32_t, 4> sampler{row, 0x00fff000u, 0u, 0u};
|
||||
@@ -918,6 +919,21 @@ void TestNestedPointerImageTable() {
|
||||
Check(snapshot.images[row + 1u].dwords[0] == 10u + row &&
|
||||
snapshot.samplers[row + 1u].dwords[0] == row,
|
||||
"nested pointer table mixed rows or pointee members");
|
||||
auto typed = make();
|
||||
typed->PlanAndTrack();
|
||||
ApplyResourceSpecialization(typed->program, specialization);
|
||||
const auto& samplers = typed->program.info.samplers;
|
||||
Check(samplers.size() == 2u * (count + 1u) && samplers[0].indirect_resources.size() == count + 1u &&
|
||||
samplers[count + 1u].indirect_resources.size() == count + 1u &&
|
||||
snapshot.samplers[0].dwords[1] == 0u,
|
||||
"expanded sampler table lost its null root or numeric-class variants");
|
||||
for (uint32_t index = 0; index <= count; ++index) {
|
||||
const auto& integer = samplers[index + count + 1u];
|
||||
Check(samplers[index].snapshot_index == index && !samplers[index].integer_border &&
|
||||
integer.snapshot_index == index && integer.integer_border && integer.force_point_filtering &&
|
||||
samplers[count + 1u].indirect_resources[index] == index + count + 1u,
|
||||
"large sampler class mapping changed descriptor identity or integer filtering");
|
||||
}
|
||||
const auto populated = specialization;
|
||||
auto equivalent = make(0u, Address::Equivalent);
|
||||
equivalent->PlanAndTrack();
|
||||
@@ -940,11 +956,11 @@ void TestNestedPointerImageTable() {
|
||||
specialization == populated,
|
||||
"empty-to-positive pointer table refresh did not restore its specialization");
|
||||
*at(table) = objects + 0x430u;
|
||||
*at(objects + 0x430u + 0x3d0u) = 42u;
|
||||
*at(objects + 0x430u + 0x3f8u) = 7u;
|
||||
*at(objects + 0x430u + 0x3d0u) = 10u + count;
|
||||
*at(objects + 0x430u + 0x3f8u) = count;
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||
snapshot.images.size() == count && snapshot.samplers.size() == count &&
|
||||
snapshot.images[1].dwords[0] == 42u && snapshot.samplers[1].dwords[0] == 7u,
|
||||
snapshot.images[1].dwords[0] == 10u + count && snapshot.samplers[1].dwords[0] == count,
|
||||
"nested descriptor refresh retained stale pointers or failed to deduplicate new pointees");
|
||||
*at(table) = objects;
|
||||
|
||||
|
||||
@@ -39675,24 +39675,32 @@ void CheckPointerImageTable(VulkanHarness &vulkan) {
|
||||
|
||||
void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
|
||||
using O = ShaderOpcode;
|
||||
constexpr u32 list_address = 160, objects = 4096;
|
||||
constexpr std::array keys{5u, 0u, 3u, 1u, 4u, 2u};
|
||||
constexpr u32 list_address = 160;
|
||||
constexpr std::array small_keys{5u, 0u, 3u, 1u, 4u, 2u};
|
||||
constexpr std::array large_keys{58u, 0u, 33u, 31u, 34u, 32u, 59u, 65535u};
|
||||
constexpr std::array list{2u, 1u, 4u, 0u, 5u, 3u};
|
||||
for (const auto [member, counted] : {std::pair{0u, false}, std::pair{0x98u, false},
|
||||
std::pair{0x16cu, false}, std::pair{64u, false}, std::pair{64u, true}})
|
||||
for (const auto [member_mode, counted] : {std::pair{0u, false}, std::pair{0x98u, false},
|
||||
std::pair{0x16cu, false}, std::pair{64u, false}, std::pair{64u, true},
|
||||
std::pair{UINT32_MAX, false}})
|
||||
for (const auto [wave, upper_only] : {std::pair{32u, false}, std::pair{64u, false},
|
||||
std::pair{64u, true}}) {
|
||||
const bool large_samplers = member_mode == UINT32_MAX;
|
||||
const u32 member = large_samplers ? 0u : member_mode;
|
||||
const bool child_table = member == 64u, compact = member != 0u && !child_table;
|
||||
const std::span<const u32> keys = large_samplers ? std::span<const u32>(large_keys)
|
||||
: std::span<const u32>(small_keys);
|
||||
const u32 objects = large_samplers ? 65536u : 4096u;
|
||||
if (upper_only && !child_table) continue;
|
||||
const u32 keys_address = child_table ? 1024u : 128u, table = child_table ? 2048u : 256u;
|
||||
const u32 rows = child_table ? 4u : keys.size(), children = child_table ? 3u : 1u;
|
||||
const u32 rows = large_samplers ? 59u : child_table ? 4u : keys.size(), children = child_table ? 3u : 1u;
|
||||
const u32 records_count = counted ? 2u : rows, count_address = 768u;
|
||||
const u32 stride = child_table ? 136u : compact ? 0x330u : 592u;
|
||||
const u32 object_stride = child_table ? 1536u : 0x430u;
|
||||
const u32 table_bytes = (rows - 1u) * stride + member + 16u;
|
||||
const u32 outputs = child_table ? wave * 2u : keys.size();
|
||||
TestCase test;
|
||||
test.name = counted ? (upper_only ? "CountedPointerChildTableUpper64" : "CountedPointerChildTable")
|
||||
test.name = large_samplers ? "LargeIndirectSamplerTable"
|
||||
: counted ? (upper_only ? "CountedPointerChildTableUpper64" : "CountedPointerChildTable")
|
||||
: child_table ? (upper_only ? "IndexedPointerChildTableUpper64" : "IndexedPointerChildTable")
|
||||
: member == 0x16cu ? "CompactPairedImageSamplerTable"
|
||||
: compact ? "CompactRecordImageSamplerTable" : "NestedPointerImageSamplerTable";
|
||||
@@ -39718,7 +39726,8 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
|
||||
test.initial.begin() + (object + (child_table ? child * 368u + member
|
||||
: compact ? member : 0x3d0u)) / 4u);
|
||||
}
|
||||
const std::array sampler{0u, 0x00fff000u, (row & 1u) ? 0x0a500000u : 0u, 0u};
|
||||
const std::array sampler{large_samplers ? row : 0u, 0x00fff000u,
|
||||
(row & 1u) ? 0x0a500000u : 0u, 0u};
|
||||
std::copy(sampler.begin(), sampler.end(), test.initial.begin() +
|
||||
(child_table ? table + row * stride + 80u : object + (compact ? 0x88u : 0x3f8u)) / 4u);
|
||||
}
|
||||
@@ -39846,6 +39855,18 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
|
||||
} else if (!child_table) test.opcodes.insert(test.opcodes.end(),
|
||||
{O::S_BUFFER_LOAD_DWORDX2, O::S_LOAD_DWORDX8, O::S_LOAD_DWORDX4});
|
||||
const auto compiled = CompileCase(test, vulkan.SubgroupSize());
|
||||
if (large_samplers) {
|
||||
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2);
|
||||
std::string text;
|
||||
const auto halves = wave > vulkan.SubgroupSize() ? 2u : 1u;
|
||||
Require(test.name, "expanded sampler bindings and emitted size",
|
||||
compiled.resources.samplers.size() == rows + 1u &&
|
||||
compiled.program.info.samplers.size() == rows + 1u &&
|
||||
tools.Disassemble(compiled.spirv, &text) &&
|
||||
CountText(text, "OpImageSampleExplicitLod") == halves &&
|
||||
compiled.spirv.size() <= 8192u * halves,
|
||||
"59 sampler records hit the SGPR root limit or duplicated image sampling bodies");
|
||||
}
|
||||
if (child_table) {
|
||||
std::vector<u32> candidates;
|
||||
for (const auto &image : compiled.resources.images)
|
||||
@@ -39923,6 +39944,10 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
|
||||
test.expected[wave + group] = 0x76543210u;
|
||||
if (outer >= records_count || child >= children) continue;
|
||||
}
|
||||
if (!child_table && outer >= rows) {
|
||||
test.expected[group] = 0u;
|
||||
continue;
|
||||
}
|
||||
const u32 row = compact ? list[outer] : outer;
|
||||
test.expected[group] = std::bit_cast<u32>(float(10u + row * children + child) *
|
||||
((row & 1u) ? 0.5f : 1.0f));
|
||||
|
||||
Reference in New Issue
Block a user