shader: remove second IR layer

This commit is contained in:
nmzik
2026-08-26 07:42:51 +02:00
parent e2c840cb23
commit ffa69d48de
49 changed files with 3691 additions and 6001 deletions
@@ -1,7 +1,7 @@
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h"
#include <algorithm>
@@ -51,22 +51,20 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
Expect(Kind::Samplers, Dense(program.info.samplers.size()));
}
bool uses_gds = false;
if (program.values != nullptr) {
for (const auto* block: program.values->blocks) {
for (const auto& inst: *block) {
if (IR::SharedAccessOf(inst.GetOpcode()) == IR::SharedAccess::None) {
continue;
}
const auto index = inst.Flags<IR::MemoryFlags>().index;
if (index >= program.values->memory_info.size()) {
return Fail(error, "shared operation has invalid memory metadata");
}
const auto kind = program.values->memory_info[index].kind;
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
return Fail(error, "shared operation has invalid resource kind");
}
uses_gds |= kind == IR::ResourceKind::Gds;
for (const auto* block: program.blocks) {
for (const auto& inst: *block) {
if (IR::SharedAccessOf(inst.GetOpcode()) == IR::SharedAccess::None) {
continue;
}
const auto index = inst.Flags<IR::MemoryFlags>().index;
if (index >= program.memory_info.size()) {
return Fail(error, "shared operation has invalid memory metadata");
}
const auto kind = program.memory_info[index].kind;
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
return Fail(error, "shared operation has invalid resource kind");
}
uses_gds |= kind == IR::ResourceKind::Gds;
}
}
if (uses_gds) {
@@ -76,11 +74,10 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
Expect(Kind::AddressMemory, Dense(program.info.addresses.size()));
}
const bool uses_flattened_runtime =
program.values != nullptr &&
(!program.values->srt_reads.empty() ||
!program.srt_reads.empty() ||
std::ranges::any_of(program.info.images, [](const IR::ImageResource& image) {
return image.indirect_mapping_capacity != 0u;
}));
});
if (uses_flattened_runtime) {
Expect(Kind::FlattenedSrt);
}
@@ -120,9 +117,6 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
return Fail(error, "native user-data layout is inconsistent");
}
if (program.values == nullptr) {
return Fail(error, "native shader plan has no typed SSA");
}
const auto planning_only_handle = [&](const IR::Inst& handle) {
return !handle.Uses().empty() &&
std::ranges::all_of(handle.Uses(), [&](const IR::Use& use) {
@@ -132,11 +126,11 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
return false;
}
const auto index = use.user->Flags<IR::MemoryFlags>().index;
return index < program.values->memory_info.size() &&
program.values->memory_info[index].planning_only;
return index < program.memory_info.size() &&
program.memory_info[index].planning_only;
});
};
for (const auto* block: program.values->blocks) {
for (const auto* block: program.blocks) {
for (const auto& inst: *block) {
const auto dense = inst.Flags<uint32_t>();
switch (inst.GetOpcode()) {
@@ -174,7 +168,7 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
case IR::ValueOpcode::ReadConst: {
const auto slot = inst.Arg(1).Resolve();
if (!slot.IsImmediate() || slot.GetType() != IR::Type::U32 ||
slot.U32() >= program.values->srt_reads.size()) {
slot.U32() >= program.srt_reads.size()) {
return Fail(error, "flattened SRT read has an invalid dense slot");
}
break;
@@ -191,19 +185,15 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
program.spirv_requirements.reset();
IR::SpirvRequirements requirements {};
if (program.values == nullptr) {
program.spirv_requirements.emplace(requirements);
return true;
}
const auto MarkBallot = [&] { requirements.subgroup_ballot = true; };
for (const auto* block: program.values->blocks) {
for (const auto* block: program.blocks) {
for (const auto& inst: *block) {
if (IR::AddressOpcodeInfoOf(inst.GetOpcode()).access != IR::AddressAccess::None) {
const auto memory_index = inst.Flags<IR::MemoryFlags>().index;
if (memory_index >= program.values->memory_info.size()) {
if (memory_index >= program.memory_info.size()) {
return Fail(error, "address operation has invalid memory metadata");
}
if (program.values->memory_info[memory_index].kind == IR::ResourceKind::Scratch) {
if (program.memory_info[memory_index].kind == IR::ResourceKind::Scratch) {
if (program.scratch_dwords == 0) {
return Fail(error, "scratch operation has no per-thread storage");
}
@@ -212,10 +202,10 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
}
if (IR::BufferAccessOf(inst.GetOpcode()) != IR::BufferAccess::None) {
const auto memory_index = inst.Flags<IR::MemoryFlags>().index;
if (memory_index >= program.values->memory_info.size()) {
if (memory_index >= program.memory_info.size()) {
return Fail(error, "buffer operation has invalid memory metadata");
}
const auto& memory = program.values->memory_info[memory_index];
const auto& memory = program.memory_info[memory_index];
if (memory.kind == IR::ResourceKind::Buffer) {
if (memory.resource >= program.info.buffers.size()) {
return Fail(error, "buffer operation has invalid resource metadata");
@@ -231,10 +221,10 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
const auto shared_access = IR::SharedAccessOf(inst.GetOpcode());
if (shared_access != IR::SharedAccess::None) {
const auto index = inst.Flags<IR::MemoryFlags>().index;
if (index >= program.values->memory_info.size()) {
if (index >= program.memory_info.size()) {
return Fail(error, "shared operation has invalid memory metadata");
}
const auto kind = program.values->memory_info[index].kind;
const auto kind = program.memory_info[index].kind;
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
return Fail(error, "shared operation has invalid resource kind");
}
@@ -288,11 +278,11 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
break;
case IR::ValueOpcode::SetAttribute: {
const auto index = inst.Flags<IR::ExportFlags>().index;
if (index >= program.values->export_info.size()) {
if (index >= program.export_info.size()) {
return Fail(error, "attribute export has invalid metadata");
}
if (program.stage == ShaderType::Pixel &&
program.values->export_info[index].vm) {
program.export_info[index].vm) {
requirements.pixel_valid_mask = true;
}
break;
@@ -330,12 +320,7 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
if (!ValidateNativeProgram(program, error)) {
return false;
}
if (program.values == nullptr) {
SetError(error, "SPIR-V emitter requires planned typed SSA");
return false;
}
const auto& value_program = *program.values;
if (!IR::ValidateValueProgram(value_program, true, error)) {
if (!IR::ValidateProgram(program, true, error)) {
return false;
}
EmitterState state(program, resources, input_info);
@@ -348,7 +333,7 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
AllocateInputVariables(state);
AllocateOutputVariables(state);
DefineModule(state);
if (!EmitValueProgram(state, value_program, error)) {
if (!EmitProgram(state, program, error)) {
return false;
}
@@ -2,7 +2,7 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -5,13 +5,11 @@
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
namespace {
bool UserDataDwordIndex(const EmitterState& state, IR::Register reg, uint32_t& dword_index) {
if (reg.file != IR::RegisterFile::Scalar) {
return false;
}
bool UserDataDwordIndex(const EmitterState& state, IR::ScalarReg reg, uint32_t& dword_index) {
const auto register_index = IR::RegIndex(reg);
const auto& registers = state.program.bindings.user_data_registers;
const auto found = std::lower_bound(registers.begin(), registers.end(), reg.index);
if (found == registers.end() || *found != reg.index) {
const auto found = std::lower_bound(registers.begin(), registers.end(), register_index);
if (found == registers.end() || *found != register_index) {
return false;
}
dword_index = static_cast<uint32_t>(found - registers.begin());
@@ -504,7 +502,7 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::GetUserData: {
const auto reg = inst.Arg(0).ScalarRegister();
uint32_t dword = 0;
if (!UserDataDwordIndex(state, {IR::RegisterFile::Scalar, IR::RegIndex(reg)}, dword)) {
if (!UserDataDwordIndex(state, reg, dword)) {
ctx.Define(inst, ConstantU32(state, 0));
} else {
ctx.Define(inst, EmitShaderDataDwordLoad(state, dword));
@@ -6,7 +6,7 @@
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
@@ -407,7 +407,7 @@ inline void EmitLabel(EmitterState& state, uint32_t label) {
}
struct ValueEmitContext {
ValueEmitContext(EmitterState& state_, const IR::ValueProgram& program_)
ValueEmitContext(EmitterState& state_, const IR::Program& program_)
: state(state_), program(program_) {}
uint32_t Def(IR::Value value);
@@ -425,7 +425,7 @@ struct ValueEmitContext {
void Fail(const IR::Inst& inst, const char* reason);
EmitterState& state;
const IR::ValueProgram& program;
const IR::Program& program;
std::unordered_map<const IR::Inst*, uint32_t> definitions;
std::unordered_map<const IR::Block*, uint32_t> labels;
const std::unordered_map<const IR::Inst*, uint32_t>* dispatcher_spills = nullptr;
@@ -847,7 +847,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst);
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst);
bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error);
bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* error);
// These templates accept local lambdas from several emitter translation units.
template <typename Fn>
@@ -79,9 +79,9 @@ void StoreDispatcherPhiEdge(ValueEmitContext& ctx, const DispatcherFunctionState
}
}
const IR::Block* TargetBlock(const IR::ValueProgram& program, uint32_t id) {
const IR::Block* TargetBlock(const IR::Program& program, uint32_t id) {
const auto found = std::ranges::find_if(
program.block_info, [&](const IR::ValueBlockInfo& info) { return info.id == id; });
program.block_info, [&](const IR::BlockInfo& info) { return info.id == id; });
if (found == program.block_info.end()) {
return nullptr;
}
@@ -94,7 +94,7 @@ void EmitReturn(ValueEmitContext& ctx) {
}
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
const IR::ValueBlockInfo& info) {
const IR::BlockInfo& info) {
const auto& term = info.terminator;
const auto emit_merge = [&]() {
if (term.loop_header) {
@@ -150,7 +150,7 @@ void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState&
}
uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
const IR::Block* block, const IR::ValueBlockInfo& info) {
const IR::Block* block, const IR::BlockInfo& info) {
const auto& term = info.terminator;
switch (term.kind) {
case CFG::TerminatorKind::Branch:
@@ -503,7 +503,7 @@ void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) {
error = fmt::format("typed opcode {} {}", IR::ValueOpcodeName(inst.GetOpcode()), reason);
}
bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error) {
bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* error) {
ValueEmitContext ctx(state, program);
std::optional<DispatcherFunctionState> dispatcher;
if (state.stage == ShaderType::Pixel && state.requirements.pixel_valid_mask) {