shader: unify stage input metadata

This commit is contained in:
nmzik
2026-08-17 02:23:47 +02:00
parent 5ea92d95a7
commit 0f3540b1ff
16 changed files with 278 additions and 407 deletions
@@ -316,10 +316,8 @@ bool ProgramRequiresExactSubgroupSize(const IR::Program& program) {
}
bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resources,
const ShaderVertexInputInfo* vertex_input_info,
const ShaderPixelInputInfo* pixel_input_info,
const ShaderComputeInputInfo* compute_input_info, std::vector<uint32_t>& spirv,
std::string* error, bool preserve_debug_values) {
ShaderStageInputInfo input_info, std::vector<uint32_t>& spirv, std::string* error,
bool preserve_debug_values) {
using namespace Emitter;
if (program.stage != ShaderType::Compute && program.stage != ShaderType::Vertex &&
@@ -345,21 +343,12 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
SetError(error, "SPIR-V emitter requires planned typed SSA");
return false;
}
ShaderVertexInputInfo default_vertex {};
ShaderPixelInputInfo default_pixel {};
ShaderComputeInputInfo default_compute {};
const auto* vertex = vertex_input_info != nullptr ? vertex_input_info : &default_vertex;
const auto* pixel = pixel_input_info != nullptr ? pixel_input_info : &default_pixel;
const auto* compute = compute_input_info != nullptr ? compute_input_info : &default_compute;
(void)preserve_debug_values;
const auto& value_program = *program.values;
if (!IR::ValidateValueProgram(value_program, true, error)) {
return false;
}
EmitterState state(program, resources);
state.vertex_input_info = vertex;
state.pixel_input_info = pixel;
state.compute_input_info = compute;
EmitterState state(program, resources, input_info);
state.stage = program.stage;
state.wave_size = program.wave_size;
state.per_invocation_masks = program.lane_mask_mode == ShaderLaneMaskMode::PerInvocation;
@@ -12,10 +12,8 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
bool ProgramRequiresExactSubgroupSize(const IR::Program& program);
bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resources,
const ShaderVertexInputInfo* vertex_input_info,
const ShaderPixelInputInfo* pixel_input_info,
const ShaderComputeInputInfo* compute_input_info, std::vector<uint32_t>& spirv,
std::string* error, bool preserve_debug_values = false);
ShaderStageInputInfo input_info, std::vector<uint32_t>& spirv, std::string* error,
bool preserve_debug_values = false);
} // namespace Libs::Graphics::ShaderRecompiler::Spirv
@@ -7,11 +7,10 @@
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
const auto* ps = state.pixel_input_info;
if (state.stage != ShaderType::Pixel || ps == nullptr) {
if (state.stage != ShaderType::Pixel) {
return attr;
}
return ShaderPixelParameterMappedLocation(*ps, attr);
return ShaderPixelParameterMappedLocation(*state.input_info.pixel, attr);
}
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
@@ -22,16 +21,15 @@ uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
active_inputs[active_count++] = input.location;
}
}
return state.stage == ShaderType::Pixel && state.pixel_input_info != nullptr
? ShaderPixelParameterLocation(*state.pixel_input_info,
return state.stage == ShaderType::Pixel
? ShaderPixelParameterLocation(*state.input_info.pixel,
{active_inputs.data(), active_count}, attr)
: attr;
}
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
const auto* ps = state.pixel_input_info;
return state.stage == ShaderType::Pixel && ps != nullptr &&
ShaderPixelParameterIsFlat(*ps, attr);
return state.stage == ShaderType::Pixel &&
ShaderPixelParameterIsFlat(*state.input_info.pixel, attr);
}
void SetError(std::string* error, const char* message) {
@@ -95,20 +95,19 @@ uint32_t EmitExportVec4U32(EmitterState& state, const IR::Instruction& inst) {
}
static bool MrtUsesUintOutput(const EmitterState& state, const IR::Instruction& inst) {
return inst.export_info.kind == IR::ExportTargetKind::Mrt &&
state.pixel_input_info != nullptr &&
inst.export_info.index < std::size(state.pixel_input_info->target_output_mode) &&
state.pixel_input_info->target_output_mode[inst.export_info.index] == 7u;
return state.stage == ShaderType::Pixel && inst.export_info.kind == IR::ExportTargetKind::Mrt &&
inst.export_info.index < std::size(state.input_info.pixel->target_output_mode) &&
state.input_info.pixel->target_output_mode[inst.export_info.index] == 7u;
}
uint32_t ApplyMrtExportMapping(EmitterState& state, const IR::Instruction& inst, uint32_t value,
uint32_t vector_type) {
if (inst.export_info.kind != IR::ExportTargetKind::Mrt || state.pixel_input_info == nullptr ||
inst.export_info.index >= state.pixel_input_info->target_export_mapping.size()) {
if (state.stage != ShaderType::Pixel || inst.export_info.kind != IR::ExportTargetKind::Mrt ||
inst.export_info.index >= state.input_info.pixel->target_export_mapping.size()) {
return value;
}
const auto mapping = state.pixel_input_info->target_export_mapping[inst.export_info.index];
const auto mapping = state.input_info.pixel->target_export_mapping[inst.export_info.index];
if (mapping.IsIdentity()) {
return value;
}
@@ -317,17 +317,16 @@ struct StorageImageDescriptors {
};
struct EmitterState {
EmitterState(const IR::Program& program_, const IR::ResourceSnapshot& resources_)
: program(program_), resources(resources_) {}
EmitterState(const IR::Program& program_, const IR::ResourceSnapshot& resources_,
ShaderStageInputInfo input_info_)
: program(program_), resources(resources_), input_info(input_info_) {}
Builder builder;
const IR::Program& program;
const IR::ResourceSnapshot& resources;
const ShaderVertexInputInfo* vertex_input_info = nullptr;
const ShaderPixelInputInfo* pixel_input_info = nullptr;
const ShaderComputeInputInfo* compute_input_info = nullptr;
ShaderType stage = ShaderType::Unknown;
uint32_t wave_size = 64;
ShaderStageInputInfo input_info;
ShaderType stage = ShaderType::Unknown;
uint32_t wave_size = 64;
bool exact_subgroup_operations = false;
bool per_invocation_masks = false;
uint32_t void_type = 0;
@@ -496,7 +496,7 @@ uint32_t EmitLdsElementPointer(EmitterState& state, uint32_t index) {
}
uint32_t LdsDwordCount(const EmitterState& state) {
return state.needs_function_lds ? 8192u : state.compute_input_info->lds_size_dwords;
return state.stage == ShaderType::Compute ? state.input_info.compute->lds_size_dwords : 8192u;
}
uint32_t EmitLdsElementInBounds(EmitterState& state, uint32_t index) {
@@ -59,13 +59,12 @@ uint32_t ConstantF32Value(EmitterState& state, float value) {
}
VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint32_t location) {
if (state.stage != ShaderType::Vertex || state.vertex_input_info == nullptr ||
location >= ShaderVertexInputInfo::RES_MAX ||
location >= static_cast<uint32_t>(state.vertex_input_info->resources_num)) {
if (state.stage != ShaderType::Vertex || location >= ShaderVertexInputInfo::RES_MAX ||
location >= static_cast<uint32_t>(state.input_info.vertex->resources_num)) {
return VertexInputScalarKind::Float;
}
switch (state.vertex_input_info->resources[location].Format()) {
switch (state.input_info.vertex->resources[location].Format()) {
case Prospero::BufferFormat::k8UInt:
case Prospero::BufferFormat::k16UInt:
case Prospero::BufferFormat::k8_8UInt:
@@ -92,12 +91,11 @@ VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint3
uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBinding& input) {
uint32_t count = input.component_count;
if (state.stage == ShaderType::Vertex && state.vertex_input_info != nullptr &&
input.location < ShaderVertexInputInfo::RES_MAX &&
input.location < static_cast<uint32_t>(state.vertex_input_info->resources_num) &&
state.vertex_input_info->resources_dst[input.location].registers_num > 0) {
if (state.stage == ShaderType::Vertex && input.location < ShaderVertexInputInfo::RES_MAX &&
input.location < static_cast<uint32_t>(state.input_info.vertex->resources_num) &&
state.input_info.vertex->resources_dst[input.location].registers_num > 0) {
count = static_cast<uint32_t>(
state.vertex_input_info->resources_dst[input.location].registers_num);
state.input_info.vertex->resources_dst[input.location].registers_num);
}
return std::clamp(count, 1u, 4u);
}
@@ -177,9 +175,9 @@ uint32_t VertexParameterInputPointerType(const EmitterState& state, VertexInputS
}
static bool MrtUsesUintOutput(const EmitterState& state, uint32_t index) {
return state.stage == ShaderType::Pixel && state.pixel_input_info != nullptr &&
index < std::size(state.pixel_input_info->target_output_mode) &&
state.pixel_input_info->target_output_mode[index] == 7u;
return state.stage == ShaderType::Pixel &&
index < std::size(state.input_info.pixel->target_output_mode) &&
state.input_info.pixel->target_output_mode[index] == 7u;
}
void AllocateInputVariables(EmitterState& state) {
@@ -260,8 +258,8 @@ void AddInputAnnotationsAndNames(EmitterState& state) {
if (flat) {
state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat});
}
if (state.stage == ShaderType::Pixel && state.pixel_input_info != nullptr &&
state.pixel_input_info->ps_no_perspective && !flat) {
if (state.stage == ShaderType::Pixel && state.input_info.pixel->ps_no_perspective &&
!flat) {
state.builder.AddAnnotation(
{OpDecorate, input.variable_id, DecorationNoPerspective});
}
@@ -521,15 +519,13 @@ void EmitHeaderAndTypes(EmitterState& state) {
// contract prevents host compilers from treating synthesized IEEE values as finite.
state.builder.AddExecutionMode({state.main_func, ExecutionModeSignedZeroInfNanPreserve, 32u});
if (state.stage == ShaderType::Compute) {
uint32_t local_x = state.needs_compute_derivatives ? 2u : 1u;
uint32_t local_y = state.needs_compute_derivatives ? 2u : 1u;
uint32_t local_z = 1u;
if (state.compute_input_info != nullptr) {
const auto* cs = state.compute_input_info;
local_x = cs->threads_num[0] != 0u ? cs->threads_num[0] : local_x;
local_y = cs->threads_num[1] != 0u ? cs->threads_num[1] : local_y;
local_z = cs->threads_num[2] != 0u ? cs->threads_num[2] : local_z;
}
uint32_t local_x = state.needs_compute_derivatives ? 2u : 1u;
uint32_t local_y = state.needs_compute_derivatives ? 2u : 1u;
uint32_t local_z = 1u;
const auto* cs = state.input_info.compute;
local_x = cs->threads_num[0] != 0u ? cs->threads_num[0] : local_x;
local_y = cs->threads_num[1] != 0u ? cs->threads_num[1] : local_y;
local_z = cs->threads_num[2] != 0u ? cs->threads_num[2] : local_z;
state.builder.AddExecutionMode(
{state.main_func, ExecutionModeLocalSize, local_x, local_y, local_z});
}
@@ -538,10 +534,9 @@ void EmitHeaderAndTypes(EmitterState& state) {
if (state.depth_variable != 0) {
state.builder.AddExecutionMode({state.main_func, ExecutionModeDepthReplacing});
}
if (state.pixel_input_info != nullptr && state.pixel_input_info->ps_early_z &&
!state.pixel_input_info->ps_pixel_kill_enable &&
!state.pixel_input_info->ps_depth_export_enable &&
!state.pixel_input_info->ps_sample_mask_export_enable) {
if (state.input_info.pixel->ps_early_z && !state.input_info.pixel->ps_pixel_kill_enable &&
!state.input_info.pixel->ps_depth_export_enable &&
!state.input_info.pixel->ps_sample_mask_export_enable) {
state.builder.AddExecutionMode({state.main_func, ExecutionModeEarlyFragmentTests});
}
}
@@ -159,9 +159,9 @@ uint32_t EmitAttribute(ValueEmitContext& ctx, uint32_t attr, uint32_t chan) {
}
bool MrtUsesUint(const EmitterState& state, const IR::ExportInfo& exp) {
return exp.kind == IR::ExportTargetKind::Mrt && state.pixel_input_info != nullptr &&
exp.index < std::size(state.pixel_input_info->target_output_mode) &&
state.pixel_input_info->target_output_mode[exp.index] == 7u;
return state.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
exp.index < std::size(state.input_info.pixel->target_output_mode) &&
state.input_info.pixel->target_output_mode[exp.index] == 7u;
}
uint32_t ExportRawComponent(ValueEmitContext& ctx, uint32_t vector, uint32_t component) {
@@ -289,9 +289,9 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) {
const bool uint_output = MrtUsesUint(state, exp);
const auto vector_type = uint_output ? state.vec4_uint_type : state.vec4_float_type;
auto value = ExportVector(ctx, data, exp, uint_output);
if (exp.kind == IR::ExportTargetKind::Mrt && state.pixel_input_info != nullptr &&
exp.index < state.pixel_input_info->target_export_mapping.size()) {
const auto mapping = state.pixel_input_info->target_export_mapping[exp.index];
if (state.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
exp.index < state.input_info.pixel->target_export_mapping.size()) {
const auto mapping = state.input_info.pixel->target_export_mapping[exp.index];
if (!mapping.IsIdentity()) {
const auto mapped = state.builder.AllocateId();
state.builder.AddFunction({OpVectorShuffle, vector_type, mapped, value, value,