shader: share emitter helpers

This commit is contained in:
nmzik
2026-09-08 13:09:56 +04:00
parent de3e86791c
commit 2b3993a595
9 changed files with 50 additions and 95 deletions
@@ -332,7 +332,7 @@ std::vector<uint32_t> EmitProgram(const IR::Program& program,
AllocateInputVariables(state);
AllocateOutputVariables(state);
DefineModule(state);
EmitProgram(state, program);
EmitProgram(state);
state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main",
state.interface_variables);
@@ -184,7 +184,7 @@ uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding
if (count == 1u) {
state.builder.AddFunction({OpLoad, scalar_type, raw, input.variable_id});
} else {
const auto pointer_type = VertexParameterScalarPointerType(state, kind);
const auto pointer_type = TypePointer(state, StorageClassInput, scalar_type);
const auto pointer = state.builder.AllocateId();
state.builder.AddFunction({OpAccessChain, pointer_type, pointer, input.variable_id,
ConstantU32(state, component)});
@@ -768,8 +768,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
return true;
}
const auto* handle = image_arg.ResolveInstruction();
const auto* source = image.source < ctx.program.descriptor_sources.size()
? &ctx.program.descriptor_sources[image.source]
const auto* source = image.source < state.program.descriptor_sources.size()
? &state.program.descriptor_sources[image.source]
: nullptr;
if (handle == nullptr || source == nullptr || !source->indirect_image.has_value() ||
source->indirect_image->key_arg >= handle->NumArgs()) {
@@ -385,6 +385,7 @@ struct EmitterState {
uint32_t mesh_cull = 0;
uint32_t entry_label = 0;
uint32_t current_label = 0;
const IR::Block* current_block = nullptr;
uint32_t pixel_valid_mask_variable = 0;
uint32_t subgroup_local_invocation_id_variable = 0;
uint32_t per_vertex_variable = 0;
@@ -437,8 +438,7 @@ inline void EmitLabel(EmitterState& state, uint32_t label) {
}
struct ValueEmitContext {
ValueEmitContext(EmitterState& state_, const IR::Program& program_)
: state(state_), program(program_) {}
explicit ValueEmitContext(EmitterState& state_): state(state_) {}
uint32_t Def(IR::Value value);
uint32_t Arg(const IR::Inst& inst, size_t index);
@@ -460,11 +460,9 @@ struct ValueEmitContext {
[[noreturn]] void Fail(const IR::Inst& inst, const char* reason) const;
EmitterState& state;
const IR::Program& program;
std::unordered_map<const IR::Inst*, uint32_t> definitions;
const std::unordered_map<const IR::Inst*, uint32_t>* dispatcher_spills = nullptr;
std::unordered_map<const IR::Inst*, std::pair<uint32_t, uint32_t>> dispatcher_block_loads;
const IR::Block* current_block = nullptr;
uint32_t scratch_u32_variable = 0;
ValueEmitContext* other_half = nullptr;
uint32_t half = 0;
@@ -507,8 +505,6 @@ uint32_t VertexParameterComponentCount(const InputBinding& input);
uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind);
uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind);
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
uint32_t components);
@@ -669,15 +665,8 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state,
const MemoryResourceAccess& access, uint32_t index,
uint32_t pointer_type);
uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value);
uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value);
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value);
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
uint32_t constant);
uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value,
uint32_t false_value);
@@ -764,7 +753,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst);
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst);
void EmitProgram(EmitterState& state, const IR::Program& program);
void EmitProgram(EmitterState& state);
void DefineGetBdaPointer(EmitterState& state);
@@ -188,31 +188,12 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state,
return pointer;
}
uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction({OpBitcast, TypeU32(state), ret, value});
return ret;
}
uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction({OpBitcast, TypeF32(state), ret, value});
return ret;
}
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction({OpBitcast, TypeI32(state), ret, value});
return ret;
}
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
uint32_t constant) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction({opcode, TypeBool(state), ret, value, ConstantU32(state, constant)});
return ret;
}
uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value,
uint32_t false_value) {
const auto ret = state.builder.AllocateId();
@@ -231,7 +212,7 @@ uint32_t EmitHalfToF32Bits(EmitterState& state, uint32_t raw) {
state.builder.AddFunction(
{OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state), GlslUnpackHalf2x16, raw});
state.builder.AddFunction({OpCompositeExtract, TypeF32(state), value, unpacked, 0});
return EmitTBufferBitcastF32ToU32(state, value);
return EmitBitcastF32ToU32(state, value);
}
uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
@@ -250,11 +231,11 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
const auto mantissa_bits_32 =
EmitBinaryU32(state, OpShiftLeftLogical, mantissa, ConstantU32(state, 23u - mantissa_bits));
const auto normal_bits = EmitBinaryU32(state, OpBitwiseOr, exponent_bits, mantissa_bits_32);
const auto normal = EmitTBufferBitcastU32ToF32(state, normal_bits);
const auto normal = EmitBitcastU32ToF32(state, normal_bits);
const auto special_bits =
EmitBinaryU32(state, OpBitwiseOr, ConstantU32(state, 0x7f800000u), mantissa_bits_32);
const auto special = EmitTBufferBitcastU32ToF32(state, special_bits);
const auto special = EmitBitcastU32ToF32(state, special_bits);
const auto mantissa_f32 = state.builder.AllocateId();
const auto subnormal = state.builder.AllocateId();
@@ -263,11 +244,11 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
{OpFMul, TypeF32(state), subnormal, mantissa_f32,
ConstantF32Value(state, std::ldexp(1.0f, 1 - 15 - static_cast<int>(mantissa_bits)))});
const auto zero_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 0);
const auto special_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 31);
const auto zero_exp = EmitCompareU32Constant(state, OpIEqual, exponent, 0);
const auto special_exp = EmitCompareU32Constant(state, OpIEqual, exponent, 31);
const auto finite = EmitTBufferSelectF32(state, zero_exp, subnormal, normal);
const auto result = EmitTBufferSelectF32(state, special_exp, special, finite);
return EmitTBufferBitcastF32ToU32(state, result);
return EmitBitcastF32ToU32(state, result);
}
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
@@ -279,13 +260,13 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
case Format::ComponentType::Uscaled: {
const auto value = state.builder.AllocateId();
state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw});
return EmitTBufferBitcastF32ToU32(state, value);
return EmitBitcastF32ToU32(state, value);
}
case Format::ComponentType::Sscaled: {
const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw);
const auto value = state.builder.AllocateId();
state.builder.AddFunction({OpConvertSToF, TypeF32(state), value, signed_raw});
return EmitTBufferBitcastF32ToU32(state, value);
return EmitBitcastF32ToU32(state, value);
}
case Format::ComponentType::Unorm: {
const auto value = state.builder.AllocateId();
@@ -294,7 +275,7 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw});
state.builder.AddFunction(
{OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)});
return EmitTBufferBitcastF32ToU32(state, normalized);
return EmitBitcastF32ToU32(state, normalized);
}
case Format::ComponentType::Snorm: {
const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw);
@@ -307,7 +288,7 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
{OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)});
state.builder.AddFunction({OpExtInst, TypeF32(state), clamped, GlslStd450(state),
GlslFMax, normalized, ConstantF32Value(state, -1.0f)});
return EmitTBufferBitcastF32ToU32(state, clamped);
return EmitBitcastF32ToU32(state, clamped);
}
case Format::ComponentType::Float:
if (bits == 32u) {
@@ -349,17 +349,6 @@ uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind ki
}
}
uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind) {
switch (kind) {
case VertexInputScalarKind::Sint:
return TypePointer(state, StorageClassInput, TypeI32(state));
case VertexInputScalarKind::Uint:
return TypePointer(state, StorageClassInput, TypeU32(state));
case VertexInputScalarKind::Float:
default: return TypePointer(state, StorageClassInput, TypeF32(state));
}
}
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
uint32_t components) {
switch (kind) {
@@ -118,18 +118,19 @@ uint32_t BranchCondition(ValueEmitContext& ctx, const IR::BlockInfo& info) {
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
const IR::BlockInfo& info) {
const auto& program = ctx.state.program;
const auto& term = info.terminator;
const auto emit_merge = [&]() {
if (term.loop_header) {
const auto* merge = TargetBlock(ctx.program, term.merge_block);
const auto* cont = TargetBlock(ctx.program, term.continue_block);
const auto* merge = TargetBlock(program, term.merge_block);
const auto* cont = TargetBlock(program, term.continue_block);
if (merge != nullptr && cont != nullptr) {
ctx.state.builder.AddFunction(
{OpLoopMerge, ctx.Label(merge), ctx.Label(cont), LoopControlNone});
}
} else if (term.kind == CFG::TerminatorKind::ConditionalBranch &&
term.merge_block != UINT32_MAX) {
if (const auto* merge = TargetBlock(ctx.program, term.merge_block); merge != nullptr) {
if (const auto* merge = TargetBlock(program, term.merge_block); merge != nullptr) {
ctx.state.builder.AddFunction(
{OpSelectionMerge, ctx.Label(merge), SelectionControlNone});
}
@@ -138,7 +139,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
switch (term.kind) {
case CFG::TerminatorKind::Branch: {
const auto* target = TargetBlock(ctx.program, term.true_block);
const auto* target = TargetBlock(program, term.true_block);
if (target == nullptr) {
EmitReturn(ctx);
return;
@@ -148,8 +149,8 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
return;
}
case CFG::TerminatorKind::ConditionalBranch: {
const auto* true_block = TargetBlock(ctx.program, term.true_block);
const auto* false_block = TargetBlock(ctx.program, term.false_block);
const auto* true_block = TargetBlock(program, term.true_block);
const auto* false_block = TargetBlock(program, term.false_block);
if (true_block == nullptr || false_block == nullptr || info.condition.IsEmpty()) {
EmitReturn(ctx);
return;
@@ -166,7 +167,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
const IR::Block* from, uint32_t target) {
const auto* block = TargetBlock(ctx.program, target);
const auto* block = TargetBlock(ctx.state.program, target);
if (block != nullptr) {
StoreDispatcherPhiEdge(ctx, dispatcher, from, block);
if (ctx.other_half != nullptr) {
@@ -272,10 +273,7 @@ void EmitDispatcherInstruction(ValueEmitContext& ctx, const DispatcherFunctionSt
template <typename EmitInstruction>
void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&& emit_instruction) {
ctx.current_block = block;
if (ctx.other_half != nullptr) {
ctx.other_half->current_block = block;
}
ctx.state.current_block = block;
EmitLabel(ctx.state, ctx.Label(block));
bool emitted_non_phi = false;
for (const auto& inst: *block) {
@@ -288,7 +286,6 @@ void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&&
}
for (uint32_t half = 0; half < ctx.state.lane_count; half++) {
auto& lane = half == 0 ? ctx : *ctx.other_half;
lane.current_block = block;
ctx.state.lane_half = half;
if (half == 0 || (inst.GetOpcode() != IR::ValueOpcode::Barrier &&
inst.GetOpcode() != IR::ValueOpcode::MeshAllocate)) {
@@ -315,28 +312,29 @@ void PatchStructuredPhis(ValueEmitContext& ctx, StructuredFunctionState& structu
}
void EmitStructuredFunction(ValueEmitContext& ctx) {
const auto& program = ctx.state.program;
StructuredFunctionState structured;
ctx.state.builder.AddFunction({OpBranch, ctx.Label(ctx.program.blocks.front())});
for (size_t index = 0; index < ctx.program.blocks.size(); index++) {
const auto* block = ctx.program.blocks[index];
ctx.state.builder.AddFunction({OpBranch, ctx.Label(program.blocks.front())});
for (size_t index = 0; index < program.blocks.size(); index++) {
const auto* block = program.blocks[index];
EmitBlock(ctx, block, [&](ValueEmitContext& lane, const IR::Inst& inst) {
EmitStructuredInstruction(lane, structured, inst);
});
structured.block_exit_labels.emplace(block, ctx.state.current_label);
EmitStructuredTerminator(ctx, block, ctx.program.block_info[index]);
EmitStructuredTerminator(ctx, block, program.block_info[index]);
}
PatchStructuredPhis(ctx, structured);
}
void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher) {
auto& state = ctx.state;
const auto* entry = ctx.program.blocks.front();
const auto* entry = state.program.blocks.front();
state.builder.AddFunction({OpBranch, ctx.Label(entry)});
EmitBlock(ctx, entry, [&](ValueEmitContext& lane, const IR::Inst& inst) {
EmitDispatcherInstruction(lane, dispatcher, inst);
});
const auto initial_pc =
EmitDispatcherNextPc(ctx, dispatcher, entry, ctx.program.block_info.front());
EmitDispatcherNextPc(ctx, dispatcher, entry, state.program.block_info.front());
const auto initial_parent = state.current_label;
state.builder.AddFunction({OpBranch, dispatcher.header_label});
@@ -357,21 +355,21 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
state.builder.AddFunction(
{OpSelectionMerge, dispatcher.after_switch_label, SelectionControlNone});
std::vector<uint32_t> words {OpSwitch, pc, dispatcher.after_switch_label};
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
words.push_back(ctx.program.block_info[index].id);
words.push_back(ctx.Label(ctx.program.blocks[index]));
for (size_t index = 1; index < state.program.blocks.size(); index++) {
words.push_back(state.program.block_info[index].id);
words.push_back(ctx.Label(state.program.blocks[index]));
}
state.builder.AddFunction(words);
std::vector<uint32_t> next_pc_words {OpPhi, TypeU32(state), next_pc,
ConstantU32(state, UINT32_MAX), dispatcher.select_label};
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
EmitBlock(ctx, ctx.program.blocks[index],
for (size_t index = 1; index < state.program.blocks.size(); index++) {
EmitBlock(ctx, state.program.blocks[index],
[&](ValueEmitContext& lane, const IR::Inst& inst) {
EmitDispatcherInstruction(lane, dispatcher, inst);
});
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, ctx.program.blocks[index],
ctx.program.block_info[index]);
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index],
state.program.block_info[index]);
next_pc_words.push_back(selected);
next_pc_words.push_back(state.current_label);
state.builder.AddFunction({OpBranch, dispatcher.after_switch_label});
@@ -423,8 +421,8 @@ uint32_t ValueEmitContext::Def(IR::Value value) {
if (inst == nullptr) {
Fail("direct SPIR-V emitter received a non-value argument");
}
if (dispatcher_spills != nullptr && current_block != nullptr &&
inst->Parent() != current_block) {
if (dispatcher_spills != nullptr && state.current_block != nullptr &&
inst->Parent() != state.current_block) {
if (const auto found = dispatcher_spills->find(inst); found != dispatcher_spills->end()) {
if (const auto loaded = dispatcher_block_loads.find(inst);
loaded != dispatcher_block_loads.end() &&
@@ -571,11 +569,11 @@ const IR::Inst* ValueEmitContext::ImageAddress(IR::Value value) {
}
const IR::MemoryInfo& ValueEmitContext::Memory(const IR::Inst& inst) const {
return program.memory_info.at(inst.Flags<IR::MemoryFlags>().index);
return state.program.memory_info.at(inst.Flags<IR::MemoryFlags>().index);
}
const IR::ExportInfo& ValueEmitContext::Export(const IR::Inst& inst) const {
return program.export_info.at(inst.Flags<IR::ExportFlags>().index);
return state.program.export_info.at(inst.Flags<IR::ExportFlags>().index);
}
uint32_t ValueEmitContext::Label(const IR::Block* block) const {
@@ -584,20 +582,21 @@ uint32_t ValueEmitContext::Label(const IR::Block* block) const {
[[noreturn]] void ValueEmitContext::Fail(const char* reason) const {
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u reason=%s\n",
program.shader_hash, static_cast<unsigned>(program.stage), reason);
state.program.shader_hash, static_cast<unsigned>(state.program.stage), reason);
std::abort();
}
[[noreturn]] void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) const {
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u opcode=%s reason=%s\n",
program.shader_hash, static_cast<unsigned>(program.stage),
state.program.shader_hash, static_cast<unsigned>(state.program.stage),
IR::ValueOpcodeName(inst.GetOpcode()), reason);
std::abort();
}
void EmitProgram(EmitterState& state, const IR::Program& program) {
ValueEmitContext ctx(state, program);
ValueEmitContext high(state, program);
void EmitProgram(EmitterState& state) {
const auto& program = state.program;
ValueEmitContext ctx(state);
ValueEmitContext high(state);
if (state.lane_count == 2) {
ctx.other_half = &high;
high.other_half = &ctx;
-1
View File
@@ -593,7 +593,6 @@ static void ShaderGetStaticInputInfoPS(
}
ps_info.ps_pos_x = (active_inputs & 0x00000100u) != 0;
ps_info.ps_pos_y = (active_inputs & 0x00000200u) != 0;
ps_info.ps_pos_xy = ps_info.ps_pos_x && ps_info.ps_pos_y;
ps_info.ps_pos_z = (active_inputs & 0x00000400u) != 0;
ps_info.ps_pos_w = (active_inputs & 0x00000800u) != 0;
ps_info.ps_front_face = (active_inputs & 0x00001000u) != 0;
-2
View File
@@ -102,7 +102,6 @@ struct ShaderVertexInputInfo {
ShaderVertexInputBuffer buffers[RES_MAX];
ShaderStageRuntime stage;
int resources_num = 0;
int fetch_shader_reg = 0;
int fetch_attrib_reg = 0;
int fetch_buffer_reg = 0;
int buffers_num = 0;
@@ -135,7 +134,6 @@ struct ShaderPixelInputInfo {
uint32_t scratch_size_dwords = 0;
bool ps_pos_x = false;
bool ps_pos_y = false;
bool ps_pos_xy = false;
bool ps_pos_z = false;
bool ps_pos_w = false;
bool ps_front_face = false;