mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
b4406b0467
Co-authored-by: fxpw <fxpw@outlook.com>
661 lines
29 KiB
C++
661 lines
29 KiB
C++
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SPIRVEMITTER_INTERNAL_H_
|
|
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SPIRVEMITTER_INTERNAL_H_
|
|
|
|
#include "common/common.h"
|
|
#include "common/stringUtils.h"
|
|
#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/passes/ResourceMaterialization.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <spirv/unified1/GLSL.std.450.h>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
|
|
|
struct InputBinding : IR::StageInput {
|
|
uint32_t variable_id = 0;
|
|
};
|
|
|
|
struct OutputBinding : IR::StageOutput {
|
|
uint32_t variable_id = 0;
|
|
uint32_t mesh_data_variable = 0;
|
|
};
|
|
|
|
using ImageDimension = Decoder::ImageDimension;
|
|
|
|
struct ImageDimensionInfo {
|
|
ImageDimension dimension;
|
|
uint32_t spirv_dimension;
|
|
uint32_t coordinate_components;
|
|
uint32_t spatial_components;
|
|
uint32_t arrayed;
|
|
uint32_t multisampled;
|
|
};
|
|
|
|
constexpr std::array<ImageDimensionInfo, 7> ImageDimensions {{
|
|
{ImageDimension::Dim1D, spv::Dim1D, 1, 1, 0, 0},
|
|
{ImageDimension::Dim1DArray, spv::Dim1D, 2, 1, 1, 0},
|
|
{ImageDimension::Dim2D, spv::Dim2D, 2, 2, 0, 0},
|
|
{ImageDimension::Dim2DArray, spv::Dim2D, 3, 2, 1, 0},
|
|
{ImageDimension::Dim3D, spv::Dim3D, 3, 3, 0, 0},
|
|
{ImageDimension::Dim2DMsaa, spv::Dim2D, 2, 2, 0, 1},
|
|
{ImageDimension::Dim2DMsaaArray, spv::Dim2D, 3, 2, 1, 1},
|
|
}};
|
|
|
|
const ImageDimensionInfo& ImageDimensionInfoFor(ImageDimension dimension);
|
|
|
|
struct BufferDefinition {
|
|
uint32_t variable = 0;
|
|
uint32_t type = 0;
|
|
uint32_t element_type = 0;
|
|
uint32_t pointer_type = 0;
|
|
uint32_t element_pointer_type = 0;
|
|
};
|
|
|
|
struct ImageDefinition {
|
|
uint32_t variable = 0;
|
|
uint32_t type = 0;
|
|
uint32_t pointer_type = 0;
|
|
uint32_t sampled_type = 0;
|
|
};
|
|
|
|
struct EmitterState {
|
|
EmitterState(IR::Program& program_, ShaderStageInputInfo input_info_, bool maintenance8_enabled_)
|
|
: builder(program_.stage == ShaderType::Mesh ? 0x00010400u : 0x00010300u),
|
|
program(program_), input_info(input_info_), maintenance8_enabled(maintenance8_enabled_) {
|
|
if (ShaderWorkgroupInput(program.stage, input_info) != nullptr) {
|
|
lds_storage_class = spv::StorageClassWorkgroup;
|
|
}
|
|
if (program.info.uses_lds && program.stage == ShaderType::Compute &&
|
|
input_info.compute != nullptr && input_info.compute->lds_storage) {
|
|
lds_storage_class = spv::StorageClassStorageBuffer;
|
|
}
|
|
}
|
|
|
|
Builder builder;
|
|
IR::Program& program;
|
|
ShaderStageInputInfo input_info;
|
|
bool maintenance8_enabled;
|
|
uint32_t debug_source = 0;
|
|
uint32_t void_type = 0;
|
|
uint32_t bool_type = 0;
|
|
uint32_t u32_type = 0;
|
|
uint32_t u64_type = 0;
|
|
uint32_t i32_type = 0;
|
|
uint32_t f32_type = 0;
|
|
uint32_t f64_type = 0;
|
|
uint32_t u32_pair_type = 0;
|
|
uint32_t i32_pair_type = 0;
|
|
uint32_t function_type = 0;
|
|
std::array<uint32_t, 3> bool_vector_types {};
|
|
std::array<uint32_t, 3> u32_vector_types {};
|
|
std::array<uint32_t, 3> i32_vector_types {};
|
|
std::array<uint32_t, 3> f32_vector_types {};
|
|
std::array<uint32_t, 6> tess_variables {};
|
|
uint32_t tess_inner_variable = 0;
|
|
uint32_t tess_patch_base = 0;
|
|
|
|
uint32_t lane_count = 1;
|
|
uint32_t lane_half = 0;
|
|
std::array<BufferDefinition, 4> storage_buffers {};
|
|
std::vector<uint32_t> memory_byte_offsets;
|
|
uint32_t bda_pagetable_variable = 0;
|
|
uint32_t fault_buffer_variable = 0;
|
|
uint32_t bda_pointer_function = 0;
|
|
uint32_t indirect_format_function = 0;
|
|
uint32_t indirect_format_geometry_function = 0;
|
|
std::map<std::array<uint32_t, 3>, uint32_t> indirect_buffer_load_functions;
|
|
uint32_t bvh_intersect_function = 0;
|
|
uint32_t gds_variable = 0;
|
|
uint32_t gds_length = 0;
|
|
uint32_t push_constant_variable = 0;
|
|
uint32_t shader_data_storage_variable = 0;
|
|
uint32_t flattened_srt_variable = 0;
|
|
spv::StorageClass lds_storage_class = spv::StorageClassFunction;
|
|
uint32_t lds_base_dwords = 0;
|
|
uint32_t lds_variable = 0;
|
|
uint32_t lds_u64_variable = 0;
|
|
std::array<uint32_t, 2> scratch_variable {};
|
|
std::array<ImageDefinition, IR::ImageBindingCount> images {};
|
|
uint32_t sampler_variable = 0;
|
|
uint32_t sampler_type = 0;
|
|
uint32_t sampler_pointer_type = 0;
|
|
uint32_t main_func = 0;
|
|
uint32_t mesh_guest_func = 0;
|
|
uint32_t mesh_allocation = 0;
|
|
uint32_t mesh_primitive_data = 0;
|
|
uint32_t mesh_primitives = 0;
|
|
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;
|
|
uint32_t point_size_variable = 0;
|
|
uint32_t clip_distance_variable = 0;
|
|
uint32_t invalid_position_clip_distance = UINT32_MAX;
|
|
uint32_t cull_distance_variable = 0;
|
|
uint32_t layer_variable = 0;
|
|
uint32_t viewport_index_variable = 0;
|
|
uint32_t depth_variable = 0;
|
|
uint32_t sample_mask_variable = 0;
|
|
std::vector<InputBinding> inputs;
|
|
std::vector<OutputBinding> outputs;
|
|
std::vector<uint32_t> interface_variables;
|
|
};
|
|
|
|
uint32_t TypeVoid(EmitterState& state);
|
|
uint32_t TypeBool(EmitterState& state);
|
|
uint32_t TypeBoolVector(EmitterState& state, uint32_t components);
|
|
uint32_t TypeU32(EmitterState& state);
|
|
uint32_t TypeU64(EmitterState& state);
|
|
uint32_t TypeU32Pair(EmitterState& state);
|
|
uint32_t TypeI32(EmitterState& state);
|
|
uint32_t TypeI32Pair(EmitterState& state);
|
|
uint32_t TypeF32(EmitterState& state);
|
|
uint32_t TypeF64(EmitterState& state);
|
|
uint32_t TypeU32Vector(EmitterState& state, uint32_t components);
|
|
|
|
uint32_t TypeU32Composite(EmitterState& state, uint32_t components);
|
|
uint32_t TypeI32Vector(EmitterState& state, uint32_t components);
|
|
uint32_t TypeF32Vector(EmitterState& state, uint32_t components);
|
|
uint32_t TypePointer(EmitterState& state, spv::StorageClass storage_class, uint32_t pointee);
|
|
uint32_t TypeFunction(EmitterState& state);
|
|
uint32_t TypeStorageBufferElementPointer(EmitterState& state, uint32_t bits = 32);
|
|
uint32_t TypePhysicalU32Pointer(EmitterState& state);
|
|
uint32_t TypePushConstantElementPointer(EmitterState& state);
|
|
uint32_t TypeU32ArrayPointer(EmitterState& state, spv::StorageClass storage_class, uint32_t dwords);
|
|
uint32_t TypeU32ElementPointer(EmitterState& state, spv::StorageClass storage_class);
|
|
|
|
inline void EmitLabel(EmitterState& state, uint32_t label) {
|
|
state.current_label = label;
|
|
state.builder.AddFunction(spv::OpLabel, label);
|
|
if (state.debug_source != 0 && state.current_block != nullptr) {
|
|
// Source lines encode the containing native block's byte offset plus one.
|
|
state.builder.AddFunction(spv::OpLine, state.debug_source,
|
|
state.current_block->start_pc + 1u, 0u);
|
|
}
|
|
}
|
|
|
|
uint32_t TypeId(EmitterState& state, IR::Type type);
|
|
|
|
// Shared instruction construction; typed aliases add no forwarding functions.
|
|
template <spv::Op opcode, IR::Type type, typename... Args>
|
|
uint32_t EmitNative(EmitterState& state, Args... args) {
|
|
const auto result = state.builder.AllocateId();
|
|
state.builder.AddFunction(opcode, TypeId(state, type), result, args...);
|
|
if constexpr (type == IR::Type::F64 &&
|
|
(opcode == spv::OpFMul || opcode == spv::OpFDiv || opcode == spv::OpExtInst)) {
|
|
state.builder.AddAnnotation(spv::OpDecorate, result, spv::DecorationNoContraction);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint32_t GlslStd450(EmitterState& state);
|
|
|
|
template <GLSLstd450 opcode, IR::Type type, typename... Args>
|
|
uint32_t EmitGlsl(EmitterState& state, Args... args) {
|
|
return EmitNative<spv::OpExtInst, type>(state, GlslStd450(state), opcode, args...);
|
|
}
|
|
|
|
inline uint32_t Unary(EmitterState& state, spv::Op opcode, uint32_t type, uint32_t value) {
|
|
const auto result = state.builder.AllocateId();
|
|
state.builder.AddFunction(opcode, type, result, value);
|
|
return result;
|
|
}
|
|
|
|
inline uint32_t PackU64(EmitterState& state, uint32_t low, uint32_t high) {
|
|
const auto pair = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 2), pair, low, high);
|
|
return Unary(state, spv::OpBitcast, TypeU64(state), pair);
|
|
}
|
|
|
|
inline uint32_t Binary(EmitterState& state, spv::Op opcode, uint32_t type, uint32_t lhs,
|
|
uint32_t rhs) {
|
|
const auto result = state.builder.AllocateId();
|
|
state.builder.AddFunction(opcode, type, result, lhs, rhs);
|
|
return result;
|
|
}
|
|
|
|
inline uint32_t Select(EmitterState& state, uint32_t type, uint32_t condition, uint32_t true_value,
|
|
uint32_t false_value) {
|
|
const auto result = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpSelect, type, result, condition, true_value, false_value);
|
|
return result;
|
|
}
|
|
|
|
struct ValueEmitContext {
|
|
explicit ValueEmitContext(EmitterState& state_): state(state_) {}
|
|
|
|
uint32_t Def(IR::Value value);
|
|
uint32_t Arg(const IR::Inst& inst, size_t index);
|
|
uint32_t HalfArg(const IR::Inst& inst, size_t index, uint32_t half);
|
|
uint32_t Ballot(IR::Value predicate);
|
|
uint32_t FirstLane(uint32_t ballot);
|
|
uint32_t Shuffle(const IR::Inst& inst, size_t index, uint32_t lane);
|
|
uint32_t Result(const IR::Inst& inst);
|
|
uint32_t Define(const IR::Inst& inst, uint32_t value);
|
|
uint32_t ResourceIndex(IR::Value value, IR::ValueOpcode opcode);
|
|
const IR::Inst* ImageAddress(IR::Value value);
|
|
const IR::MemoryInfo& Memory(const IR::Inst& inst) const;
|
|
const IR::ExportInfo& Export(const IR::Inst& inst) const;
|
|
[[noreturn]] void Fail(const char* reason) const;
|
|
[[noreturn]] void Fail(const IR::Inst& inst, const char* reason) const;
|
|
|
|
EmitterState& state;
|
|
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;
|
|
uint32_t scratch_u32_variable = 0;
|
|
ValueEmitContext* other_half = nullptr;
|
|
uint32_t half = 0;
|
|
};
|
|
|
|
enum class VertexInputScalarKind { Float, Sint, Uint };
|
|
|
|
constexpr uint32_t NoImageComponent = 0xffffffffu;
|
|
|
|
struct DppTargetLane {
|
|
uint32_t lane = 0;
|
|
uint32_t valid = 0;
|
|
};
|
|
|
|
struct ImageSampleLayout {
|
|
uint32_t offset = NoImageComponent;
|
|
uint32_t dref = NoImageComponent;
|
|
uint32_t bias = NoImageComponent;
|
|
uint32_t coord = 0;
|
|
uint32_t lod = NoImageComponent;
|
|
uint32_t grad_x = NoImageComponent;
|
|
uint32_t grad_y = NoImageComponent;
|
|
};
|
|
|
|
struct F32Class {
|
|
uint32_t bits = 0;
|
|
uint32_t nan = 0;
|
|
uint32_t zero = 0;
|
|
};
|
|
|
|
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr);
|
|
|
|
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr);
|
|
|
|
bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr);
|
|
|
|
VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint32_t location);
|
|
|
|
uint32_t VertexParameterComponentCount(const InputBinding& input);
|
|
|
|
uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind);
|
|
|
|
|
|
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp);
|
|
|
|
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
|
|
|
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
|
|
|
|
[[noreturn]] void ExitDescriptorBindingFailure(const EmitterState& state,
|
|
IR::DescriptorBindingKind kind, uint32_t resource,
|
|
const char* reason);
|
|
|
|
uint32_t DescriptorElementPointer(EmitterState& state, uint32_t result_ptr_type,
|
|
uint32_t variable_id, uint32_t array_index,
|
|
IR::DescriptorBindingKind kind, uint32_t resource,
|
|
const char* variable_name);
|
|
|
|
uint32_t ImageScalarType(EmitterState& state, Prospero::TextureNumericClass numeric_class);
|
|
|
|
uint32_t ImageVectorType(EmitterState& state, Prospero::TextureNumericClass numeric_class,
|
|
uint32_t components);
|
|
|
|
uint32_t ImageType(EmitterState& state, const IR::ImageResource& image);
|
|
|
|
uint32_t ImageViewSizeType(EmitterState& state, ImageDimension dimension);
|
|
|
|
uint32_t ImageDescriptorPointer(EmitterState& state, uint32_t resource, uint32_t mip = 0,
|
|
uint32_t array_index = 0);
|
|
|
|
uint32_t LoadImageDescriptor(EmitterState& state, uint32_t resource, uint32_t mip = 0,
|
|
uint32_t array_index = 0);
|
|
|
|
uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler, uint32_t array_index = 0);
|
|
|
|
uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampler_id,
|
|
uint32_t mip = 0, uint32_t array_index = 0,
|
|
bool sampler_dynamic = false);
|
|
|
|
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
|
|
uint32_t texel, uint32_t array_index);
|
|
|
|
spv::ExecutionModel ExecutionModelForStage(ShaderType stage);
|
|
|
|
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
|
|
|
uint32_t ConstantI32(EmitterState& state, int32_t value);
|
|
|
|
uint32_t ConstantF32(EmitterState& state, uint32_t bits);
|
|
|
|
uint32_t ConstantF32Value(EmitterState& state, float value);
|
|
|
|
uint32_t ConstantBool(EmitterState& state, bool value);
|
|
|
|
uint32_t ConstantU64(EmitterState& state, uint64_t value);
|
|
|
|
uint32_t ConstantU32CompositeZero(EmitterState& state, uint32_t components);
|
|
|
|
uint32_t DefineInterfaceVariable(EmitterState& state, uint32_t type, spv::StorageClass storage,
|
|
const char* name);
|
|
void DefineModule(EmitterState& state);
|
|
void DefineTessellationInterfaces(EmitterState& state);
|
|
void DefineTessellationExecutionModes(EmitterState& state);
|
|
void DefineMeshOutputs(EmitterState& state, uint32_t clip_distance_count,
|
|
uint32_t cull_distance_count);
|
|
void EmitMeshEntryPoint(EmitterState& state);
|
|
void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst);
|
|
uint32_t MeshOutputPointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index = 0);
|
|
uint32_t MeshPrimitivePointer(EmitterState& state);
|
|
|
|
DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control,
|
|
uint32_t lane_bits);
|
|
|
|
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
|
|
bool left);
|
|
|
|
DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subid, uint32_t amount);
|
|
|
|
DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row);
|
|
|
|
DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags);
|
|
|
|
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
|
|
|
|
uint32_t InputVariableForKind(const EmitterState& state, IR::StageInputKind kind);
|
|
|
|
const InputBinding* InputBindingForParameter(const EmitterState& state, uint32_t location);
|
|
|
|
uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding& input,
|
|
uint32_t component);
|
|
|
|
uint32_t EmitInputComponentU32(EmitterState& state, IR::StageInputKind kind, uint32_t component);
|
|
|
|
uint32_t EmitLocalInvocationIndex(EmitterState& state);
|
|
|
|
uint32_t EmitBallotLaneActiveBool(EmitterState& state, uint32_t ballot, uint32_t lane);
|
|
|
|
uint32_t EmitSubgroupLaneActiveBool(EmitterState& state, uint32_t lane);
|
|
|
|
inline constexpr auto EmitAddU32 = EmitNative<spv::OpIAdd, IR::Type::U32, uint32_t, uint32_t>;
|
|
|
|
uint32_t EmitBinaryU32(EmitterState& state, spv::Op opcode, uint32_t lhs, uint32_t rhs);
|
|
|
|
uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index);
|
|
|
|
uint32_t StorageBufferPackedStride(const EmitterState& state, const IR::MemoryInfo& mem);
|
|
|
|
Prospero::BufferFormat StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem);
|
|
|
|
uint32_t SharedIndirectBufferResource(const EmitterState& state, uint32_t resource);
|
|
|
|
void EmitMemoryOffsets(EmitterState& state);
|
|
|
|
uint32_t LdsDwordCount(const EmitterState& state);
|
|
void EnsureLdsStorage(EmitterState& state);
|
|
|
|
struct MemoryResourceAccess {
|
|
IR::ResourceKind kind = IR::ResourceKind::None;
|
|
uint32_t object_pointer = 0;
|
|
uint32_t length = 0;
|
|
uint32_t byte_offset = 0;
|
|
uint32_t element_bits = 32;
|
|
bool non_uniform = false;
|
|
spv::MemoryAccessMask memory_access = spv::MemoryAccessMaskNone;
|
|
};
|
|
|
|
uint32_t EmitIndirectResourceIndex(EmitterState& state, uint32_t key, uint32_t mapping_offset,
|
|
uint32_t search_iterations, uint32_t default_resource);
|
|
MemoryResourceAccess PrepareMemoryResourceAccess(EmitterState& state, const IR::MemoryInfo& mem,
|
|
uint32_t array_index = 0u);
|
|
|
|
MemoryResourceAccess PrepareStorageBufferResourceAccess(EmitterState& state,
|
|
const IR::MemoryInfo& mem,
|
|
BufferDefinition& buffer,
|
|
uint32_t array_index = 0);
|
|
|
|
uint32_t EmitMemoryElementInBounds(EmitterState& state, const MemoryResourceAccess& access,
|
|
uint32_t index);
|
|
|
|
uint32_t EmitMemoryElementPointer(EmitterState& state, const MemoryResourceAccess& access,
|
|
uint32_t index);
|
|
|
|
uint32_t EmitStorageBufferElementPointer(EmitterState& state, const MemoryResourceAccess& access,
|
|
uint32_t index, uint32_t pointer_type);
|
|
|
|
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value);
|
|
|
|
inline constexpr auto EmitTBufferSelectF32 =
|
|
EmitNative<spv::OpSelect, IR::Type::F32, uint32_t, uint32_t, uint32_t>;
|
|
|
|
bool IsSignedFormatComponent(Format::ComponentType type);
|
|
|
|
uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits);
|
|
|
|
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
|
uint32_t component, uint32_t raw);
|
|
|
|
enum class UnormRounding { RoundEven, HalfUp, Truncate };
|
|
|
|
uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
|
uint32_t component, uint32_t data, uint32_t data_bits,
|
|
UnormRounding rounding = UnormRounding::RoundEven);
|
|
|
|
uint32_t EncodePackedColorComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
|
uint32_t component, uint32_t data, uint32_t data_bits,
|
|
UnormRounding rounding = UnormRounding::RoundEven);
|
|
|
|
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode);
|
|
|
|
uint32_t EmitAtomicOperation(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t pointer,
|
|
uint32_t scope);
|
|
|
|
void EmitAtomicMemoryBarrier(EmitterState& state, IR::ResourceKind kind);
|
|
|
|
uint32_t EmitFloatAtomicReplacement(EmitterState& state, uint32_t old, uint32_t source,
|
|
bool max_value);
|
|
|
|
uint32_t EmitDsFloatAtomicReplacement(EmitterState& state, uint32_t old, uint32_t source,
|
|
bool max_value);
|
|
|
|
uint32_t EmitDsSwizzleTargetLane(EmitterState& state, uint32_t subid, uint32_t control);
|
|
|
|
inline constexpr auto EmitSelectValueU32 =
|
|
EmitNative<spv::OpSelect, IR::Type::U32, uint32_t, uint32_t, uint32_t>;
|
|
|
|
uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t mask);
|
|
|
|
uint32_t EmitShiftRightConstant(EmitterState& state, uint32_t value, uint32_t shift);
|
|
|
|
inline constexpr auto EmitOrU32 = EmitNative<spv::OpBitwiseOr, IR::Type::U32, uint32_t, uint32_t>;
|
|
|
|
uint32_t EmitCompareU32Constant(EmitterState& state, spv::Op opcode, uint32_t value,
|
|
uint32_t constant);
|
|
|
|
uint32_t EmitSubConstantMinusU32(EmitterState& state, uint32_t constant, uint32_t value);
|
|
|
|
uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32);
|
|
|
|
inline constexpr auto EmitBitcastF32ToU32 = EmitNative<spv::OpBitcast, IR::Type::U32, uint32_t>;
|
|
|
|
inline constexpr auto EmitBitcastU32ToF32 = EmitNative<spv::OpBitcast, IR::Type::F32, uint32_t>;
|
|
|
|
inline constexpr auto EmitAndU32 = EmitNative<spv::OpBitwiseAnd, IR::Type::U32, uint32_t, uint32_t>;
|
|
|
|
inline constexpr auto EmitLogicalAndBool =
|
|
EmitNative<spv::OpLogicalAnd, IR::Type::U1, uint32_t, uint32_t>;
|
|
|
|
inline constexpr auto EmitLogicalOrBool =
|
|
EmitNative<spv::OpLogicalOr, IR::Type::U1, uint32_t, uint32_t>;
|
|
|
|
inline constexpr auto EmitLogicalNotBool = EmitNative<spv::OpLogicalNot, IR::Type::U1, uint32_t>;
|
|
|
|
F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits);
|
|
|
|
F32Class EmitClassifyF32(EmitterState& state, uint32_t value);
|
|
|
|
uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
|
uint32_t class_match);
|
|
|
|
uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask);
|
|
uint32_t EmitClassMaskF16(EmitterState& state, uint32_t bits, uint32_t mask);
|
|
|
|
uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value);
|
|
|
|
inline constexpr auto EmitTruncF32Value = EmitGlsl<GLSLstd450Trunc, IR::Type::F32, uint32_t>;
|
|
|
|
uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value);
|
|
|
|
uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signed_zero);
|
|
|
|
inline constexpr auto EmitFNegateValue = EmitNative<spv::OpFNegate, IR::Type::F32, uint32_t>;
|
|
|
|
inline constexpr auto EmitFAbsValue = EmitGlsl<GLSLstd450FAbs, IR::Type::F32, uint32_t>;
|
|
|
|
uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits);
|
|
|
|
void EmitProgram(EmitterState& state);
|
|
|
|
void DefineGetBdaPointer(EmitterState& state);
|
|
void DefineIndirectBufferFormats(ValueEmitContext& ctx);
|
|
void DefineIndirectBufferLoads(ValueEmitContext& ctx);
|
|
void DefineBvhIntersect(EmitterState& state);
|
|
uint32_t GetBdaPointer(EmitterState& state, uint32_t address);
|
|
|
|
// These templates accept local lambdas from several emitter translation units.
|
|
template <typename Fn>
|
|
auto EmitIndexSwitch(EmitterState& state, uint32_t index, uint32_t count,
|
|
uint32_t result_type, Fn&& emit) {
|
|
constexpr bool has_result = !std::is_void_v<std::invoke_result_t<Fn, uint32_t>>;
|
|
EXIT_IF(count == 0u);
|
|
const auto merge_label = state.builder.AllocateId();
|
|
std::vector<uint32_t> labels(count);
|
|
std::vector<uint32_t> words {spv::OpSwitch, index, merge_label};
|
|
for (uint32_t item = 0; item < count; item++) {
|
|
labels[item] = state.builder.AllocateId();
|
|
words.push_back(item);
|
|
words.push_back(labels[item]);
|
|
}
|
|
if constexpr (has_result) {
|
|
words[2] = labels.front();
|
|
}
|
|
state.builder.AddFunction(spv::OpSelectionMerge, merge_label, spv::SelectionControlMaskNone);
|
|
state.builder.AddFunction(words);
|
|
std::vector<uint32_t> phi;
|
|
if constexpr (has_result) {
|
|
phi = {spv::OpPhi, result_type, state.builder.AllocateId()};
|
|
}
|
|
for (uint32_t item = 0; item < count; item++) {
|
|
EmitLabel(state, labels[item]);
|
|
if constexpr (has_result) {
|
|
phi.push_back(emit(item));
|
|
phi.push_back(state.current_label);
|
|
} else {
|
|
emit(item);
|
|
}
|
|
state.builder.AddFunction(spv::OpBranch, merge_label);
|
|
}
|
|
EmitLabel(state, merge_label);
|
|
if constexpr (has_result) {
|
|
state.builder.AddFunction(phi);
|
|
return phi[2];
|
|
}
|
|
}
|
|
|
|
template <typename Fn>
|
|
void EmitIfCondition(EmitterState& state, uint32_t condition, Fn&& fn) {
|
|
const auto then_label = state.builder.AllocateId();
|
|
const auto merge_label = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpSelectionMerge, merge_label, spv::SelectionControlMaskNone);
|
|
state.builder.AddFunction(spv::OpBranchConditional, condition, then_label, merge_label);
|
|
EmitLabel(state, then_label);
|
|
fn();
|
|
state.builder.AddFunction(spv::OpBranch, merge_label);
|
|
EmitLabel(state, merge_label);
|
|
}
|
|
|
|
template <typename Fn>
|
|
uint32_t EmitValueOrDefaultIfCondition(EmitterState& state, uint32_t condition, uint32_t type,
|
|
uint32_t default_value, Fn&& fn) {
|
|
const auto then_label = state.builder.AllocateId();
|
|
const auto header = state.current_label;
|
|
const auto merge_label = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpSelectionMerge, merge_label, spv::SelectionControlMaskNone);
|
|
state.builder.AddFunction(spv::OpBranchConditional, condition, then_label, merge_label);
|
|
EmitLabel(state, then_label);
|
|
const auto then_value = fn();
|
|
const auto then_exit = state.current_label;
|
|
state.builder.AddFunction(spv::OpBranch, merge_label);
|
|
EmitLabel(state, merge_label);
|
|
const auto value = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpPhi, type, value, then_value, then_exit, default_value,
|
|
header);
|
|
return value;
|
|
}
|
|
|
|
template <typename Fn>
|
|
uint32_t EmitValueOrZeroIfCondition(EmitterState& state, uint32_t condition, Fn&& fn) {
|
|
return EmitValueOrDefaultIfCondition(state, condition, TypeU32(state), ConstantU32(state, 0),
|
|
std::forward<Fn>(fn));
|
|
}
|
|
|
|
template <typename Fn>
|
|
uint32_t AtomicUpdate(EmitterState& state, uint32_t pointer, IR::ResourceKind kind, Fn&& desired) {
|
|
const auto scope = kind == IR::ResourceKind::Lds ? spv::ScopeWorkgroup : spv::ScopeDevice;
|
|
const auto preheader = state.builder.AllocateId();
|
|
const auto header = state.builder.AllocateId();
|
|
const auto cont = state.builder.AllocateId();
|
|
const auto merge = state.builder.AllocateId();
|
|
const auto initial = state.builder.AllocateId();
|
|
const auto observed = state.builder.AllocateId();
|
|
const auto exchanged = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpBranch, preheader);
|
|
EmitLabel(state, preheader);
|
|
state.builder.AddFunction(spv::OpAtomicLoad, TypeU32(state), initial, pointer,
|
|
ConstantU32(state, scope),
|
|
ConstantU32(state, spv::MemorySemanticsMaskNone));
|
|
state.builder.AddFunction(spv::OpBranch, header);
|
|
EmitLabel(state, header);
|
|
state.builder.AddFunction(spv::OpPhi, TypeU32(state), observed, initial, preheader, exchanged,
|
|
cont);
|
|
const auto next = desired(observed);
|
|
state.builder.AddFunction(spv::OpAtomicCompareExchange, TypeU32(state), exchanged, pointer,
|
|
ConstantU32(state, scope),
|
|
ConstantU32(state, spv::MemorySemanticsMaskNone),
|
|
ConstantU32(state, spv::MemorySemanticsMaskNone), next, observed);
|
|
const auto success = state.builder.AllocateId();
|
|
state.builder.AddFunction(spv::OpIEqual, TypeBool(state), success, exchanged, observed);
|
|
state.builder.AddFunction(spv::OpLoopMerge, merge, cont, spv::LoopControlMaskNone);
|
|
state.builder.AddFunction(spv::OpBranchConditional, success, merge, cont);
|
|
EmitLabel(state, cont);
|
|
state.builder.AddFunction(spv::OpBranch, header);
|
|
EmitLabel(state, merge);
|
|
EmitAtomicMemoryBarrier(state, kind);
|
|
return observed;
|
|
}
|
|
|
|
} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter
|
|
|
|
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SPIRVEMITTER_INTERNAL_H_ */
|