mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
228 lines
8.7 KiB
C++
228 lines
8.7 KiB
C++
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
|
|
|
|
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
|
|
|
uint32_t EmitExportComponentF32(EmitterState& state, const IR::Instruction& inst,
|
|
uint32_t component) {
|
|
const bool enabled = ((inst.export_info.en >> component) & 1u) != 0;
|
|
if (!enabled || component >= inst.src_count || component >= 4u) {
|
|
return ConstantF32(state, component == 3u ? 0x3f800000u : 0u);
|
|
}
|
|
return EmitFloatLoad(state, inst.src[component]);
|
|
}
|
|
|
|
uint32_t EmitExportVec4F32(EmitterState& state, const IR::Instruction& inst) {
|
|
if (inst.export_info.compr) {
|
|
uint32_t components[4] = {
|
|
ConstantF32(state, 0u),
|
|
ConstantF32(state, 0u),
|
|
ConstantF32(state, 0u),
|
|
ConstantF32(state, 0x3f800000u),
|
|
};
|
|
for (uint32_t pair_index = 0; pair_index < 2u && pair_index < inst.src_count;
|
|
pair_index++) {
|
|
const auto raw = EmitValueLoad(state, inst.src[pair_index]);
|
|
const auto unpacked = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpExtInst, state.vec2_float_type, unpacked,
|
|
state.glsl_std450, GlslUnpackHalf2x16, raw});
|
|
for (uint32_t lane = 0; lane < 2u; lane++) {
|
|
const auto component = pair_index * 2u + lane;
|
|
if (((inst.export_info.en >> component) & 1u) == 0) {
|
|
continue;
|
|
}
|
|
components[component] = state.builder.AllocateId();
|
|
state.builder.AddFunction(
|
|
{OpCompositeExtract, state.float_type, components[component], unpacked, lane});
|
|
}
|
|
}
|
|
const auto vec = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpCompositeConstruct, state.vec4_float_type, vec, components[0],
|
|
components[1], components[2], components[3]});
|
|
return vec;
|
|
}
|
|
|
|
const auto x = EmitExportComponentF32(state, inst, 0);
|
|
const auto y = EmitExportComponentF32(state, inst, 1);
|
|
const auto z = EmitExportComponentF32(state, inst, 2);
|
|
const auto w = EmitExportComponentF32(state, inst, 3);
|
|
const auto vec = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpCompositeConstruct, state.vec4_float_type, vec, x, y, z, w});
|
|
return vec;
|
|
}
|
|
|
|
uint32_t EmitExportComponentU32(EmitterState& state, const IR::Instruction& inst,
|
|
uint32_t component) {
|
|
const bool enabled = ((inst.export_info.en >> component) & 1u) != 0;
|
|
if (!enabled || component >= inst.src_count || component >= 4u) {
|
|
return ConstantU32(state, component == 3u ? 1u : 0u);
|
|
}
|
|
return EmitValueLoad(state, inst.src[component]);
|
|
}
|
|
|
|
uint32_t EmitExportVec4U32(EmitterState& state, const IR::Instruction& inst) {
|
|
uint32_t components[4] = {
|
|
ConstantU32(state, 0u),
|
|
ConstantU32(state, 0u),
|
|
ConstantU32(state, 0u),
|
|
ConstantU32(state, 1u),
|
|
};
|
|
|
|
if (inst.export_info.compr) {
|
|
for (uint32_t pair_index = 0; pair_index < 2u && pair_index < inst.src_count;
|
|
pair_index++) {
|
|
const auto raw = EmitValueLoad(state, inst.src[pair_index]);
|
|
for (uint32_t lane = 0; lane < 2u; lane++) {
|
|
const auto component = pair_index * 2u + lane;
|
|
if (((inst.export_info.en >> component) & 1u) == 0) {
|
|
continue;
|
|
}
|
|
components[component] = state.builder.AllocateId();
|
|
state.builder.AddFunction(
|
|
{OpBitFieldUExtract, state.uint_type, components[component], raw,
|
|
ConstantU32(state, lane * 16u), ConstantU32(state, 16u)});
|
|
}
|
|
}
|
|
} else {
|
|
for (uint32_t component = 0; component < 4u; component++) {
|
|
components[component] = EmitExportComponentU32(state, inst, component);
|
|
}
|
|
}
|
|
|
|
const auto vec = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpCompositeConstruct, state.vec4_uint_type, vec, components[0],
|
|
components[1], components[2], components[3]});
|
|
return vec;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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()) {
|
|
return value;
|
|
}
|
|
|
|
const auto mapping = state.pixel_input_info->target_export_mapping[inst.export_info.index];
|
|
if (mapping.IsIdentity()) {
|
|
return value;
|
|
}
|
|
|
|
const auto mapped = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpVectorShuffle, vector_type, mapped, value, value, mapping.Map(0),
|
|
mapping.Map(1), mapping.Map(2), mapping.Map(3)});
|
|
return mapped;
|
|
}
|
|
|
|
bool ExportWritesData(const IR::Instruction& inst) {
|
|
switch (inst.export_info.kind) {
|
|
case IR::ExportTargetKind::Null:
|
|
case IR::ExportTargetKind::Primitive: return false;
|
|
case IR::ExportTargetKind::MrtZ: return (inst.export_info.en & 0x5u) != 0;
|
|
default: return inst.export_info.en != 0;
|
|
}
|
|
}
|
|
|
|
void EmitMrtZExport(EmitterState& state, const IR::Instruction& inst) {
|
|
if ((inst.export_info.en & 0x1u) != 0 && state.depth_variable != 0) {
|
|
state.builder.AddFunction(
|
|
{OpStore, state.depth_variable, EmitExportComponentF32(state, inst, 0)});
|
|
}
|
|
|
|
if ((inst.export_info.en & 0x4u) != 0 && state.sample_mask_variable != 0) {
|
|
const auto raw =
|
|
inst.src_count > 2u ? EmitValueLoad(state, inst.src[2]) : ConstantU32(state, 0);
|
|
const auto mask = state.builder.AllocateId();
|
|
const auto ptr = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpBitcast, state.int_type, mask, raw});
|
|
state.builder.AddFunction({OpAccessChain, state.ptr_output_int, ptr,
|
|
state.sample_mask_variable, ConstantU32(state, 0)});
|
|
state.builder.AddFunction({OpStore, ptr, mask});
|
|
}
|
|
}
|
|
|
|
void EmitExport(EmitterState& state, const IR::Instruction& inst) {
|
|
if (inst.export_info.kind == IR::ExportTargetKind::Null ||
|
|
inst.export_info.kind == IR::ExportTargetKind::Primitive) {
|
|
return;
|
|
}
|
|
|
|
if (!ExportWritesData(inst)) {
|
|
return;
|
|
}
|
|
|
|
if (inst.export_info.kind == IR::ExportTargetKind::MrtZ) {
|
|
EmitMrtZExport(state, inst);
|
|
return;
|
|
}
|
|
|
|
const auto variable = OutputVariableForExport(state, inst.export_info);
|
|
if (variable == 0) {
|
|
return;
|
|
}
|
|
|
|
const auto uint_output = MrtUsesUintOutput(state, inst);
|
|
const auto vector_type = uint_output ? state.vec4_uint_type : state.vec4_float_type;
|
|
const auto value = ApplyMrtExportMapping(
|
|
state, inst, uint_output ? EmitExportVec4U32(state, inst) : EmitExportVec4F32(state, inst),
|
|
vector_type);
|
|
if (inst.export_info.kind == IR::ExportTargetKind::Position) {
|
|
const auto pointer = state.builder.AllocateId();
|
|
state.builder.AddFunction(
|
|
{OpAccessChain, state.ptr_output_vec4_float, pointer, variable, ConstantU32(state, 0)});
|
|
state.builder.AddFunction({OpStore, pointer, value});
|
|
return;
|
|
}
|
|
|
|
state.builder.AddFunction({OpStore, variable, value});
|
|
}
|
|
|
|
bool ExportUsesPixelValidMask(const EmitterState& state, const IR::Instruction& inst) {
|
|
return state.stage == ShaderType::Pixel && inst.export_info.vm && state.needs_pixel_valid_mask;
|
|
}
|
|
|
|
void EmitKillIfBoolFalse(EmitterState& state, uint32_t active) {
|
|
const auto kill_label = state.builder.AllocateId();
|
|
const auto merge_label = state.builder.AllocateId();
|
|
const auto inactive = state.builder.AllocateId();
|
|
state.builder.AddFunction({OpLogicalNot, state.bool_type, inactive, active});
|
|
state.builder.AddFunction({OpSelectionMerge, merge_label, SelectionControlNone});
|
|
state.builder.AddFunction({OpBranchConditional, inactive, kill_label, merge_label});
|
|
state.builder.AddFunction({OpLabel, kill_label});
|
|
state.builder.AddFunction({OpKill});
|
|
state.builder.AddFunction({OpLabel, merge_label});
|
|
}
|
|
|
|
void EmitUpdatePixelValidMask(EmitterState& state) {
|
|
if (state.pixel_valid_mask_variable == 0) {
|
|
return;
|
|
}
|
|
|
|
const auto active = EmitExecActiveBool(state);
|
|
const auto value = state.builder.AllocateId();
|
|
state.builder.AddFunction(
|
|
{OpSelect, state.uint_type, value, active, ConstantU32(state, 1), ConstantU32(state, 0)});
|
|
state.builder.AddFunction({OpStore, state.pixel_valid_mask_variable, value});
|
|
}
|
|
|
|
void EmitKillIfPixelValidMaskInactive(EmitterState& state) {
|
|
if (state.pixel_valid_mask_variable == 0) {
|
|
return;
|
|
}
|
|
|
|
const auto mask_value = state.builder.AllocateId();
|
|
const auto active = state.builder.AllocateId();
|
|
state.builder.AddFunction(
|
|
{OpLoad, state.uint_type, mask_value, state.pixel_valid_mask_variable});
|
|
state.builder.AddFunction(
|
|
{OpINotEqual, state.bool_type, active, mask_value, ConstantU32(state, 0)});
|
|
EmitKillIfBoolFalse(state, active);
|
|
}
|
|
|
|
} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter
|