mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: drop null checks on the stage input info pointers
This commit is contained in:
@@ -209,20 +209,20 @@ int BufferTableAttribFromOffset(uint32_t raw_offset, int dword) {
|
||||
}
|
||||
|
||||
Frontend::EmbeddedFetchPlan DetectEmbeddedVertexFetch(
|
||||
const Decoder::Program& decoded, const ShaderVertexInputInfo* input_info,
|
||||
const Decoder::Program& decoded, const ShaderVertexInputInfo& input_info,
|
||||
uint32_t user_data_base, uint32_t user_data_count, uint32_t wave_size) {
|
||||
const uint32_t vertex_index_reg = input_info->logical_stage == ShaderType::Local ? 2u : 5u;
|
||||
const uint32_t instance_index_reg = input_info->logical_stage == ShaderType::Local ? 5u : 8u;
|
||||
const uint32_t vertex_index_reg = input_info.logical_stage == ShaderType::Local ? 2u : 5u;
|
||||
const uint32_t instance_index_reg = input_info.logical_stage == ShaderType::Local ? 5u : 8u;
|
||||
Frontend::EmbeddedFetchPlan data;
|
||||
data.loads.reserve(input_info->resources_num);
|
||||
data.loads.reserve(input_info.resources_num);
|
||||
int32_t vertex_offset_candidate = -1;
|
||||
int32_t instance_offset_candidate = -1;
|
||||
bool vertex_offset_conflict = false;
|
||||
bool instance_offset_conflict = false;
|
||||
|
||||
const int shift_regs = 8;
|
||||
const int attrib_reg = input_info->fetch_attrib_reg + shift_regs;
|
||||
const int buffer_reg = input_info->fetch_buffer_reg + shift_regs;
|
||||
const int attrib_reg = input_info.fetch_attrib_reg + shift_regs;
|
||||
const int buffer_reg = input_info.fetch_buffer_reg + shift_regs;
|
||||
|
||||
std::array<EmbeddedFetchSgprInfo, 108> sgprs {};
|
||||
std::array<bool, 256> vgpr_is_index {};
|
||||
@@ -488,7 +488,6 @@ void PrepareCallTargets(ShaderSource& source, const CompileOptions& options) {
|
||||
if (source.decoded.instructions[i].opcode != Decoder::Opcode::S_SWAPPC_B64) continue;
|
||||
source.calls.push_back({.instruction = i});
|
||||
}
|
||||
EXIT_IF(options.input_info.compute == nullptr);
|
||||
const auto graph = CFG::BuildGraph(source.decoded);
|
||||
Program query;
|
||||
query.stage = options.stage;
|
||||
@@ -809,9 +808,9 @@ TranslateResult TranslateProgram(const Decoder::Program& decoded, const CompileO
|
||||
const auto& cfg = *selected_cfg;
|
||||
Frontend::EmbeddedFetchPlan embedded_fetch;
|
||||
if ((options.stage == ShaderType::Vertex || options.stage == ShaderType::Local) &&
|
||||
options.input_info.vertex != nullptr && options.input_info.vertex->fetch_embedded) {
|
||||
options.input_info.vertex->fetch_embedded) {
|
||||
embedded_fetch = DetectEmbeddedVertexFetch(
|
||||
decoded, options.input_info.vertex, options.user_data_base,
|
||||
decoded, *options.input_info.vertex, options.user_data_base,
|
||||
static_cast<uint32_t>(options.user_data.size()), options.wave_size);
|
||||
if (!embedded_fetch.loads.empty()) {
|
||||
LOGF("%s embedded vertex fetch plan: detected=%" PRIu64 "\n", GetDumpLabel(options),
|
||||
|
||||
@@ -233,8 +233,8 @@ std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo inp
|
||||
? 2u
|
||||
: 1u;
|
||||
DefineModule(state);
|
||||
ValidateNativeProgram(program, program.stage == ShaderType::Compute &&
|
||||
input_info.compute != nullptr && input_info.compute->lds_storage);
|
||||
ValidateNativeProgram(program,
|
||||
program.stage == ShaderType::Compute && input_info.compute->lds_storage);
|
||||
EmitProgram(state);
|
||||
state.builder.AddEntryPoint(ExecutionModelForStage(state.program.stage), state.main_func,
|
||||
"main", state.interface_variables);
|
||||
|
||||
@@ -53,8 +53,7 @@ uint32_t EmitBuiltinU32(EmitterState& state, IR::StageInputKind kind, uint32_t c
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLoad, TypeBool(state), value, variable);
|
||||
// SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS selects uint masks or float +/-1.
|
||||
const bool all_bits =
|
||||
state.input_info.pixel != nullptr && state.input_info.pixel->ps_front_face_all_bits;
|
||||
const bool all_bits = state.input_info.pixel->ps_front_face_all_bits;
|
||||
const auto front = ConstantU32(state, all_bits ? 0xffffffffu : 0x3f800000u);
|
||||
const auto back = ConstantU32(state, all_bits ? 0u : 0xbf800000u);
|
||||
state.builder.AddFunction(spv::OpSelect, TypeU32(state), bits, value, front, back);
|
||||
|
||||
@@ -80,7 +80,7 @@ struct EmitterState {
|
||||
lds_storage_class = spv::StorageClassWorkgroup;
|
||||
}
|
||||
if (program.info.uses_lds && program.stage == ShaderType::Compute &&
|
||||
input_info.compute != nullptr && input_info.compute->lds_storage) {
|
||||
input_info.compute->lds_storage) {
|
||||
lds_storage_class = spv::StorageClassStorageBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,10 +169,10 @@ uint32_t LdsDwordCount(const EmitterState& state) {
|
||||
if (const auto* workgroup = ShaderWorkgroupInput(state.program.stage, state.input_info)) {
|
||||
return workgroup->lds_size_dwords;
|
||||
}
|
||||
if (state.program.stage == ShaderType::Pixel && state.input_info.pixel != nullptr) {
|
||||
if (state.program.stage == ShaderType::Pixel) {
|
||||
return std::max(state.input_info.pixel->lds_size_dwords, 1u);
|
||||
}
|
||||
if (state.program.stage == ShaderType::Vertex && state.input_info.vertex != nullptr) {
|
||||
if (state.program.stage == ShaderType::Vertex) {
|
||||
return std::max(state.input_info.vertex->lds_size_dwords, 1u);
|
||||
}
|
||||
return FUNCTION_LDS_DWORDS;
|
||||
|
||||
@@ -987,30 +987,6 @@ void ValidateTranslateOptions(const TranslateOptions& options) {
|
||||
options.stage != ShaderType::Local) {
|
||||
EXIT("embedded fetch requires a vertex or local shader");
|
||||
}
|
||||
switch (options.stage) {
|
||||
case ShaderType::Vertex:
|
||||
case ShaderType::Local:
|
||||
case ShaderType::TessellationControl:
|
||||
case ShaderType::TessellationEvaluation:
|
||||
case ShaderType::Mesh:
|
||||
if (options.input_info.vertex == nullptr) {
|
||||
EXIT("vertex shader translation has no vertex input metadata");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Pixel:
|
||||
if (options.input_info.pixel == nullptr) {
|
||||
EXIT("pixel shader translation has no pixel input metadata");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Compute:
|
||||
if (options.input_info.compute == nullptr) {
|
||||
EXIT("compute shader translation has no compute input metadata");
|
||||
}
|
||||
return;
|
||||
default:
|
||||
EXIT("shader translation has unsupported stage %u",
|
||||
static_cast<uint32_t>(options.stage));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1044,7 +1020,9 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
case ShaderType::Compute:
|
||||
result.scratch_dwords = options.input_info.compute->scratch_size_dwords;
|
||||
break;
|
||||
default: break; // ValidateTranslateOptions rejects unsupported stages.
|
||||
default:
|
||||
EXIT("shader translation has unsupported stage %u",
|
||||
static_cast<uint32_t>(options.stage));
|
||||
}
|
||||
result.dispatcher_fallback = cfg.irreducible || cfg.unsupported;
|
||||
result.cfg_failure_kind = cfg.failure_kind;
|
||||
|
||||
@@ -362,8 +362,7 @@ class Tracker {
|
||||
public:
|
||||
Tracker(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg,
|
||||
ShaderStageInputInfo input)
|
||||
: m_program(program), m_decoded(decoded), m_native_cfg(native_cfg),
|
||||
m_workgroup(program.stage == ShaderType::Compute ? input.compute : nullptr),
|
||||
: m_program(program), m_decoded(decoded), m_native_cfg(native_cfg), m_input(input),
|
||||
m_scalar_writes(std::move(program.scalar_writes)), m_info(program.info) {
|
||||
std::ranges::sort(m_scalar_writes, {}, &Program::ScalarWrite::pc);
|
||||
m_info.buffers.clear();
|
||||
@@ -1853,7 +1852,7 @@ private:
|
||||
if (inst->GetOpcode() == ValueOpcode::GetBuiltin &&
|
||||
inst->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::LocalInvocationId)) &&
|
||||
ImmediateU32(inst->Arg(1), axis) && axis < 3u &&
|
||||
m_workgroup->threads_num[axis] <= (uint64_t {1} << bits)) return axis + 1u;
|
||||
m_input.compute->threads_num[axis] <= (uint64_t {1} << bits)) return axis + 1u;
|
||||
if (!allow_induction || inst->GetOpcode() != ValueOpcode::Phi ||
|
||||
(!induction.IsEmpty() && induction != at)) return {};
|
||||
induction = at;
|
||||
@@ -1864,20 +1863,20 @@ private:
|
||||
bool LinearLocalIndex(const std::array<uint64_t, 5>& index) const {
|
||||
uint64_t stride = 1u;
|
||||
for (uint32_t axis = 0; axis < 3u; ++axis) {
|
||||
if (m_workgroup->threads_num[axis] != 1u && index[axis + 1u] != stride) return false;
|
||||
stride *= m_workgroup->threads_num[axis];
|
||||
if (m_input.compute->threads_num[axis] != 1u && index[axis + 1u] != stride) return false;
|
||||
stride *= m_input.compute->threads_num[axis];
|
||||
}
|
||||
return index[0] == 0u;
|
||||
}
|
||||
|
||||
void ForwardPrivateLds() {
|
||||
if (m_workgroup == nullptr) return;
|
||||
if (m_program.stage != ShaderType::Compute) return;
|
||||
uint64_t bytes = 4u;
|
||||
for (const auto size: m_workgroup->threads_num) {
|
||||
for (const auto size: m_input.compute->threads_num) {
|
||||
if (size == 0u || size > 1024u) return;
|
||||
bytes *= size;
|
||||
}
|
||||
if (bytes > uint64_t {m_workgroup->lds_size_dwords} * 4u) return;
|
||||
if (bytes > uint64_t {m_input.compute->lds_size_dwords} * 4u) return;
|
||||
struct Access { Inst* inst; uint32_t offset; };
|
||||
std::vector<Access> reads, writes;
|
||||
Value address;
|
||||
@@ -1893,7 +1892,7 @@ private:
|
||||
if ((op != ValueOpcode::LoadSharedU32 && op != ValueOpcode::WriteSharedU32) ||
|
||||
memory.data_bits != 32u || memory.secondary_offset != 0u ||
|
||||
(memory.offset & 3u) != 0u ||
|
||||
uint64_t {memory.offset} + bytes > uint64_t {m_workgroup->lds_size_dwords} * 4u)
|
||||
uint64_t {memory.offset} + bytes > uint64_t {m_input.compute->lds_size_dwords} * 4u)
|
||||
return;
|
||||
if (address.IsEmpty()) address = inst.Arg(0).Resolve();
|
||||
else if (!EquivalentValue(m_program, address, inst.Arg(0))) return;
|
||||
@@ -2039,16 +2038,16 @@ private:
|
||||
Value SharedLoadBound(Value value, Value active) {
|
||||
const auto* read = ActiveValue(value, active).TryInstruction();
|
||||
if (read == nullptr || read->GetOpcode() != ValueOpcode::LoadSharedU32 ||
|
||||
m_workgroup == nullptr || m_program.stage != ShaderType::Compute ||
|
||||
m_program.stage != ShaderType::Compute ||
|
||||
read->Arg(1).Resolve() != Value(true)) return {};
|
||||
uint64_t group_size = 1u;
|
||||
for (const auto dimension: m_workgroup->threads_num) {
|
||||
if (dimension == 0u || dimension > m_workgroup->wave_size) return {};
|
||||
for (const auto dimension: m_input.compute->threads_num) {
|
||||
if (dimension == 0u || dimension > m_input.compute->wave_size) return {};
|
||||
group_size *= dimension;
|
||||
}
|
||||
// Indexed LDS operations within one native wave execute in order. Multiwave
|
||||
// append lists require a separate workgroup synchronization proof.
|
||||
if (group_size != m_workgroup->wave_size || group_size != m_program.wave_size ||
|
||||
if (group_size != m_input.compute->wave_size || group_size != m_program.wave_size ||
|
||||
(group_size != 32u && group_size != 64u)) return {};
|
||||
const auto memory = [&](const Inst& inst) -> const MemoryInfo* {
|
||||
const auto index = inst.Flags<MemoryFlags>().index;
|
||||
@@ -2101,7 +2100,7 @@ private:
|
||||
if (op != ValueOpcode::SharedAtomicIAdd32 ||
|
||||
ActiveValue(inst.Arg(1), guard) != Value(1u) ||
|
||||
!constant_address(inst, address) ||
|
||||
uint64_t {address} + 4u > uint64_t {m_workgroup->lds_size_dwords} * 4u ||
|
||||
uint64_t {address} + 4u > uint64_t {m_input.compute->lds_size_dwords} * 4u ||
|
||||
address >= 0x10000u ||
|
||||
std::ranges::any_of(appends, [&](const Append& at) { return at.counter == address; })) return {};
|
||||
appends.push_back({.atomic = &inst, .counter = address});
|
||||
@@ -4059,7 +4058,7 @@ private:
|
||||
Program& m_program;
|
||||
const Decoder::Program& m_decoded;
|
||||
const CFG::Graph& m_native_cfg;
|
||||
const ShaderWorkgroupInputInfo* m_workgroup;
|
||||
ShaderStageInputInfo m_input;
|
||||
std::vector<Program::ScalarWrite> m_scalar_writes;
|
||||
std::vector<ResolvedHandle> m_resolved_handles;
|
||||
std::vector<const Inst*> m_srt_visiting;
|
||||
@@ -4092,8 +4091,8 @@ UniformFillPlan RecognizePatternFill(Program& program, const Decoder::Program& d
|
||||
// Dispatch thread dimensions, valid in both wave sizes.
|
||||
Pattern {.code_words = 38, .hash = 0xacbda1a1ccdd4198ull, .wave_size_mask = 32 | 64, .bounded = false},
|
||||
};
|
||||
if (program.stage != ShaderType::Compute || input.compute == nullptr ||
|
||||
decoded.instructions.empty() || program.user_data_base != 0 || program.user_data_count < 10 ||
|
||||
if (program.stage != ShaderType::Compute || decoded.instructions.empty() ||
|
||||
program.user_data_base != 0 || program.user_data_count < 10 ||
|
||||
program.info.buffers.size() != 1 || !program.info.images.empty()) return {};
|
||||
const auto& cs = *input.compute;
|
||||
const bool matching_inputs = cs.workgroup_register == 10 && cs.thread_ids_num == 1 &&
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
// Resolves native descriptor sources, plans their scalar reads, and assigns dense resource bindings.
|
||||
void TrackResources(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg,
|
||||
ShaderStageInputInfo input = {});
|
||||
ShaderStageInputInfo input);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
|
||||
@@ -47,26 +47,17 @@ void ValidateOptions(const Program& program, ShaderStageInputInfo input_info) {
|
||||
case ShaderType::TessellationControl:
|
||||
case ShaderType::TessellationEvaluation:
|
||||
case ShaderType::Mesh:
|
||||
if (input_info.vertex == nullptr) {
|
||||
return Fail("vertex shader has no input metadata");
|
||||
}
|
||||
if (input_info.vertex->resources_num < 0 ||
|
||||
input_info.vertex->resources_num > ShaderVertexInputInfo::RES_MAX) {
|
||||
return Fail("vertex resource count is out of range");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Pixel:
|
||||
if (input_info.pixel == nullptr) {
|
||||
return Fail("pixel shader has no input metadata");
|
||||
}
|
||||
if (input_info.pixel->input_num > std::size(input_info.pixel->interpolator_settings)) {
|
||||
return Fail("pixel input count is out of range");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Compute:
|
||||
if (input_info.compute == nullptr) {
|
||||
return Fail("compute shader has no input metadata");
|
||||
}
|
||||
if (input_info.compute->thread_ids_num < 0 || input_info.compute->thread_ids_num > 3) {
|
||||
return Fail("compute thread ID count is out of range");
|
||||
}
|
||||
@@ -191,7 +182,7 @@ void AddBuiltinInput(ShaderInfo& info, StageInputKind kind) {
|
||||
|
||||
void CollectOutput(const Program& program, ShaderStageInputInfo input_info,
|
||||
ShaderInfo& info, const Inst& inst) {
|
||||
const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel != nullptr &&
|
||||
const bool alpha_remap = program.stage == ShaderType::Pixel &&
|
||||
input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None;
|
||||
const auto& export_info = program.export_info[inst.Flags<ExportFlags>().index];
|
||||
if (export_info.kind == ExportTargetKind::MrtZ) {
|
||||
|
||||
@@ -52,6 +52,7 @@ void CheckFatal(F &&function, std::string_view expected, const char *message) {
|
||||
struct Fixture {
|
||||
Program program;
|
||||
Block *block = nullptr;
|
||||
ShaderComputeInputInfo compute_info{};
|
||||
|
||||
explicit Fixture(ShaderType stage = ShaderType::Compute) {
|
||||
program.stage = stage;
|
||||
@@ -131,7 +132,9 @@ struct Fixture {
|
||||
Value(0u), Value(0u), Value(0u)});
|
||||
}
|
||||
|
||||
void PlanAndTrack(Libs::Graphics::ShaderStageInputInfo input = {}) {
|
||||
void PlanAndTrack() { PlanAndTrack({.compute = &compute_info}); }
|
||||
|
||||
void PlanAndTrack(Libs::Graphics::ShaderStageInputInfo input) {
|
||||
for (size_t index = 0; index < program.blocks.size(); ++index) {
|
||||
const auto condition = program.blocks[index]->condition;
|
||||
if (!condition.IsEmpty())
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
namespace {
|
||||
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
using Libs::Graphics::ShaderComputeInputInfo;
|
||||
using Libs::Graphics::ShaderType;
|
||||
|
||||
void Check(bool condition, const char *message) {
|
||||
@@ -37,6 +38,7 @@ void CheckFatal(F &&function, std::string_view expected, const char *message) {
|
||||
|
||||
struct Fixture {
|
||||
Program program;
|
||||
ShaderComputeInputInfo compute{};
|
||||
|
||||
explicit Fixture(uint32_t block_count = 1) {
|
||||
program.stage = ShaderType::Compute;
|
||||
@@ -73,7 +75,7 @@ struct Fixture {
|
||||
return static_cast<uint32_t>(program.memory_info.size() - 1u);
|
||||
}
|
||||
|
||||
void Plan() { TrackResources(program, {}, {}); }
|
||||
void Plan() { TrackResources(program, {}, {}, {.compute = &compute}); }
|
||||
};
|
||||
|
||||
struct TestMemory {
|
||||
@@ -158,7 +160,7 @@ void TestScalarMemoryDomainMismatchFails() {
|
||||
Fixture raw;
|
||||
const auto raw_memory = raw.AddMemory(ResourceKind::ScalarBuffer);
|
||||
RawRead(raw, Address(raw, Value(0x1000u), Value(0u)), Value(0u), raw_memory);
|
||||
CheckFatal([&] { TrackResources(raw.program, {}, {}); },
|
||||
CheckFatal([&] { raw.Plan(); },
|
||||
"incompatible scalar memory metadata",
|
||||
"raw scalar load accepted descriptor-buffer metadata");
|
||||
|
||||
@@ -169,7 +171,7 @@ void TestScalarMemoryDomainMismatchFails() {
|
||||
{Value(0x1000u), Value(0u), Value(16u), Value(0u)});
|
||||
buffer.EmitMemory(ValueOpcode::ReadConstBuffer, {resource, Value(0u)},
|
||||
buffer_memory);
|
||||
CheckFatal([&] { TrackResources(buffer.program, {}, {}); },
|
||||
CheckFatal([&] { buffer.Plan(); },
|
||||
"incompatible scalar memory metadata",
|
||||
"descriptor scalar load accepted raw-address metadata");
|
||||
}
|
||||
|
||||
@@ -14343,7 +14343,7 @@ void BuildTypedPlan(const uint32_t *code, uint32_t words,
|
||||
ShaderRecompiler::IR::RemoveIdentities(ir);
|
||||
ShaderRecompiler::IR::EliminateDeadCode(ir.blocks);
|
||||
}
|
||||
ShaderRecompiler::IR::TrackResources(ir, decoded, cfg);
|
||||
ShaderRecompiler::IR::TrackResources(ir, decoded, cfg, options.input_info);
|
||||
ShaderRecompiler::IR::EliminateDeadCode(ir.blocks);
|
||||
}
|
||||
|
||||
@@ -16182,10 +16182,6 @@ void TestCompilerStageInputOwnership() {
|
||||
CheckSpirvBinaryValidates(compiled.spirv);
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
options.stage = ShaderType::Vertex;
|
||||
options.input_info.vertex = nullptr;
|
||||
ExpectFatal([&] { (void)TranslateProgram(shader, options); },
|
||||
"missing stage metadata bypassed translation validation");
|
||||
const uint32_t auxiliary_position[] = {
|
||||
EncodeExp0(0x0d, 0x1), EncodeExp1(0, 0, 0, 0), EncodeSopp(0x01)};
|
||||
for (const auto stage : {ShaderType::Pixel, ShaderType::Compute}) {
|
||||
|
||||
Reference in New Issue
Block a user