mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
shader: share bounded record descriptors for packed loads and byte stores
Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
@@ -4293,7 +4293,7 @@ void TestBoundedFormattedBufferStore() {
|
||||
auto fixture = make();
|
||||
auto plan = ExtractResourcePlan(fixture->program);
|
||||
const auto& source = plan.descriptor_sources[plan.info.buffers[0].source];
|
||||
Check(source.indirect_descriptor && source.indirect_descriptor->indexed_store &&
|
||||
Check(source.indirect_descriptor && source.indirect_descriptor->indexed &&
|
||||
!source.indirect_descriptor->signed_count &&
|
||||
!source.indirect_descriptor->selector &&
|
||||
!source.indirect_descriptor->key_count.IsEmpty() &&
|
||||
@@ -4393,6 +4393,175 @@ void TestBoundedFormattedBufferStore() {
|
||||
"finite store accepted an unproved induction or indexed byte offset");
|
||||
}
|
||||
|
||||
void TestBoundedBufferRecords() {
|
||||
namespace CFG = Libs::Graphics::ShaderRecompiler::CFG;
|
||||
namespace Prospero = Libs::Graphics::Prospero;
|
||||
using Libs::Graphics::DstSel;
|
||||
const auto make = [](uint32_t step = 1u, bool bypass = false) {
|
||||
auto fixture = std::make_unique<Fixture>();
|
||||
auto& f = *fixture;
|
||||
auto* entry = f.block;
|
||||
auto* header = f.AddBlock();
|
||||
auto* body = f.AddBlock();
|
||||
auto* latch = f.AddBlock();
|
||||
auto* exit = f.AddBlock();
|
||||
const auto branch = [](Block* from, Block* to) {
|
||||
from->AddBranch(to);
|
||||
from->terminator = {.kind = CFG::TerminatorKind::Branch, .true_block = to};
|
||||
};
|
||||
branch(entry, header);
|
||||
branch(body, latch);
|
||||
branch(latch, header);
|
||||
header->AddBranch(body);
|
||||
header->AddBranch(bypass ? latch : exit);
|
||||
header->terminator = {.kind = CFG::TerminatorKind::ConditionalBranch,
|
||||
.true_block = body, .false_block = bypass ? latch : exit};
|
||||
exit->terminator.kind = CFG::TerminatorKind::Return;
|
||||
std::array<Value, 4> table_words;
|
||||
for (uint32_t word = 0; word < 4u; ++word) table_words[word] = f.UserData(word);
|
||||
const auto table = f.Buffer(table_words);
|
||||
const auto count = f.Emit(ValueOpcode::LoadAddressU32,
|
||||
{f.Address(f.UserData(4u), f.UserData(5u)), Value(0u), Value(0u), Value(true)},
|
||||
f.AddMemory({.kind = ResourceKind::ScalarAddress}, 0x424u));
|
||||
const auto lane = f.Emit(ValueOpcode::LaneId);
|
||||
auto& phi = header->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32));
|
||||
phi.AddPhiOperand(entry, Value(0u));
|
||||
phi.AddPhiOperand(latch, f.Emit(ValueOpcode::IAdd32, {Value(&phi), Value(step)}, 0, latch));
|
||||
header->condition = f.Emit(ValueOpcode::SLessThan32, {Value(&phi), count}, 0, header);
|
||||
f.block = body;
|
||||
const auto offset = f.Emit(ValueOpcode::IMul32, {Value(&phi), Value(192u)});
|
||||
for (const bool formatted : {true, false}) {
|
||||
std::array<Value, 4> words;
|
||||
for (uint32_t word = 0; word < 4u; ++word)
|
||||
words[word] = f.Emit(ValueOpcode::ReadConstBuffer, {table, offset},
|
||||
f.AddMemory({.kind = ResourceKind::ScalarBuffer,
|
||||
.offset = (formatted ? 160u : 176u) + word * 4u,
|
||||
.component_index = word, .component_count = 4u}, 0x1808u));
|
||||
for (uint32_t use = 0; use < (formatted ? 112u : 16u); ++use) {
|
||||
const auto handle = f.Buffer(words);
|
||||
if (formatted) {
|
||||
const auto loaded = f.Emit(ValueOpcode::LoadBufferU32x2,
|
||||
{handle, lane, Value(0u), Value(0u), Value(true)},
|
||||
f.AddMemory({.kind = ResourceKind::Buffer, .data_dwords = 2u, .data_bits = 16u,
|
||||
.component_count = 4u, .formatted = true, .idxen = true}, 0x1914u));
|
||||
f.Emit(ValueOpcode::ReferenceU32,
|
||||
{f.Emit(ValueOpcode::CompositeExtractU32x2, {loaded, Value(0u)})});
|
||||
} else {
|
||||
f.Emit(ValueOpcode::StoreBufferU8,
|
||||
{handle, lane, Value(0u), Value(0u), Value(uint8_t{42u}), Value(true)},
|
||||
f.AddMemory({.kind = ResourceKind::Buffer, .data_bits = 8u, .idxen = true}, 0x190cu));
|
||||
}
|
||||
}
|
||||
}
|
||||
f.PlanAndTrack();
|
||||
return fixture;
|
||||
};
|
||||
auto fixture = make();
|
||||
const auto plan = ExtractResourcePlan(fixture->program);
|
||||
Check(plan.info.buffers.size() == 2u, "record descriptors did not share their resource roots");
|
||||
uint32_t selections = 0u;
|
||||
std::array<const Inst*, 2> selected{};
|
||||
for (const auto* block : fixture->program.blocks) {
|
||||
for (const auto& inst : *block) {
|
||||
if (inst.GetOpcode() == ValueOpcode::GetBufferResourceIndex) {
|
||||
++selections;
|
||||
Check(block->id == 2u && inst.Flags<uint32_t>() < 2u,
|
||||
"record selection moved outside its loop or lost its buffer root");
|
||||
selected[inst.Flags<uint32_t>()] = &inst;
|
||||
}
|
||||
if (inst.GetOpcode() == ValueOpcode::LoadBufferU32x2 || inst.GetOpcode() == ValueOpcode::StoreBufferU8) {
|
||||
const auto root = fixture->program.memory_info[inst.Flags<MemoryFlags>().index].resource;
|
||||
Check(inst.Arg(0).Instruction()->Arg(0).Instruction() == selected[root],
|
||||
"record operation did not use its dominating shared selection");
|
||||
}
|
||||
}
|
||||
}
|
||||
Check(selections == 2u, "repeated record operations duplicated candidate mapping searches");
|
||||
for (uint32_t root = 0; root < 2u; ++root) {
|
||||
const auto& source = plan.descriptor_sources[plan.info.buffers[root].source];
|
||||
Check(source.indirect_descriptor && source.indirect_descriptor->indexed &&
|
||||
source.indirect_descriptor->signed_count &&
|
||||
source.indirect_descriptor->table_stride == 192u &&
|
||||
source.indirect_descriptor->table_immediate == (root == 0u ? 160u : 176u) &&
|
||||
!source.indirect_descriptor->key_count.IsEmpty(),
|
||||
"record descriptor lost its bounded loop or member offset");
|
||||
}
|
||||
LinearTestMemory memory;
|
||||
constexpr uint32_t table_offset = 256u;
|
||||
const auto format = static_cast<uint32_t>(Prospero::BufferFormat::k16_16_16_16Float);
|
||||
for (uint32_t row = 0; row < 4u; ++row) {
|
||||
const auto first = (table_offset + row * 192u) / 4u;
|
||||
std::fill_n(memory.words.begin() + first, 48u, 0xdeadbeefu);
|
||||
for (const bool formatted : {true, false}) {
|
||||
const auto word = first + (formatted ? 40u : 44u);
|
||||
memory.words[word] = 0x100000u + row * 0x1000u + (formatted ? 0u : 0x8000u);
|
||||
memory.words[word + 1u] = (formatted ? 8u : 3u) << 16u;
|
||||
memory.words[word + 2u] = 16u;
|
||||
memory.words[word + 3u] = formatted ? (format << 12u) | DstSel(4, 5, 6, 1) : 0x5204u;
|
||||
}
|
||||
}
|
||||
std::array<uint32_t, 6> user_data{uint32_t(memory.base) + table_offset, 8u << 16u,
|
||||
4u * 192u / 8u, 0x5204u, uint32_t(memory.base), 0u};
|
||||
SrtRuntime runtime{.user_data = user_data, .read_memory = ReadLinearTestMemory,
|
||||
.userdata = &memory, .read_specialization_memory = ReadLinearTestMemory};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
const auto materialize = [&] { return MaterializeResources(plan, runtime, snapshot, specialization); };
|
||||
for (const uint32_t count : {0u, 1u, 4u, 2u, UINT32_MAX, 0u, 4u}) {
|
||||
memory.words[0] = count;
|
||||
const uint32_t rows = count == UINT32_MAX ? 0u : count;
|
||||
user_data[2] = rows == 0u ? 0u : 4u * 192u / 8u;
|
||||
memory.reads = 0u;
|
||||
Check(materialize() && snapshot.buffers.size() == 2u * std::max(rows, 1u) &&
|
||||
memory.reads == 1u + rows * 2u,
|
||||
"bounded record enumeration read unrelated fields or retained stale candidates");
|
||||
for (uint32_t row = 0; row < rows; ++row)
|
||||
for (const auto member : {160u, 176u})
|
||||
Check(std::ranges::find(snapshot.specialization_reads,
|
||||
std::pair{memory.base + table_offset + row * 192u + member, uint64_t{16u}}) !=
|
||||
snapshot.specialization_reads.end(),
|
||||
"record descriptor escaped same-dispatch overlap capture");
|
||||
}
|
||||
const auto original = specialization;
|
||||
memory.words[(table_offset + 160u) / 4u] += 0x100u;
|
||||
Check(materialize() && specialization == original,
|
||||
"record address refresh changed the shader specialization");
|
||||
user_data[2] = 4u;
|
||||
memory.reads = 0u;
|
||||
Check(materialize() && snapshot.buffers.size() == 2u && memory.reads == 1u,
|
||||
"record enumeration read beyond the scalar buffer table bounds");
|
||||
user_data[2] = 4u * 192u / 8u;
|
||||
const auto first = (table_offset + 160u) / 4u;
|
||||
const auto first_address = memory.words[first];
|
||||
memory.words[first] = 0u;
|
||||
memory.words[first + 2u] = 0u;
|
||||
Check(materialize() && specialization.buffers[0].zero_stride_oob &&
|
||||
specialization.buffers[0].descriptor_swizzle == DstSel(4, 5, 6, 1) &&
|
||||
specialization.buffers[0].indirect_root == 0u,
|
||||
"empty formatted candidate lost its OOB constants or other candidates");
|
||||
ApplyResourceSpecialization(fixture->program, specialization);
|
||||
uint32_t loads = 0u;
|
||||
for (const auto* block : fixture->program.blocks)
|
||||
for (const auto& inst : *block) loads += inst.GetOpcode() == ValueOpcode::LoadBufferU32x2;
|
||||
Check(loads == 112u, "empty root incorrectly folded nonempty formatted candidates");
|
||||
memory.words[first] = first_address;
|
||||
memory.words[first + 2u] = 16u;
|
||||
for (const auto [word, replacement] : std::array<std::pair<uint32_t, uint32_t>, 5>{
|
||||
{{first + 1u, 4u << 16u}, {first + 1u, 9u << 16u},
|
||||
{first, memory.words[first] + 1u}, {first + 3u, memory.words[first + 3u] | (1u << 28u)},
|
||||
{first + 1u, (8u << 16u) | (1u << 31u)}}}) {
|
||||
const auto saved = memory.words[word];
|
||||
memory.words[word] = replacement;
|
||||
Check(!materialize(), "formatted record accepted unproved alignment or addressing");
|
||||
memory.words[word] = saved;
|
||||
}
|
||||
memory.words[0] = MaxIndirectDescriptorProbes + 1u;
|
||||
Check(!materialize(), "record table accepted an unbounded candidate count");
|
||||
for (const auto [step, bypass] : {std::pair{2u, false}, std::pair{1u, true}})
|
||||
CheckFatal([&] { make(step, bypass); }, "not a valid runtime value",
|
||||
"record table accepted an unproved loop bound");
|
||||
}
|
||||
|
||||
void TestFiniteBufferSelection() {
|
||||
enum class Variant { Valid, WiderExec, Unbounded, Cycle, TooMany };
|
||||
const auto make = [](Variant variant) {
|
||||
@@ -4516,7 +4685,7 @@ void TestPackedScalarBufferSelection() {
|
||||
fixture->PlanAndTrack();
|
||||
auto plan = ExtractResourcePlan(fixture->program);
|
||||
const auto& source = plan.descriptor_sources[plan.info.buffers[0].source];
|
||||
Check(source.indirect_descriptor && source.indirect_descriptor->indexed_store &&
|
||||
Check(source.indirect_descriptor && source.indirect_descriptor->indexed &&
|
||||
source.indirect_descriptor->selector->byte_ranges ==
|
||||
std::vector<std::pair<uint32_t, uint32_t>>{{0x1c0u, 1024u}},
|
||||
"packed scalar lookup lost a Phi arm or independent DWORD alignment");
|
||||
@@ -4571,8 +4740,8 @@ void TestPackedScalarBufferSelection() {
|
||||
mixed->PlanAndTrack();
|
||||
const auto& buffers = mixed->program.info.buffers;
|
||||
Check(buffers.size() == 2u && buffers[0].source != buffers[1].source &&
|
||||
mixed->program.descriptor_sources[buffers[0].source].indirect_descriptor->indexed_store &&
|
||||
!mixed->program.descriptor_sources[buffers[1].source].indirect_descriptor->indexed_store,
|
||||
mixed->program.descriptor_sources[buffers[0].source].indirect_descriptor->indexed &&
|
||||
!mixed->program.descriptor_sources[buffers[1].source].indirect_descriptor->indexed,
|
||||
"indexed and byte-addressed stores incorrectly shared their descriptor bounds contract");
|
||||
}
|
||||
|
||||
@@ -5888,6 +6057,7 @@ int main() {
|
||||
Run("dynamic SRT", TestDynamicSrtReadRemainsExplicit);
|
||||
Run("writable descriptor phi", TestWritableDescriptorPhi);
|
||||
Run("bounded formatted buffer store", TestBoundedFormattedBufferStore);
|
||||
Run("bounded buffer records", TestBoundedBufferRecords);
|
||||
Run("finite buffer selection", TestFiniteBufferSelection);
|
||||
Run("packed scalar buffer selection", TestPackedScalarBufferSelection);
|
||||
Run("conditional sampler phi", TestConditionalSamplerPhi);
|
||||
|
||||
@@ -36575,6 +36575,135 @@ void CheckBoundedFormattedBufferCopy(VulkanHarness &vulkan) {
|
||||
std::printf("[compute] %-32s ok\n", "BoundedFormattedBufferCopy");
|
||||
}
|
||||
|
||||
void CheckBoundedRecordBufferAccess(VulkanHarness &vulkan) {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
using F = Prospero::BufferFormat;
|
||||
const u32 alignment = vulkan.RuntimeContext().GetPhysicalDeviceProperties()
|
||||
.limits.minStorageBufferOffsetAlignment;
|
||||
const u32 slot = (4096u + alignment - 1u) / alignment * alignment;
|
||||
for (const u32 wave : {32u, 64u}) {
|
||||
for (const u32 count : {0u, 2u, 6u}) {
|
||||
TestCase test;
|
||||
test.name = wave == 32 ? "BoundedRecordBufferAccess32" : "BoundedRecordBufferAccess64";
|
||||
constexpr u32 table = 256u;
|
||||
test.initial.assign((slot * 13u + 6u * wave * 16u) / 4u, 0xdeadbeefu);
|
||||
test.has_user_data = test.has_compute_info = true;
|
||||
test.user_data[0] = table;
|
||||
test.user_data[1] = 8u << 16u;
|
||||
test.user_data[2] = 6u * 192u / 8u;
|
||||
test.user_data[3] = 0x5204u;
|
||||
test.user_data[4] = count;
|
||||
const std::array<u32, 4> output{slot * 13u, 16u << 16u, 6u * wave, 0x5204u};
|
||||
std::copy(output.begin(), output.end(), test.user_data.begin() + 16u);
|
||||
test.compute_info.wave_size = wave;
|
||||
test.compute_info.threads_num[0] = wave;
|
||||
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1u;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.compute_info.lds_size_dwords = 0;
|
||||
auto &code = test.code;
|
||||
code.insert(code.end(), {EncodeSMovB32(32, InlineU32(0)), EncodeSop1(0x04, 36, 126)});
|
||||
AppendVMovLiteral(&code, 13, 0x80000000u);
|
||||
code.insert(code.end(), {EncodeVopc(0xc2, InlineU32(6), 0),
|
||||
EncodeVop2(0x01, 12, Vgpr(0), 13)});
|
||||
AppendVMovLiteral(&code, 14, 0x55555556u);
|
||||
code.insert(code.end(), {EncodeVopc(0xc2, InlineU32(6), 0),
|
||||
EncodeVop2(0x01, 15, Vgpr(0), 14)});
|
||||
AppendVMovLiteral(&code, 10, 0xa5c39b72u);
|
||||
const auto header = code.size();
|
||||
code.push_back(EncodeSopc(0x04, 32, 4));
|
||||
const auto exit_branch = code.size();
|
||||
code.push_back(0);
|
||||
code.insert(code.end(), {EncodeSop2(0x26, 33, 32, 255), 192u,
|
||||
EncodeSmem0(0x0a, 8, 0), EncodeSmem1(160u, 33),
|
||||
EncodeSmem0(0x0a, 12, 0), EncodeSmem1(176u, 33), EncodeSopp(0x0c, 0),
|
||||
EncodeMubuf0(0x83, 0, true, false), EncodeMubuf1(4, 2, 12),
|
||||
EncodeMubuf0(0x83, 0, true, false, true), EncodeMubuf1(6, 2, 12),
|
||||
EncodeSopp(0x0c, 0), EncodeSop2(0x1e, 34, 32, InlineU32(std::countr_zero(wave))),
|
||||
EncodeVop2(0x25, 20, 34, 0),
|
||||
EncodeMubuf0(0x1e, 0, true, false), EncodeMubuf1(4, 4, 20)});
|
||||
AppendSMovLiteral(&code, 126, 0x55555555u);
|
||||
AppendSMovLiteral(&code, 127, wave == 64 ? 0x55555555u : 0u);
|
||||
code.insert(code.end(), {EncodeMubuf0(0x19, 0, true, false, true), EncodeMubuf1(10, 3, 15),
|
||||
EncodeSop1(0x04, 126, 36), EncodeSop2(0x02, 32, 32, InlineU32(1))});
|
||||
code.push_back(EncodeSopp(0x02, header - code.size() - 1u));
|
||||
code[exit_branch] = EncodeSopp(0x04, code.size() - exit_branch - 1u);
|
||||
AppendEnd(&code);
|
||||
for (u32 row = 0; row < 6u; ++row) {
|
||||
const u32 src = slot * (row + 1u) + (row + 1u) * 4u;
|
||||
const u32 dst = slot * (row + 7u) + row + 1u;
|
||||
const auto format = count == 6u && row == 0u ? F::k32_32_32_32Float :
|
||||
count == 6u && row == 1u ? F::k8_8_8_8UNorm : F::k16_16_16_16Float;
|
||||
const u32 swizzle = row == 2u ? DstSel(1, 4, 1, 4) : DstSel(4, 5, 6, 7);
|
||||
std::array<u32, 4> input{row == 2u ? 0u : src, 16u << 16u,
|
||||
row == 2u ? 0u : 2u, static_cast<u32>(format) << 12u | swizzle};
|
||||
if (row == 3u) input.fill(0u);
|
||||
const std::array<u32, 4> destination{dst, 3u << 16u, 16u, 0x5204u};
|
||||
std::copy(input.begin(), input.end(), test.initial.begin() + table / 4u + row * 48u + 40u);
|
||||
std::copy(destination.begin(), destination.end(), test.initial.begin() + table / 4u + row * 48u + 44u);
|
||||
for (u32 record = 0; record < 2u; ++record) {
|
||||
const auto word = src / 4u + record * 4u;
|
||||
if (format == F::k32_32_32_32Float) {
|
||||
const std::array<u32, 4> data{0x3f800000u, 0x3f000000u, 0u, 0xc0000000u};
|
||||
std::copy(data.begin(), data.end(), test.initial.begin() + word);
|
||||
} else if (format == F::k8_8_8_8UNorm) test.initial[word] = 0xffffffffu;
|
||||
else {
|
||||
test.initial[word] = 0x38003c00u;
|
||||
test.initial[word + 1u] = 0xc0000000u;
|
||||
}
|
||||
}
|
||||
}
|
||||
test.expected = test.initial;
|
||||
auto expected_bytes = std::as_writable_bytes(std::span(test.expected));
|
||||
for (u32 row = 0; row < count; ++row) {
|
||||
for (u32 lane = 0; lane < wave; ++lane) {
|
||||
std::array<u32, 2> value{};
|
||||
if (row == 2u) value = {0x3c00u, 0x3c00u};
|
||||
else if (row != 3u && lane < 2u && !(row == 5u && lane == 1u))
|
||||
value = count == 6u && row == 1u ? std::array<u32, 2>{0x3c003c00u, 0x3c003c00u}
|
||||
: std::array<u32, 2>{0x38003c00u, 0xc0000000u};
|
||||
const u32 out = slot * 13u / 4u + (row * wave + lane) * 4u;
|
||||
for (u32 word = 0; word < 4u; ++word) test.expected[out + word] = value[word % 2u];
|
||||
if (lane < 16u && lane != 6u && (lane & 1u) == 0u)
|
||||
expected_bytes[slot * (row + 7u) + row + 1u + lane * 3u] = std::byte{0xc3};
|
||||
}
|
||||
}
|
||||
if (count == 2u) {
|
||||
test.required_spirv = {"StorageBufferArrayNonUniformIndexing"};
|
||||
test.forbidden_spirv = {"OpSwitch", "PhysicalStorageBuffer"};
|
||||
} else if (count == 6u) test.required_spirv = {"OpSwitch"};
|
||||
auto compiled = CompileCase(test, vulkan.SubgroupSize());
|
||||
Require(test.name, "mixed native aliases", count != 6u ||
|
||||
(compiled.program.info.buffer_u8 && compiled.program.info.buffer_u16),
|
||||
"mixed candidates lost an 8-bit or 16-bit native binding");
|
||||
u32 selections = 0;
|
||||
for (auto *block : compiled.program.blocks)
|
||||
for (const auto &inst : *block) selections += inst.GetOpcode() == ValueOpcode::GetBufferResourceIndex;
|
||||
Require(test.name, "shared loop selection", count == 0u ? selections <= 2u : selections == 2u,
|
||||
"repeated formatted loads repeated the descriptor lookup");
|
||||
for (u32 resource = 0; resource < compiled.resources.buffers.size(); ++resource) {
|
||||
ShaderBufferResource descriptor{};
|
||||
std::copy_n(compiled.resources.buffers[resource].dwords.begin(), 4u, descriptor.fields);
|
||||
const u32 address = descriptor.Base48();
|
||||
const u32 prefix = address % alignment;
|
||||
u32 bytes = std::max(4u, static_cast<u32>(descriptor.GetSize()));
|
||||
if (address == slot * 5u + 20u) bytes = 24u;
|
||||
if (address == slot * 6u + 24u) bytes = 23u;
|
||||
test.storage_buffer_ranges.emplace_back(address - prefix, bytes + prefix);
|
||||
const u32 binding = compiled.program.info.buffers[resource].descriptor_index;
|
||||
if (binding != UINT32_MAX)
|
||||
compiled.packed_user_data[compiled.program.bindings.memory_offset_dword + binding / 4u] |=
|
||||
prefix << ((binding % 4u) * 8u);
|
||||
}
|
||||
auto output_buffer = vulkan.CreateStorageBuffer(test.name, test.initial, test.initial.size());
|
||||
vulkan.Dispatch(test, compiled, output_buffer);
|
||||
CompareWords(test, "row keys, mixed formats, empty-One, partial payloads, prefixes and byte canaries",
|
||||
test.expected, vulkan.ReadBuffer(test.name, output_buffer, test.expected.size()));
|
||||
vulkan.DestroyBuffer(&output_buffer);
|
||||
}
|
||||
}
|
||||
std::printf("[compute] %-32s ok\n", "BoundedRecordBufferAccess");
|
||||
}
|
||||
|
||||
void CheckFiniteBufferSelection(VulkanHarness &vulkan) {
|
||||
for (const u32 wave : {32u, 64u}) {
|
||||
TestCase test;
|
||||
@@ -36663,9 +36792,11 @@ void CheckIndirectBufferStore(VulkanHarness &vulkan) {
|
||||
{Value(&lane), Value(7u)});
|
||||
auto &key = block->AppendNewInst(ValueOpcode::IAdd32,
|
||||
{Value(&selector), Value(8u)});
|
||||
auto &selection = block->AppendNewInst(ValueOpcode::GetBufferResourceIndex, {Value(&key)});
|
||||
selection.SetFlags<uint32_t>(0u);
|
||||
auto &handle = block->AppendNewInst(
|
||||
ValueOpcode::GetBufferResource,
|
||||
{Value(&key), Value(0u), Value(0u), Value(0u)});
|
||||
{Value(&selection), Value(0u), Value(0u), Value(0u)});
|
||||
handle.SetFlags<uint32_t>(0u);
|
||||
auto &lane_offset = block->AppendNewInst(ValueOpcode::IMul32,
|
||||
{Value(&lane), Value(4u)});
|
||||
@@ -36715,7 +36846,7 @@ void CheckIndirectBufferStore(VulkanHarness &vulkan) {
|
||||
if (indexed) {
|
||||
for (auto &buffer : program.info.buffers) buffer.packed_stride = 4;
|
||||
}
|
||||
program.descriptor_sources[0].indirect_descriptor->indexed_store = indexed;
|
||||
program.descriptor_sources[0].indirect_descriptor->indexed = indexed;
|
||||
program.memory_info[0].idxen = indexed;
|
||||
program.memory_info[0].offen = !indexed;
|
||||
store.SetArg(1, indexed ? Value(&index) : Value(0u));
|
||||
@@ -45378,6 +45509,7 @@ int main(int argc, char **argv) {
|
||||
CheckBoundedFormattedBufferCopy(vulkan);
|
||||
CheckFiniteBufferSelection(vulkan);
|
||||
CheckIndirectBufferStore(vulkan);
|
||||
CheckBoundedRecordBufferAccess(vulkan);
|
||||
CheckRuntimeBufferRecords(vulkan);
|
||||
RunCase(&vulkan, BufferLoadsGpuSelectedDescriptors());
|
||||
RunCase(&vulkan, BufferLoadDwordGpuSelectedDescriptors());
|
||||
@@ -46079,6 +46211,7 @@ int main(int argc, char **argv) {
|
||||
CheckRectListShaders();
|
||||
CheckBoundedFormattedBufferCopy(vulkan);
|
||||
CheckIndirectBufferStore(vulkan);
|
||||
CheckBoundedRecordBufferAccess(vulkan);
|
||||
CheckFiniteBufferSelection(vulkan);
|
||||
CheckRuntimeBufferRecords(vulkan);
|
||||
CheckComputeThreadDimensions(vulkan);
|
||||
|
||||
Reference in New Issue
Block a user