mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-24 12:56:53 +02:00
shader: initialize EXEC from requested dispatch thread extents
Co-authored-by: TheCruZ <thecruzwtf@gmail.com> Co-authored-by: Mac <me@itsmemac.com>
This commit is contained in:
@@ -302,6 +302,10 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
m_context.GetPipelineCache().GetComputePipeline(input_info, compute_program);
|
m_context.GetPipelineCache().GetComputePipeline(input_info, compute_program);
|
||||||
auto& bindings = m_compute_bindings;
|
auto& bindings = m_compute_bindings;
|
||||||
PrepareBindings(input_info.stage, bindings);
|
PrepareBindings(input_info.stage, bindings);
|
||||||
|
if (program.bindings.dispatch_thread_dword != ShaderRecompiler::IR::PushData::NoStart) {
|
||||||
|
std::copy(std::begin(input_info.dispatch_threads_num), std::end(input_info.dispatch_threads_num),
|
||||||
|
bindings.shader_data.begin() + program.bindings.dispatch_thread_dword);
|
||||||
|
}
|
||||||
FindBuffers(bindings);
|
FindBuffers(bindings);
|
||||||
if (program.info.uses_dma) {
|
if (program.info.uses_dma) {
|
||||||
m_context.PrepareBda();
|
m_context.PrepareBda();
|
||||||
|
|||||||
@@ -85,9 +85,13 @@ void ValidateNativeProgram(const IR::Program& program) {
|
|||||||
}
|
}
|
||||||
const auto has_shader_data_storage = present[static_cast<size_t>(Kind::ShaderData)];
|
const auto has_shader_data_storage = present[static_cast<size_t>(Kind::ShaderData)];
|
||||||
const auto shader_data_dwords = program.bindings.ShaderDataDwords();
|
const auto shader_data_dwords = program.bindings.ShaderDataDwords();
|
||||||
|
const auto user_data_dwords = program.bindings.user_data_registers.size();
|
||||||
|
const bool has_dispatch_threads = program.bindings.dispatch_thread_dword != IR::PushData::NoStart;
|
||||||
if ((program.bindings.UsesPushData() &&
|
if ((program.bindings.UsesPushData() &&
|
||||||
!IR::PushData::CanFit(program.bindings.push_data_start_dword, shader_data_dwords)) ||
|
!IR::PushData::CanFit(program.bindings.push_data_start_dword, shader_data_dwords)) ||
|
||||||
program.bindings.memory_offset_dword != program.bindings.user_data_registers.size() ||
|
(has_dispatch_threads && (program.stage != ShaderType::Compute ||
|
||||||
|
program.bindings.dispatch_thread_dword != user_data_dwords)) ||
|
||||||
|
program.bindings.memory_offset_dword != user_data_dwords + (has_dispatch_threads ? 3u : 0u) ||
|
||||||
program.bindings.memory_offset_count != buffers.size() ||
|
program.bindings.memory_offset_count != buffers.size() ||
|
||||||
has_shader_data_storage != (shader_data_dwords != 0 && !program.bindings.UsesPushData()) ||
|
has_shader_data_storage != (shader_data_dwords != 0 && !program.bindings.UsesPushData()) ||
|
||||||
!std::is_sorted(program.bindings.user_data_registers.begin(),
|
!std::is_sorted(program.bindings.user_data_registers.begin(),
|
||||||
|
|||||||
@@ -631,6 +631,14 @@ uint32_t EmitGetBuiltin(ValueEmitContext& ctx, IR::Value kind, IR::Value index)
|
|||||||
return EmitBuiltinU32(ctx.state, static_cast<IR::StageInputKind>(kind.U32()), index.U32());
|
return EmitBuiltinU32(ctx.state, static_cast<IR::StageInputKind>(kind.U32()), index.U32());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t EmitGetDispatchThreadExtent(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||||
|
const auto start = ctx.state.program.bindings.dispatch_thread_dword;
|
||||||
|
if (start == IR::PushData::NoStart || !inst.Arg(0).IsImmediate() || inst.Arg(0).U32() >= 3u) {
|
||||||
|
ctx.Fail(inst, "invalid dispatch thread extent");
|
||||||
|
}
|
||||||
|
return EmitShaderDataDwordLoad(ctx.state, start + inst.Arg(0).U32());
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t EmitUndefU1(EmitterState& state, const IR::Inst& inst) {
|
uint32_t EmitUndefU1(EmitterState& state, const IR::Inst& inst) {
|
||||||
const auto result = state.builder.AllocateId();
|
const auto result = state.builder.AllocateId();
|
||||||
state.builder.AddFunction(spv::OpUndef, TypeId(state, inst.GetType()), result);
|
state.builder.AddFunction(spv::OpUndef, TypeId(state, inst.GetType()), result);
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ uint32_t EmitMeshDrawParameter(ValueEmitContext& ctx, const IR::Ins
|
|||||||
uint32_t EmitGetTessellationAttribute(ValueEmitContext& ctx, const IR::Inst& inst);
|
uint32_t EmitGetTessellationAttribute(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
void EmitSetTessellationAttribute(ValueEmitContext& ctx, const IR::Inst& inst);
|
void EmitSetTessellationAttribute(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
uint32_t EmitGetUserData(EmitterState& state, IR::ScalarReg reg);
|
uint32_t EmitGetUserData(EmitterState& state, IR::ScalarReg reg);
|
||||||
|
uint32_t EmitGetDispatchThreadExtent(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
uint32_t EmitGetBuiltin(ValueEmitContext& ctx, IR::Value kind, IR::Value index);
|
uint32_t EmitGetBuiltin(ValueEmitContext& ctx, IR::Value kind, IR::Value index);
|
||||||
uint32_t EmitUndefU1(EmitterState& state, const IR::Inst& inst);
|
uint32_t EmitUndefU1(EmitterState& state, const IR::Inst& inst);
|
||||||
inline constexpr auto EmitUndefU8 = EmitUndefU1;
|
inline constexpr auto EmitUndefU8 = EmitUndefU1;
|
||||||
|
|||||||
@@ -1104,6 +1104,16 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
|||||||
IR::U32(IR::Value(total_threads)));
|
IR::U32(IR::Value(total_threads)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (options.stage == ShaderType::Compute &&
|
||||||
|
options.input_info.compute->dispatch_thread_dimensions) {
|
||||||
|
for (uint32_t axis = 0; axis < 3u; axis++) {
|
||||||
|
const auto extent = IR::U32(entry_ir.Emit(
|
||||||
|
IR::ValueOpcode::GetDispatchThreadExtent, {IR::Value(axis)}));
|
||||||
|
initial_exec = entry_ir.LogicalAnd(
|
||||||
|
initial_exec,
|
||||||
|
entry_ir.ULessThan(builtin(IR::StageInputKind::GlobalInvocationId, axis), extent));
|
||||||
|
}
|
||||||
|
}
|
||||||
entry_ir.SetExec(initial_exec);
|
entry_ir.SetExec(initial_exec);
|
||||||
const auto initial_mask = entry_ir.Emit(IR::ValueOpcode::Ballot, {initial_exec});
|
const auto initial_mask = entry_ir.Emit(IR::ValueOpcode::Ballot, {initial_exec});
|
||||||
entry_ir.SetExecLo(entry_ir.CompositeExtract(initial_mask, 0));
|
entry_ir.SetExecLo(entry_ir.CompositeExtract(initial_mask, 0));
|
||||||
|
|||||||
@@ -426,6 +426,7 @@ struct DescriptorBinding {
|
|||||||
|
|
||||||
struct BindingLayout {
|
struct BindingLayout {
|
||||||
uint32_t push_data_start_dword = PushData::NoStart;
|
uint32_t push_data_start_dword = PushData::NoStart;
|
||||||
|
uint32_t dispatch_thread_dword = PushData::NoStart;
|
||||||
uint32_t memory_offset_dword = 0;
|
uint32_t memory_offset_dword = 0;
|
||||||
uint32_t memory_offset_count = 0;
|
uint32_t memory_offset_count = 0;
|
||||||
std::vector<uint32_t> user_data_registers;
|
std::vector<uint32_t> user_data_registers;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ VALUE_OPCODE(ConditionRef, U1, U1)
|
|||||||
|
|
||||||
// Translation and register-state pseudo operations.
|
// Translation and register-state pseudo operations.
|
||||||
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
||||||
|
VALUE_OPCODE(GetDispatchThreadExtent, U32, U32)
|
||||||
VALUE_OPCODE(GetShaderBase, U64)
|
VALUE_OPCODE(GetShaderBase, U64)
|
||||||
VALUE_OPCODE(MeshDrawParameter, U32, U32)
|
VALUE_OPCODE(MeshDrawParameter, U32, U32)
|
||||||
VALUE_OPCODE(MeshAllocate, Void, U32)
|
VALUE_OPCODE(MeshAllocate, Void, U32)
|
||||||
|
|||||||
@@ -15,11 +15,16 @@ namespace {
|
|||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> CollectUserData(const Program& program) {
|
void CollectShaderData(const Program& program, BindingLayout& layout) {
|
||||||
std::array<bool, NumScalarRegs> registers {};
|
std::array<bool, NumScalarRegs> registers {};
|
||||||
|
bool uses_dispatch_threads = false;
|
||||||
for (const auto* block: program.blocks) {
|
for (const auto* block: program.blocks) {
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) {
|
if (!inst.HasUses()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uses_dispatch_threads |= inst.GetOpcode() == ValueOpcode::GetDispatchThreadExtent;
|
||||||
|
if (inst.GetOpcode() != ValueOpcode::GetUserData) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (inst.Arg(0).GetType() != Type::ScalarReg) {
|
if (inst.Arg(0).GetType() != Type::ScalarReg) {
|
||||||
@@ -32,13 +37,16 @@ std::vector<uint32_t> CollectUserData(const Program& program) {
|
|||||||
registers[index] = true;
|
registers[index] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<uint32_t> result;
|
|
||||||
for (uint32_t index = 0; index < registers.size(); index++) {
|
for (uint32_t index = 0; index < registers.size(); index++) {
|
||||||
if (registers[index]) {
|
if (registers[index]) {
|
||||||
result.push_back(index);
|
layout.user_data_registers.push_back(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
layout.memory_offset_dword = static_cast<uint32_t>(layout.user_data_registers.size());
|
||||||
|
if (uses_dispatch_threads) {
|
||||||
|
layout.dispatch_thread_dword = layout.memory_offset_dword;
|
||||||
|
layout.memory_offset_dword += 3u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
||||||
@@ -109,8 +117,7 @@ void AllocateBindings(Program& program, uint32_t push_data_start_dword) {
|
|||||||
BindingLayout next;
|
BindingLayout next;
|
||||||
std::vector<uint32_t> buffers;
|
std::vector<uint32_t> buffers;
|
||||||
const bool uses_gds = CollectMemoryResources(program, buffers);
|
const bool uses_gds = CollectMemoryResources(program, buffers);
|
||||||
next.user_data_registers = CollectUserData(program);
|
CollectShaderData(program, next);
|
||||||
next.memory_offset_dword = static_cast<uint32_t>(next.user_data_registers.size());
|
|
||||||
next.memory_offset_count = static_cast<uint32_t>(buffers.size());
|
next.memory_offset_count = static_cast<uint32_t>(buffers.size());
|
||||||
next.push_data_start_dword =
|
next.push_data_start_dword =
|
||||||
PushData::StartFor(push_data_start_dword, next.ShaderDataDwords());
|
PushData::StartFor(push_data_start_dword, next.ShaderDataDwords());
|
||||||
|
|||||||
@@ -14152,6 +14152,13 @@ public:
|
|||||||
vk::Sampler sampler = nullptr) {
|
vk::Sampler sampler = nullptr) {
|
||||||
using Kind = ShaderRecompiler::IR::DescriptorBindingKind;
|
using Kind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||||
const auto &layout = compiled.program.bindings;
|
const auto &layout = compiled.program.bindings;
|
||||||
|
auto shader_data = compiled.packed_user_data;
|
||||||
|
if (layout.dispatch_thread_dword != ShaderRecompiler::IR::PushData::NoStart) {
|
||||||
|
Require(test.name, "dispatch dimensions", test.has_compute_info,
|
||||||
|
"runtime thread extents are required by the compiled shader");
|
||||||
|
std::copy_n(test.compute_info.dispatch_threads_num, 3,
|
||||||
|
shader_data.begin() + layout.dispatch_thread_dword);
|
||||||
|
}
|
||||||
auto Binding = [&](Kind kind) {
|
auto Binding = [&](Kind kind) {
|
||||||
return ShaderRecompiler::IR::FindBinding(layout, kind);
|
return ShaderRecompiler::IR::FindBinding(layout, kind);
|
||||||
};
|
};
|
||||||
@@ -14378,8 +14385,7 @@ public:
|
|||||||
}
|
}
|
||||||
if (const auto *user = Binding(Kind::ShaderData); user != nullptr) {
|
if (const auto *user = Binding(Kind::ShaderData); user != nullptr) {
|
||||||
user_data_buffer =
|
user_data_buffer =
|
||||||
CreateStorageBuffer(test.name, compiled.packed_user_data,
|
CreateStorageBuffer(test.name, shader_data, shader_data.size());
|
||||||
compiled.packed_user_data.size());
|
|
||||||
user_data_info = {user_data_buffer.buffer, 0, user_data_buffer.size};
|
user_data_info = {user_data_buffer.buffer, 0, user_data_buffer.size};
|
||||||
vk::WriteDescriptorSet write{};
|
vk::WriteDescriptorSet write{};
|
||||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||||
@@ -14574,8 +14580,7 @@ public:
|
|||||||
1, &descriptor_set, 0, nullptr);
|
1, &descriptor_set, 0, nullptr);
|
||||||
if (layout.UsesPushData()) {
|
if (layout.UsesPushData()) {
|
||||||
ShaderRecompiler::IR::PushData push_data;
|
ShaderRecompiler::IR::PushData push_data;
|
||||||
std::copy(compiled.packed_user_data.begin(),
|
std::copy(shader_data.begin(), shader_data.end(),
|
||||||
compiled.packed_user_data.end(),
|
|
||||||
push_data.dwords.begin() + layout.push_data_start_dword);
|
push_data.dwords.begin() + layout.push_data_start_dword);
|
||||||
cmd.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0,
|
cmd.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0,
|
||||||
sizeof(push_data), push_data.dwords.data());
|
sizeof(push_data), push_data.dwords.data());
|
||||||
@@ -30601,6 +30606,94 @@ TestCase ImageD16StoreUnpacksHalfPairs() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckComputeThreadDimensions(VulkanHarness &vulkan) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
constexpr u32 grid_size = 16 * 8 * 4;
|
||||||
|
const std::array extents = {std::array{9u, 5u, 3u}, std::array{13u, 7u, 4u},
|
||||||
|
std::array{3u, 2u, 1u}, std::array{16u, 8u, 4u}};
|
||||||
|
for (const u32 wave_size : {32u, 64u}) {
|
||||||
|
for (const bool thread_dimensions : {false, true}) {
|
||||||
|
TestCase test;
|
||||||
|
test.name = thread_dimensions ? (wave_size == 32 ? "ThreadDimensions32" : "ThreadDimensions64")
|
||||||
|
: (wave_size == 32 ? "GroupDimensions32" : "GroupDimensions64");
|
||||||
|
auto &code = test.code;
|
||||||
|
code.push_back(EncodeSop1(0x04, 20, 126)); // Save the SPI-provided initial EXEC.
|
||||||
|
AppendSMovLiteral(&code, 24, grid_size * 4);
|
||||||
|
for (u32 axis = 0; axis < 3; ++axis) {
|
||||||
|
code.push_back(EncodeVop1(0x01, 3 + axis, 4 + axis));
|
||||||
|
code.push_back(EncodeVop2(0x1a, 3 + axis, InlineU32(3 - axis), 3 + axis));
|
||||||
|
code.push_back(EncodeVop2(0x25, 3 + axis, Vgpr(axis), 3 + axis));
|
||||||
|
}
|
||||||
|
code.push_back(EncodeVop2(0x1a, 4, InlineU32(4), 4));
|
||||||
|
code.push_back(EncodeVop2(0x1a, 5, InlineU32(7), 5));
|
||||||
|
code.push_back(EncodeVop2(0x25, 3, Vgpr(4), 3));
|
||||||
|
code.push_back(EncodeVop2(0x25, 3, Vgpr(5), 3));
|
||||||
|
code.push_back(EncodeVop2(0x25, 20, InlineU32(1), 3));
|
||||||
|
code.push_back(EncodeVop2(0x1a, 20, InlineU32(2), 20));
|
||||||
|
AppendVMovU32(&code, 6, 1);
|
||||||
|
AppendVMovU32(&code, 7, 0);
|
||||||
|
AppendBufferStoreDword(&code, 6, 20);
|
||||||
|
AppendBufferStoreOpcode(&code, 0x32, 6, 7);
|
||||||
|
code.push_back(EncodeSop2(0x0f, 126, 20, InlineU32(1)));
|
||||||
|
AppendVMovU32(&code, 6, 2); // Divergence changes only lane zero of each wave.
|
||||||
|
code.push_back(EncodeSop1(0x04, 126, 20));
|
||||||
|
code.push_back(EncodeVop2(0x25, 20, 24, 20));
|
||||||
|
AppendBufferStoreDword(&code, 6, 20);
|
||||||
|
AppendVMovU32(&code, 8, 1);
|
||||||
|
AppendBufferStoreOpcode(&code, 0x32, 8, 7);
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.initial.resize(1 + 2 * grid_size);
|
||||||
|
test.opcodes = {O::S_MOV_B64, O::S_MOV_B32, O::S_AND_B64, O::V_MOV_B32,
|
||||||
|
O::V_LSHLREV_B32, O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD,
|
||||||
|
O::BUFFER_ATOMIC_ADD, O::S_ENDPGM};
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.compute_info.host_subgroup_size = vulkan.SubgroupSize();
|
||||||
|
test.compute_info.threads_num[0] = 8;
|
||||||
|
test.compute_info.threads_num[1] = 4;
|
||||||
|
test.compute_info.threads_num[2] = 2;
|
||||||
|
std::fill_n(test.compute_info.group_id, 3, true);
|
||||||
|
test.compute_info.thread_ids_num = 3;
|
||||||
|
test.compute_info.workgroup_register = 4;
|
||||||
|
test.compute_info.dispatch_thread_dimensions = thread_dimensions;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
test.expand_shader_data_storage = thread_dimensions && wave_size == 32;
|
||||||
|
const auto compiled = CompileCase(test, vulkan.SubgroupSize());
|
||||||
|
std::vector<u32> compiled_key;
|
||||||
|
BuildStageStaticKey(test.compute_info, compiled_key);
|
||||||
|
for (const auto &extent : extents) {
|
||||||
|
std::copy(extent.begin(), extent.end(), test.compute_info.dispatch_threads_num);
|
||||||
|
std::vector<u32> runtime_key;
|
||||||
|
BuildStageStaticKey(test.compute_info, runtime_key);
|
||||||
|
Require(test.name, "runtime shader reuse", compiled_key == runtime_key,
|
||||||
|
"dispatch thread counts changed the compiled shader cache key");
|
||||||
|
test.dispatch_x = thread_dimensions ? (extent[0] + 7) / 8 : 2;
|
||||||
|
test.dispatch_y = thread_dimensions ? (extent[1] + 3) / 4 : 2;
|
||||||
|
test.dispatch_z = thread_dimensions ? (extent[2] + 1) / 2 : 2;
|
||||||
|
test.expected.assign(test.initial.size(), 0);
|
||||||
|
for (u32 z = 0; z < 4; ++z) {
|
||||||
|
for (u32 y = 0; y < 8; ++y) {
|
||||||
|
for (u32 x = 0; x < 16; ++x) {
|
||||||
|
if (thread_dimensions && (x >= extent[0] || y >= extent[1] || z >= extent[2])) continue;
|
||||||
|
const auto index = x + 16 * y + 128 * z;
|
||||||
|
const auto local_index = x % 8 + 8 * (y % 4) + 32 * (z % 2);
|
||||||
|
test.expected[0] += 2;
|
||||||
|
test.expected[1 + index] = 1;
|
||||||
|
test.expected[1 + grid_size + index] = local_index % wave_size == 0 ? 2 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto output = vulkan.CreateStorageBuffer(test.name, test.initial, test.initial.size());
|
||||||
|
vulkan.Dispatch(test, compiled, output);
|
||||||
|
const auto actual = vulkan.ReadBuffer(test.name, output, test.expected.size());
|
||||||
|
vulkan.DestroyBuffer(&output);
|
||||||
|
CompareWords(test, "initial EXEC, restored EXEC and padded-lane atomics", test.expected, actual);
|
||||||
|
if (!thread_dimensions) break;
|
||||||
|
}
|
||||||
|
std::printf("[compute] %-32s ok\n", test.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CheckIndirectBufferStore(VulkanHarness &vulkan) {
|
void CheckIndirectBufferStore(VulkanHarness &vulkan) {
|
||||||
using namespace ShaderRecompiler::IR;
|
using namespace ShaderRecompiler::IR;
|
||||||
TestCase test;
|
TestCase test;
|
||||||
@@ -37246,6 +37339,11 @@ int main(int argc, char **argv) {
|
|||||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
EnsureConfigInitialized();
|
EnsureConfigInitialized();
|
||||||
CheckLeastRecentlyUsedCacheOrdering();
|
CheckLeastRecentlyUsedCacheOrdering();
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--thread-dimensions-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
CheckComputeThreadDimensions(vulkan);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--lds-limit-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--lds-limit-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
CheckComputeLdsLimit(vulkan);
|
CheckComputeLdsLimit(vulkan);
|
||||||
@@ -38006,6 +38104,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckPixelParameterAliases();
|
CheckPixelParameterAliases();
|
||||||
CheckRectListShaders();
|
CheckRectListShaders();
|
||||||
CheckIndirectBufferStore(vulkan);
|
CheckIndirectBufferStore(vulkan);
|
||||||
|
CheckComputeThreadDimensions(vulkan);
|
||||||
CheckIndirectImageKeySwitch();
|
CheckIndirectImageKeySwitch();
|
||||||
CheckWave64WholeWaveResults();
|
CheckWave64WholeWaveResults();
|
||||||
CheckPs5GameExampleImageClearRuntimeShape();
|
CheckPs5GameExampleImageClearRuntimeShape();
|
||||||
|
|||||||
Reference in New Issue
Block a user