early-access version 1866
This commit is contained in:
1368
src/shader_recompiler/backend/spirv/emit_context.cpp
Executable file
1368
src/shader_recompiler/backend/spirv/emit_context.cpp
Executable file
File diff suppressed because it is too large
Load Diff
307
src/shader_recompiler/backend/spirv/emit_context.h
Executable file
307
src/shader_recompiler/backend/spirv/emit_context.h
Executable file
@@ -0,0 +1,307 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
using Sirit::Id;
|
||||
|
||||
class VectorTypes {
|
||||
public:
|
||||
void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name);
|
||||
|
||||
[[nodiscard]] Id operator[](size_t size) const noexcept {
|
||||
return defs[size - 1];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Id, 4> defs{};
|
||||
};
|
||||
|
||||
struct TextureDefinition {
|
||||
Id id;
|
||||
Id sampled_type;
|
||||
Id pointer_type;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct TextureBufferDefinition {
|
||||
Id id;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct ImageBufferDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct ImageDefinition {
|
||||
Id id;
|
||||
Id image_type;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct UniformDefinitions {
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U32{};
|
||||
Id F32{};
|
||||
Id U32x2{};
|
||||
Id U32x4{};
|
||||
};
|
||||
|
||||
struct StorageTypeDefinition {
|
||||
Id array{};
|
||||
Id element{};
|
||||
};
|
||||
|
||||
struct StorageTypeDefinitions {
|
||||
StorageTypeDefinition U8{};
|
||||
StorageTypeDefinition S8{};
|
||||
StorageTypeDefinition U16{};
|
||||
StorageTypeDefinition S16{};
|
||||
StorageTypeDefinition U32{};
|
||||
StorageTypeDefinition U64{};
|
||||
StorageTypeDefinition F32{};
|
||||
StorageTypeDefinition U32x2{};
|
||||
StorageTypeDefinition U32x4{};
|
||||
};
|
||||
|
||||
struct StorageDefinitions {
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U32{};
|
||||
Id F32{};
|
||||
Id U64{};
|
||||
Id U32x2{};
|
||||
Id U32x4{};
|
||||
};
|
||||
|
||||
struct GenericElementInfo {
|
||||
Id id{};
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
class EmitContext final : public Sirit::Module {
|
||||
public:
|
||||
explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& binding);
|
||||
~EmitContext();
|
||||
|
||||
[[nodiscard]] Id Def(const IR::Value& value);
|
||||
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2) {
|
||||
return ConstantComposite(U32[2], Const(element_1), Const(element_2));
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2, u32 element_3) {
|
||||
return ConstantComposite(U32[3], Const(element_1), Const(element_2), Const(element_3));
|
||||
}
|
||||
|
||||
Id Const(u32 element_1, u32 element_2, u32 element_3, u32 element_4) {
|
||||
return ConstantComposite(U32[4], Const(element_1), Const(element_2), Const(element_3),
|
||||
Const(element_4));
|
||||
}
|
||||
|
||||
Id SConst(s32 value) {
|
||||
return Constant(S32[1], value);
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2) {
|
||||
return ConstantComposite(S32[2], SConst(element_1), SConst(element_2));
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2, s32 element_3) {
|
||||
return ConstantComposite(S32[3], SConst(element_1), SConst(element_2), SConst(element_3));
|
||||
}
|
||||
|
||||
Id SConst(s32 element_1, s32 element_2, s32 element_3, s32 element_4) {
|
||||
return ConstantComposite(S32[4], SConst(element_1), SConst(element_2), SConst(element_3),
|
||||
SConst(element_4));
|
||||
}
|
||||
|
||||
Id Const(f32 value) {
|
||||
return Constant(F32[1], value);
|
||||
}
|
||||
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
Stage stage{};
|
||||
|
||||
Id void_id{};
|
||||
Id U1{};
|
||||
Id U8{};
|
||||
Id S8{};
|
||||
Id U16{};
|
||||
Id S16{};
|
||||
Id U64{};
|
||||
VectorTypes F32;
|
||||
VectorTypes U32;
|
||||
VectorTypes S32;
|
||||
VectorTypes F16;
|
||||
VectorTypes F64;
|
||||
|
||||
Id true_value{};
|
||||
Id false_value{};
|
||||
Id u32_zero_value{};
|
||||
Id f32_zero_value{};
|
||||
|
||||
UniformDefinitions uniform_types;
|
||||
StorageTypeDefinitions storage_types;
|
||||
|
||||
Id private_u32{};
|
||||
|
||||
Id shared_u8{};
|
||||
Id shared_u16{};
|
||||
Id shared_u32{};
|
||||
Id shared_u64{};
|
||||
Id shared_u32x2{};
|
||||
Id shared_u32x4{};
|
||||
|
||||
Id input_f32{};
|
||||
Id input_u32{};
|
||||
Id input_s32{};
|
||||
|
||||
Id output_f32{};
|
||||
Id output_u32{};
|
||||
|
||||
Id image_buffer_type{};
|
||||
Id sampled_texture_buffer_type{};
|
||||
Id image_u32{};
|
||||
|
||||
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
|
||||
std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
|
||||
std::vector<TextureBufferDefinition> texture_buffers;
|
||||
std::vector<ImageBufferDefinition> image_buffers;
|
||||
std::vector<TextureDefinition> textures;
|
||||
std::vector<ImageDefinition> images;
|
||||
|
||||
Id workgroup_id{};
|
||||
Id local_invocation_id{};
|
||||
Id invocation_id{};
|
||||
Id sample_id{};
|
||||
Id is_helper_invocation{};
|
||||
Id subgroup_local_invocation_id{};
|
||||
Id subgroup_mask_eq{};
|
||||
Id subgroup_mask_lt{};
|
||||
Id subgroup_mask_le{};
|
||||
Id subgroup_mask_gt{};
|
||||
Id subgroup_mask_ge{};
|
||||
Id instance_id{};
|
||||
Id instance_index{};
|
||||
Id base_instance{};
|
||||
Id vertex_id{};
|
||||
Id vertex_index{};
|
||||
Id base_vertex{};
|
||||
Id front_face{};
|
||||
Id point_coord{};
|
||||
Id tess_coord{};
|
||||
Id clip_distances{};
|
||||
Id layer{};
|
||||
Id viewport_index{};
|
||||
Id viewport_mask{};
|
||||
Id primitive_id{};
|
||||
|
||||
Id fswzadd_lut_a{};
|
||||
Id fswzadd_lut_b{};
|
||||
|
||||
Id indexed_load_func{};
|
||||
Id indexed_store_func{};
|
||||
|
||||
Id local_memory{};
|
||||
|
||||
Id shared_memory_u8{};
|
||||
Id shared_memory_u16{};
|
||||
Id shared_memory_u32{};
|
||||
Id shared_memory_u64{};
|
||||
Id shared_memory_u32x2{};
|
||||
Id shared_memory_u32x4{};
|
||||
|
||||
Id shared_memory_u32_type{};
|
||||
|
||||
Id shared_store_u8_func{};
|
||||
Id shared_store_u16_func{};
|
||||
Id increment_cas_shared{};
|
||||
Id increment_cas_ssbo{};
|
||||
Id decrement_cas_shared{};
|
||||
Id decrement_cas_ssbo{};
|
||||
Id f32_add_cas{};
|
||||
Id f16x2_add_cas{};
|
||||
Id f16x2_min_cas{};
|
||||
Id f16x2_max_cas{};
|
||||
Id f32x2_add_cas{};
|
||||
Id f32x2_min_cas{};
|
||||
Id f32x2_max_cas{};
|
||||
|
||||
Id load_global_func_u32{};
|
||||
Id load_global_func_u32x2{};
|
||||
Id load_global_func_u32x4{};
|
||||
Id write_global_func_u32{};
|
||||
Id write_global_func_u32x2{};
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
Id output_tess_level_inner{};
|
||||
std::array<Id, 30> patches{};
|
||||
|
||||
std::array<Id, 8> frag_color{};
|
||||
Id sample_mask{};
|
||||
Id frag_depth{};
|
||||
|
||||
std::vector<Id> interfaces;
|
||||
|
||||
private:
|
||||
void DefineCommonTypes(const Info& info);
|
||||
void DefineCommonConstants();
|
||||
void DefineInterfaces(const IR::Program& program);
|
||||
void DefineLocalMemory(const IR::Program& program);
|
||||
void DefineSharedMemory(const IR::Program& program);
|
||||
void DefineSharedMemoryFunctions(const IR::Program& program);
|
||||
void DefineConstantBuffers(const Info& info, u32& binding);
|
||||
void DefineStorageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextureBuffers(const Info& info, u32& binding);
|
||||
void DefineImageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextures(const Info& info, u32& binding);
|
||||
void DefineImages(const Info& info, u32& binding);
|
||||
void DefineAttributeMemAccess(const Info& info);
|
||||
void DefineGlobalMemoryFunctions(const Info& info);
|
||||
|
||||
void DefineInputs(const IR::Program& program);
|
||||
void DefineOutputs(const IR::Program& program);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
541
src/shader_recompiler/backend/spirv/emit_spirv.cpp
Executable file
541
src/shader_recompiler/backend/spirv/emit_spirv.cpp
Executable file
@@ -0,0 +1,541 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <span>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ArgType>
|
||||
ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Id>) {
|
||||
return ctx.Def(arg);
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return arg;
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return arg.U32();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return arg.Attribute();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return arg.Patch();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return arg.Reg();
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, inst,
|
||||
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
} else {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
Id TypeId(const EmitContext& ctx, IR::Type type) {
|
||||
switch (type) {
|
||||
case IR::Type::U1:
|
||||
return ctx.U1;
|
||||
case IR::Type::U32:
|
||||
return ctx.U32[1];
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
void Traverse(EmitContext& ctx, IR::Program& program) {
|
||||
IR::Block* current_block{};
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block: {
|
||||
const Id label{node.data.block->Definition<Id>()};
|
||||
if (current_block) {
|
||||
ctx.OpBranch(label);
|
||||
}
|
||||
current_block = node.data.block;
|
||||
ctx.AddLabel(label);
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::AbstractSyntaxNode::Type::If: {
|
||||
const Id if_label{node.data.if_node.body->Definition<Id>()};
|
||||
const Id endif_label{node.data.if_node.merge->Definition<Id>()};
|
||||
ctx.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(ctx.Def(node.data.if_node.cond), if_label, endif_label);
|
||||
break;
|
||||
}
|
||||
case IR::AbstractSyntaxNode::Type::Loop: {
|
||||
const Id body_label{node.data.loop.body->Definition<Id>()};
|
||||
const Id continue_label{node.data.loop.continue_block->Definition<Id>()};
|
||||
const Id endloop_label{node.data.loop.merge->Definition<Id>()};
|
||||
|
||||
ctx.OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone);
|
||||
ctx.OpBranch(body_label);
|
||||
break;
|
||||
}
|
||||
case IR::AbstractSyntaxNode::Type::Break: {
|
||||
const Id break_label{node.data.break_node.merge->Definition<Id>()};
|
||||
const Id skip_label{node.data.break_node.skip->Definition<Id>()};
|
||||
ctx.OpBranchConditional(ctx.Def(node.data.break_node.cond), break_label, skip_label);
|
||||
break;
|
||||
}
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
if (current_block) {
|
||||
ctx.OpBranch(node.data.end_if.merge->Definition<Id>());
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat: {
|
||||
Id cond{ctx.Def(node.data.repeat.cond)};
|
||||
if (!Settings::values.disable_shader_loop_safety_checks) {
|
||||
const Id pointer_type{ctx.TypePointer(spv::StorageClass::Private, ctx.U32[1])};
|
||||
const Id safety_counter{ctx.AddGlobalVariable(
|
||||
pointer_type, spv::StorageClass::Private, ctx.Const(0x2000u))};
|
||||
if (ctx.profile.supported_spirv >= 0x00010400) {
|
||||
ctx.interfaces.push_back(safety_counter);
|
||||
}
|
||||
const Id old_counter{ctx.OpLoad(ctx.U32[1], safety_counter)};
|
||||
const Id new_counter{ctx.OpISub(ctx.U32[1], old_counter, ctx.Const(1u))};
|
||||
ctx.OpStore(safety_counter, new_counter);
|
||||
|
||||
const Id safety_cond{
|
||||
ctx.OpSGreaterThanEqual(ctx.U1, new_counter, ctx.u32_zero_value)};
|
||||
cond = ctx.OpLogicalAnd(ctx.U1, cond, safety_cond);
|
||||
}
|
||||
const Id loop_header_label{node.data.repeat.loop_header->Definition<Id>()};
|
||||
const Id merge_label{node.data.repeat.merge->Definition<Id>()};
|
||||
ctx.OpBranchConditional(cond, loop_header_label, merge_label);
|
||||
break;
|
||||
}
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
ctx.OpReturn();
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.OpUnreachable();
|
||||
break;
|
||||
}
|
||||
if (node.type != IR::AbstractSyntaxNode::Type::Block) {
|
||||
current_block = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Id DefineMain(EmitContext& ctx, IR::Program& program) {
|
||||
const Id void_function{ctx.TypeFunction(ctx.void_id)};
|
||||
const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)};
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
block->SetDefinition(ctx.OpLabel());
|
||||
}
|
||||
Traverse(ctx, program);
|
||||
ctx.OpFunctionEnd();
|
||||
return main;
|
||||
}
|
||||
|
||||
spv::ExecutionMode ExecutionMode(TessPrimitive primitive) {
|
||||
switch (primitive) {
|
||||
case TessPrimitive::Isolines:
|
||||
return spv::ExecutionMode::Isolines;
|
||||
case TessPrimitive::Triangles:
|
||||
return spv::ExecutionMode::Triangles;
|
||||
case TessPrimitive::Quads:
|
||||
return spv::ExecutionMode::Quads;
|
||||
}
|
||||
throw InvalidArgument("Tessellation primitive {}", primitive);
|
||||
}
|
||||
|
||||
spv::ExecutionMode ExecutionMode(TessSpacing spacing) {
|
||||
switch (spacing) {
|
||||
case TessSpacing::Equal:
|
||||
return spv::ExecutionMode::SpacingEqual;
|
||||
case TessSpacing::FractionalOdd:
|
||||
return spv::ExecutionMode::SpacingFractionalOdd;
|
||||
case TessSpacing::FractionalEven:
|
||||
return spv::ExecutionMode::SpacingFractionalEven;
|
||||
}
|
||||
throw InvalidArgument("Tessellation spacing {}", spacing);
|
||||
}
|
||||
|
||||
void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
||||
const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
|
||||
spv::ExecutionModel execution_model{};
|
||||
switch (program.stage) {
|
||||
case Stage::Compute: {
|
||||
const std::array<u32, 3> workgroup_size{program.workgroup_size};
|
||||
execution_model = spv::ExecutionModel::GLCompute;
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0],
|
||||
workgroup_size[1], workgroup_size[2]);
|
||||
break;
|
||||
}
|
||||
case Stage::VertexB:
|
||||
execution_model = spv::ExecutionModel::Vertex;
|
||||
break;
|
||||
case Stage::TessellationControl:
|
||||
execution_model = spv::ExecutionModel::TessellationControl;
|
||||
ctx.AddCapability(spv::Capability::Tessellation);
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.invocations);
|
||||
break;
|
||||
case Stage::TessellationEval:
|
||||
execution_model = spv::ExecutionModel::TessellationEvaluation;
|
||||
ctx.AddCapability(spv::Capability::Tessellation);
|
||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive));
|
||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing));
|
||||
ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise
|
||||
? spv::ExecutionMode::VertexOrderCw
|
||||
: spv::ExecutionMode::VertexOrderCcw);
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
execution_model = spv::ExecutionModel::Geometry;
|
||||
ctx.AddCapability(spv::Capability::Geometry);
|
||||
ctx.AddCapability(spv::Capability::GeometryStreams);
|
||||
switch (ctx.runtime_info.input_topology) {
|
||||
case InputTopology::Points:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
|
||||
break;
|
||||
case InputTopology::Lines:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::InputLines);
|
||||
break;
|
||||
case InputTopology::LinesAdjacency:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::InputLinesAdjacency);
|
||||
break;
|
||||
case InputTopology::Triangles:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::Triangles);
|
||||
break;
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::InputTrianglesAdjacency);
|
||||
break;
|
||||
}
|
||||
switch (program.output_topology) {
|
||||
case OutputTopology::PointList:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OutputPoints);
|
||||
break;
|
||||
case OutputTopology::LineStrip:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OutputLineStrip);
|
||||
break;
|
||||
case OutputTopology::TriangleStrip:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip);
|
||||
break;
|
||||
}
|
||||
if (program.info.stores[IR::Attribute::PointSize]) {
|
||||
ctx.AddCapability(spv::Capability::GeometryPointSize);
|
||||
}
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices);
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations);
|
||||
if (program.is_geometry_passthrough) {
|
||||
if (ctx.profile.support_geometry_shader_passthrough) {
|
||||
ctx.AddExtension("SPV_NV_geometry_shader_passthrough");
|
||||
ctx.AddCapability(spv::Capability::GeometryShaderPassthroughNV);
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Geometry shader passthrough used with no support");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Stage::Fragment:
|
||||
execution_model = spv::ExecutionModel::Fragment;
|
||||
if (ctx.profile.lower_left_origin_mode) {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OriginLowerLeft);
|
||||
} else {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
|
||||
}
|
||||
if (program.info.stores_frag_depth) {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
|
||||
}
|
||||
if (ctx.runtime_info.force_early_z) {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Stage {}", program.stage);
|
||||
}
|
||||
ctx.AddEntryPoint(execution_model, main, "main", interfaces);
|
||||
}
|
||||
|
||||
void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
|
||||
Id main_func) {
|
||||
const Info& info{program.info};
|
||||
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
|
||||
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
|
||||
} else if (info.uses_fp32_denorms_flush) {
|
||||
if (profile.support_fp32_denorm_flush) {
|
||||
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
|
||||
} else {
|
||||
// Drivers will most likely flush denorms by default, no need to warn
|
||||
}
|
||||
} else if (info.uses_fp32_denorms_preserve) {
|
||||
if (profile.support_fp32_denorm_preserve) {
|
||||
ctx.AddCapability(spv::Capability::DenormPreserve);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 32U);
|
||||
} else {
|
||||
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm preserve used in shader without host support");
|
||||
}
|
||||
}
|
||||
if (!profile.support_separate_denorm_behavior || profile.has_broken_fp16_float_controls) {
|
||||
// No separate denorm behavior
|
||||
return;
|
||||
}
|
||||
if (info.uses_fp16_denorms_flush && info.uses_fp16_denorms_preserve) {
|
||||
LOG_DEBUG(Shader_SPIRV, "Fp16 denorm flush and preserve on the same shader");
|
||||
} else if (info.uses_fp16_denorms_flush) {
|
||||
if (profile.support_fp16_denorm_flush) {
|
||||
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 16U);
|
||||
} else {
|
||||
// Same as fp32, no need to warn as most drivers will flush by default
|
||||
}
|
||||
} else if (info.uses_fp16_denorms_preserve) {
|
||||
if (profile.support_fp16_denorm_preserve) {
|
||||
ctx.AddCapability(spv::Capability::DenormPreserve);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U);
|
||||
} else {
|
||||
LOG_DEBUG(Shader_SPIRV, "Fp16 denorm preserve used in shader without host support");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& program,
|
||||
EmitContext& ctx, Id main_func) {
|
||||
if (profile.has_broken_fp16_float_controls && program.info.uses_fp16) {
|
||||
return;
|
||||
}
|
||||
if (program.info.uses_fp16 && profile.support_fp16_signed_zero_nan_preserve) {
|
||||
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 16U);
|
||||
}
|
||||
if (profile.support_fp32_signed_zero_nan_preserve) {
|
||||
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U);
|
||||
}
|
||||
if (program.info.uses_fp64 && profile.support_fp64_signed_zero_nan_preserve) {
|
||||
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 64U);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
|
||||
if (info.uses_sampled_1d) {
|
||||
ctx.AddCapability(spv::Capability::Sampled1D);
|
||||
}
|
||||
if (info.uses_sparse_residency) {
|
||||
ctx.AddCapability(spv::Capability::SparseResidency);
|
||||
}
|
||||
if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
|
||||
ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
|
||||
ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
|
||||
}
|
||||
if (info.stores[IR::Attribute::ViewportIndex]) {
|
||||
ctx.AddCapability(spv::Capability::MultiViewport);
|
||||
}
|
||||
if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
|
||||
ctx.AddExtension("SPV_NV_viewport_array2");
|
||||
ctx.AddCapability(spv::Capability::ShaderViewportMaskNV);
|
||||
}
|
||||
if (info.stores[IR::Attribute::Layer] || info.stores[IR::Attribute::ViewportIndex]) {
|
||||
if (profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
|
||||
ctx.AddExtension("SPV_EXT_shader_viewport_index_layer");
|
||||
ctx.AddCapability(spv::Capability::ShaderViewportIndexLayerEXT);
|
||||
}
|
||||
}
|
||||
if (!profile.support_vertex_instance_id &&
|
||||
(info.loads[IR::Attribute::InstanceId] || info.loads[IR::Attribute::VertexId])) {
|
||||
ctx.AddExtension("SPV_KHR_shader_draw_parameters");
|
||||
ctx.AddCapability(spv::Capability::DrawParameters);
|
||||
}
|
||||
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
|
||||
info.uses_subgroup_shuffles) &&
|
||||
profile.support_vote) {
|
||||
ctx.AddExtension("SPV_KHR_shader_ballot");
|
||||
ctx.AddCapability(spv::Capability::SubgroupBallotKHR);
|
||||
if (!profile.warp_size_potentially_larger_than_guest) {
|
||||
// vote ops are only used when not taking the long path
|
||||
ctx.AddExtension("SPV_KHR_subgroup_vote");
|
||||
ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
|
||||
}
|
||||
}
|
||||
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
|
||||
ctx.AddCapability(spv::Capability::Int64Atomics);
|
||||
}
|
||||
if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
|
||||
ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat);
|
||||
}
|
||||
if (info.uses_typeless_image_writes) {
|
||||
ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat);
|
||||
}
|
||||
if (info.uses_image_buffers) {
|
||||
ctx.AddCapability(spv::Capability::ImageBuffer);
|
||||
}
|
||||
if (info.uses_sample_id) {
|
||||
ctx.AddCapability(spv::Capability::SampleRateShading);
|
||||
}
|
||||
if (!ctx.runtime_info.xfb_varyings.empty()) {
|
||||
ctx.AddCapability(spv::Capability::TransformFeedback);
|
||||
}
|
||||
if (info.uses_derivatives) {
|
||||
ctx.AddCapability(spv::Capability::DerivativeControl);
|
||||
}
|
||||
// TODO: Track this usage
|
||||
ctx.AddCapability(spv::Capability::ImageGatherExtended);
|
||||
ctx.AddCapability(spv::Capability::ImageQuery);
|
||||
ctx.AddCapability(spv::Capability::SampledBuffer);
|
||||
}
|
||||
|
||||
void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
|
||||
auto inst{program.blocks.front()->begin()};
|
||||
size_t block_index{0};
|
||||
ctx.PatchDeferredPhi([&](size_t phi_arg) {
|
||||
if (phi_arg == 0) {
|
||||
++inst;
|
||||
if (inst == program.blocks[block_index]->end() ||
|
||||
inst->GetOpcode() != IR::Opcode::Phi) {
|
||||
do {
|
||||
++block_index;
|
||||
inst = program.blocks[block_index]->begin();
|
||||
} while (inst->GetOpcode() != IR::Opcode::Phi);
|
||||
}
|
||||
}
|
||||
return ctx.Def(inst->Arg(phi_arg));
|
||||
});
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings) {
|
||||
EmitContext ctx{profile, runtime_info, program, bindings};
|
||||
const Id main{DefineMain(ctx, program)};
|
||||
DefineEntryPoint(program, ctx, main);
|
||||
if (profile.support_float_controls) {
|
||||
ctx.AddExtension("SPV_KHR_float_controls");
|
||||
SetupDenormControl(profile, program, ctx, main);
|
||||
SetupSignedNanCapabilities(profile, program, ctx, main);
|
||||
}
|
||||
SetupCapabilities(profile, program.info, ctx);
|
||||
PatchPhiNodes(program, ctx);
|
||||
return ctx.Assemble();
|
||||
}
|
||||
|
||||
Id EmitPhi(EmitContext& ctx, IR::Inst* inst) {
|
||||
const size_t num_args{inst->NumArgs()};
|
||||
boost::container::small_vector<Id, 32> blocks;
|
||||
blocks.reserve(num_args);
|
||||
for (size_t index = 0; index < num_args; ++index) {
|
||||
blocks.push_back(inst->PhiBlock(index)->Definition<Id>());
|
||||
}
|
||||
// The type of a phi instruction is stored in its flags
|
||||
const Id result_type{TypeId(ctx, inst->Flags<IR::Type>())};
|
||||
return ctx.DeferredOpPhi(result_type, std::span(blocks.data(), blocks.size()));
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
Id EmitIdentity(EmitContext& ctx, const IR::Value& value) {
|
||||
const Id id{ctx.Def(value)};
|
||||
if (!Sirit::ValidId(id)) {
|
||||
throw NotImplementedException("Forward identity declaration");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
Id EmitConditionRef(EmitContext& ctx, const IR::Value& value) {
|
||||
const Id id{ctx.Def(value)};
|
||||
if (!Sirit::ValidId(id)) {
|
||||
throw NotImplementedException("Forward identity declaration");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void EmitReference(EmitContext&) {}
|
||||
|
||||
void EmitPhiMove(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
27
src/shader_recompiler/backend/spirv/emit_spirv.h
Executable file
27
src/shader_recompiler/backend/spirv/emit_spirv.h
Executable file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitSPIRV(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
448
src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
Executable file
448
src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
Executable file
@@ -0,0 +1,448 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
if (index_offset > 0) {
|
||||
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
|
||||
}
|
||||
return ctx.profile.support_explicit_workgroup_layout
|
||||
? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)
|
||||
: ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index);
|
||||
}
|
||||
|
||||
Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)};
|
||||
return ctx.Const(imm_offset);
|
||||
}
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
|
||||
const Id index{ctx.Def(offset)};
|
||||
if (shift == 0) {
|
||||
return index;
|
||||
}
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
|
||||
}
|
||||
|
||||
Id StoragePointer(EmitContext& ctx, const StorageTypeDefinition& type_def,
|
||||
Id StorageDefinitions::*member_ptr, const IR::Value& binding,
|
||||
const IR::Value& offset, size_t element_size) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Dynamic storage buffer indexing");
|
||||
}
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
|
||||
const Id index{StorageIndex(ctx, offset, element_size)};
|
||||
return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
||||
const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))};
|
||||
const Id semantics{ctx.u32_zero_value};
|
||||
return {scope, semantics};
|
||||
}
|
||||
|
||||
Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
const Id pointer{SharedPointer(ctx, offset)};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
}
|
||||
|
||||
Id StorageAtomicU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding,
|
||||
offset, sizeof(u32))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
}
|
||||
|
||||
Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id),
|
||||
Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
|
||||
if (ctx.profile.support_int64_atomics) {
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64,
|
||||
binding, offset, sizeof(u64))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value);
|
||||
}
|
||||
LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
||||
binding, offset, sizeof(u32[2]))};
|
||||
const Id original_value{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))};
|
||||
const Id result{(ctx.*non_atomic_func)(ctx.U64, value, original_value)};
|
||||
ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result));
|
||||
return original_value;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicIAdd);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMin);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMin);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMax);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMax);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset, Id value) {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_shared, index, value);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset, Id value) {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_shared, index, value);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicAnd);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicOr);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) {
|
||||
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicExchange);
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_int64_atomics && ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id shift_id{ctx.Const(3U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value);
|
||||
}
|
||||
LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
||||
const Id pointer_1{SharedPointer(ctx, offset, 0)};
|
||||
const Id pointer_2{SharedPointer(ctx, offset, 1)};
|
||||
const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)};
|
||||
const Id value_2{ctx.OpLoad(ctx.U32[1], pointer_2)};
|
||||
const Id new_vector{ctx.OpBitcast(ctx.U32[2], value)};
|
||||
ctx.OpStore(pointer_1, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 0U));
|
||||
ctx.OpStore(pointer_2, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 1U));
|
||||
return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2));
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMin);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicUMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMin);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicSMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMax);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMax);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_ssbo, base_index, value, ssbo);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_ssbo, base_index, value, ssbo);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicAnd);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicOr32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicOr);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicXor32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicXor);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicExchange32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicExchange);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicIAdd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd,
|
||||
&Sirit::Module::OpIAdd);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicSMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMin,
|
||||
&Sirit::Module::OpSMin);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicUMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMin,
|
||||
&Sirit::Module::OpUMin);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicSMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMax,
|
||||
&Sirit::Module::OpSMax);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicUMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMax,
|
||||
&Sirit::Module::OpUMax);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAnd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicAnd,
|
||||
&Sirit::Module::OpBitwiseAnd);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicOr64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicOr,
|
||||
&Sirit::Module::OpBitwiseOr);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicXor,
|
||||
&Sirit::Module::OpBitwiseXor);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_int64_atomics) {
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64,
|
||||
binding, offset, sizeof(u64))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value);
|
||||
}
|
||||
LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
||||
binding, offset, sizeof(u32[2]))};
|
||||
const Id original{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))};
|
||||
ctx.OpStore(pointer, value);
|
||||
return original;
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.F32[1], ctx.f32_add_cas, base_index, value, ssbo);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_add_cas, base_index, value, ssbo)};
|
||||
return ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_add_cas, base_index, value, ssbo)};
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_min_cas, base_index, value, ssbo)};
|
||||
return ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_min_cas, base_index, value, ssbo)};
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_max_cas, base_index, value, ssbo)};
|
||||
return ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_max_cas, base_index, value, ssbo)};
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicIAdd64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicSMin64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicUMin64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicSMax64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicUMax64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicInc64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicDec64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicAnd64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicOr64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicXor64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicExchange64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicAddF32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicAddF16x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicAddF32x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicMinF16x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicMinF32x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicMaxF16x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitGlobalAtomicMaxF32x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
38
src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
Executable file
38
src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
Executable file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void MemoryBarrier(EmitContext& ctx, spv::Scope scope) {
|
||||
const auto semantics{
|
||||
spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
|
||||
spv::MemorySemanticsMask::WorkgroupMemory | spv::MemorySemanticsMask::AtomicCounterMemory |
|
||||
spv::MemorySemanticsMask::ImageMemory};
|
||||
ctx.OpMemoryBarrier(ctx.Const(static_cast<u32>(scope)), ctx.Const(static_cast<u32>(semantics)));
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
const auto execution{spv::Scope::Workgroup};
|
||||
const auto memory{spv::Scope::Workgroup};
|
||||
const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease |
|
||||
spv::MemorySemanticsMask::WorkgroupMemory};
|
||||
ctx.OpControlBarrier(ctx.Const(static_cast<u32>(execution)),
|
||||
ctx.Const(static_cast<u32>(memory)),
|
||||
ctx.Const(static_cast<u32>(memory_semantics)));
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
MemoryBarrier(ctx, spv::Scope::Workgroup);
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
MemoryBarrier(ctx, spv::Scope::Device);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
66
src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
Executable file
66
src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
Executable file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBitCastF32U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitPackUint2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitUnpackUint2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackFloat2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackFloat2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F16[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackHalf2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUnpackHalf2x16(ctx.F32[2], value);
|
||||
}
|
||||
|
||||
Id EmitPackDouble2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U32[2], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
155
src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
Executable file
155
src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
Executable file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.U32[4], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.F16[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F16[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F16[4], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[2], e1, e2);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[3], e1, e2, e3);
|
||||
}
|
||||
|
||||
Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) {
|
||||
return ctx.OpCompositeConstruct(ctx.F32[4], e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index) {
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F32[4], object, composite, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[2], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[3], object, composite, index);
|
||||
}
|
||||
|
||||
Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index) {
|
||||
return ctx.OpCompositeInsert(ctx.F64[4], object, composite, index);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
505
src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
Executable file
505
src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
Executable file
@@ -0,0 +1,505 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
struct AttrInfo {
|
||||
Id pointer;
|
||||
Id id;
|
||||
bool needs_cast;
|
||||
};
|
||||
|
||||
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
||||
const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
|
||||
switch (type) {
|
||||
case AttributeType::Float:
|
||||
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
||||
case AttributeType::UnsignedInt:
|
||||
return AttrInfo{ctx.input_u32, ctx.U32[1], true};
|
||||
case AttributeType::SignedInt:
|
||||
return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
|
||||
case AttributeType::Disabled:
|
||||
return std::nullopt;
|
||||
}
|
||||
throw InvalidArgument("Invalid attribute type {}", type);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
|
||||
switch (ctx.stage) {
|
||||
case Stage::TessellationControl:
|
||||
case Stage::TessellationEval:
|
||||
case Stage::Geometry:
|
||||
return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...);
|
||||
default:
|
||||
return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
|
||||
if (ctx.stage == Stage::TessellationControl) {
|
||||
const Id invocation_id{ctx.OpLoad(ctx.U32[1], ctx.invocation_id)};
|
||||
return ctx.OpAccessChain(result_type, base, invocation_id, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return ctx.OpAccessChain(result_type, base, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
struct OutAttr {
|
||||
OutAttr(Id pointer_) : pointer{pointer_} {}
|
||||
OutAttr(Id pointer_, Id type_) : pointer{pointer_}, type{type_} {}
|
||||
|
||||
Id pointer{};
|
||||
Id type{};
|
||||
};
|
||||
|
||||
std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const u32 element{IR::GenericAttributeElement(attr)};
|
||||
const GenericElementInfo& info{ctx.output_generics.at(index).at(element)};
|
||||
if (info.num_components == 1) {
|
||||
return info.id;
|
||||
} else {
|
||||
const u32 index_element{element - info.first_element};
|
||||
const Id index_id{ctx.Const(index_element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PointSize:
|
||||
return ctx.output_point_size;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW: {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
|
||||
}
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
case IR::Attribute::ClipDistance3:
|
||||
case IR::Attribute::ClipDistance4:
|
||||
case IR::Attribute::ClipDistance5:
|
||||
case IR::Attribute::ClipDistance6:
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
const u32 index{static_cast<u32>(attr) - base};
|
||||
const Id clip_num{ctx.Const(index)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
|
||||
}
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.profile.support_viewport_index_layer_non_geometry ||
|
||||
ctx.stage == Shader::Stage::Geometry) {
|
||||
return OutAttr{ctx.layer, ctx.U32[1]};
|
||||
}
|
||||
return std::nullopt;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.profile.support_viewport_index_layer_non_geometry ||
|
||||
ctx.stage == Shader::Stage::Geometry) {
|
||||
return OutAttr{ctx.viewport_index, ctx.U32[1]};
|
||||
}
|
||||
return std::nullopt;
|
||||
case IR::Attribute::ViewportMask:
|
||||
if (!ctx.profile.support_viewport_mask) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return OutAttr{ctx.OpAccessChain(ctx.output_u32, ctx.viewport_mask, ctx.u32_zero_value),
|
||||
ctx.U32[1]};
|
||||
default:
|
||||
throw NotImplementedException("Read attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size,
|
||||
const IR::Value& binding, const IR::Value& offset) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Constant buffer indexing");
|
||||
}
|
||||
const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr};
|
||||
const Id uniform_type{ctx.uniform_types.*member_ptr};
|
||||
if (!offset.IsImmediate()) {
|
||||
Id index{ctx.Def(offset)};
|
||||
if (element_size > 1) {
|
||||
const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
|
||||
const Id shift{ctx.Const(log2_element_size)};
|
||||
index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
|
||||
}
|
||||
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)};
|
||||
return ctx.OpLoad(result_type, access_chain);
|
||||
}
|
||||
// Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
|
||||
const Id imm_offset{ctx.Const(offset.U32() / element_size)};
|
||||
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)};
|
||||
return ctx.OpLoad(result_type, access_chain);
|
||||
}
|
||||
|
||||
Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset);
|
||||
}
|
||||
|
||||
Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset);
|
||||
}
|
||||
|
||||
Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 element{(offset.U32() / 4) % 4 + index_offset};
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], vector, element);
|
||||
}
|
||||
const Id shift{ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), ctx.Const(2u))};
|
||||
Id element{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(3u))};
|
||||
if (index_offset > 0) {
|
||||
element = ctx.OpIAdd(ctx.U32[1], element, ctx.Const(index_offset));
|
||||
}
|
||||
return ctx.OpVectorExtractDynamic(ctx.U32[1], vector, element);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetRegister(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
|
||||
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)};
|
||||
return ctx.OpUConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
element = GetCbufU32(ctx, binding, offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
element = GetCbufElement(ctx, vector, offset, 0u);
|
||||
}
|
||||
const Id bit_offset{ctx.BitOffset8(offset)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], element, bit_offset, ctx.Const(8u));
|
||||
}
|
||||
|
||||
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
|
||||
const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)};
|
||||
return ctx.OpSConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
element = GetCbufU32(ctx, binding, offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
element = GetCbufElement(ctx, vector, offset, 0u);
|
||||
}
|
||||
const Id bit_offset{ctx.BitOffset8(offset)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], element, bit_offset, ctx.Const(8u));
|
||||
}
|
||||
|
||||
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
|
||||
const Id load{
|
||||
GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)};
|
||||
return ctx.OpUConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
element = GetCbufU32(ctx, binding, offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
element = GetCbufElement(ctx, vector, offset, 0u);
|
||||
}
|
||||
const Id bit_offset{ctx.BitOffset16(offset)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], element, bit_offset, ctx.Const(16u));
|
||||
}
|
||||
|
||||
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
|
||||
const Id load{
|
||||
GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)};
|
||||
return ctx.OpSConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
element = GetCbufU32(ctx, binding, offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
element = GetCbufElement(ctx, vector, offset, 0u);
|
||||
}
|
||||
const Id bit_offset{ctx.BitOffset16(offset)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], element, bit_offset, ctx.Const(16u));
|
||||
}
|
||||
|
||||
Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return GetCbufU32(ctx, binding, offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
return GetCbufElement(ctx, vector, offset, 0u);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding,
|
||||
offset);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u),
|
||||
GetCbufElement(ctx, vector, offset, 1u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
|
||||
if (!type) {
|
||||
// Attribute is disabled
|
||||
return ctx.Const(0.0f);
|
||||
}
|
||||
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
|
||||
// Varying component is not written
|
||||
return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
|
||||
}
|
||||
const Id generic_id{ctx.input_generics.at(index)};
|
||||
const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
|
||||
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
|
||||
ctx.Const(element)));
|
||||
case IR::Attribute::InstanceId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
|
||||
}
|
||||
case IR::Attribute::VertexId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_id));
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
|
||||
}
|
||||
case IR::Attribute::FrontFace:
|
||||
return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
|
||||
ctx.Const(std::numeric_limits<u32>::max()), ctx.u32_zero_value);
|
||||
case IR::Attribute::PointSpriteS:
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
|
||||
case IR::Attribute::PointSpriteT:
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.Const(1U)));
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value));
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U)));
|
||||
|
||||
default:
|
||||
throw NotImplementedException("Read attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
|
||||
const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
if (Sirit::ValidId(output->type)) {
|
||||
value = ctx.OpBitcast(output->type, value);
|
||||
}
|
||||
ctx.OpStore(output->pointer, value);
|
||||
}
|
||||
|
||||
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) {
|
||||
switch (ctx.stage) {
|
||||
case Stage::TessellationControl:
|
||||
case Stage::TessellationEval:
|
||||
case Stage::Geometry:
|
||||
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex);
|
||||
default:
|
||||
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unused]] Id vertex) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value);
|
||||
}
|
||||
|
||||
Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const Id element{ctx.Const(IR::GenericPatchElement(patch))};
|
||||
const Id type{ctx.stage == Stage::TessellationControl ? ctx.output_f32 : ctx.input_f32};
|
||||
const Id pointer{ctx.OpAccessChain(type, ctx.patches.at(index), element)};
|
||||
return ctx.OpLoad(ctx.F32[1], pointer);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
|
||||
const Id pointer{[&] {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const Id element{ctx.Const(IR::GenericPatchElement(patch))};
|
||||
return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element);
|
||||
}
|
||||
switch (patch) {
|
||||
case IR::Patch::TessellationLodLeft:
|
||||
case IR::Patch::TessellationLodRight:
|
||||
case IR::Patch::TessellationLodTop:
|
||||
case IR::Patch::TessellationLodBottom: {
|
||||
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
|
||||
const Id index_id{ctx.Const(index)};
|
||||
return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_outer, index_id);
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner,
|
||||
ctx.u32_zero_value);
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner, ctx.Const(1u));
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}()};
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
|
||||
const Id component_id{ctx.Const(component)};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, Id value) {
|
||||
ctx.OpStore(ctx.sample_mask, value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, Id value) {
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitWorkgroupId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[3], ctx.workgroup_id);
|
||||
}
|
||||
|
||||
Id EmitLocalInvocationId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id);
|
||||
}
|
||||
|
||||
Id EmitInvocationId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
|
||||
}
|
||||
|
||||
Id EmitSampleId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.sample_id);
|
||||
}
|
||||
|
||||
Id EmitIsHelperInvocation(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation);
|
||||
}
|
||||
|
||||
Id EmitYDirection(EmitContext& ctx) {
|
||||
return ctx.Const(ctx.runtime_info.y_negate ? -1.0f : 1.0f);
|
||||
}
|
||||
|
||||
Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
|
||||
const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value) {
|
||||
const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
28
src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
Executable file
28
src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
Executable file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
if (ctx.profile.support_demote_to_helper_invocation) {
|
||||
ctx.OpDemoteToHelperInvocationEXT();
|
||||
} else {
|
||||
const Id kill_label{ctx.OpLabel()};
|
||||
const Id impossible_label{ctx.OpLabel()};
|
||||
ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label);
|
||||
ctx.AddLabel(kill_label);
|
||||
ctx.OpKill();
|
||||
ctx.AddLabel(impossible_label);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
269
src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
Executable file
269
src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
Executable file
@@ -0,0 +1,269 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id ExtractU16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U16, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractS16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.S16, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractU8(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int8) {
|
||||
return ctx.OpUConvert(ctx.U8, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id ExtractS8(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int8) {
|
||||
return ctx.OpSConvert(ctx.S8, value);
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitConvertS16F16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS16F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS16F64(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertS32F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
return ctx.OpBitcast(ctx.U32[1], ctx.OpConvertFToS(ctx.S32[1], value));
|
||||
} else {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertS32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertS64F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToS(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU16F16(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU16F32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU16F64(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.support_int16) {
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value));
|
||||
} else {
|
||||
return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitConvertU32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertFToU(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU64U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32F64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64F32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFConvert(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16S32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF16U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF16U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32S32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
}
|
||||
return ctx.OpConvertSToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF32U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF32U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64S8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], ExtractS8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64S16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], ExtractS16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64S32(EmitContext& ctx, Id value) {
|
||||
if (ctx.profile.has_broken_signed_operations) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
}
|
||||
return ctx.OpConvertSToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64S64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertSToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64U8(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], ExtractU8(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], ExtractU16(ctx, value));
|
||||
}
|
||||
|
||||
Id EmitConvertF64U32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitConvertF64U64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpConvertUToF(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
396
src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
Executable file
396
src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
Executable file
@@ -0,0 +1,396 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
|
||||
const auto flags{inst->Flags<IR::FpControl>()};
|
||||
if (flags.no_contraction) {
|
||||
ctx.Decorate(op, spv::Decoration::NoContraction);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
Id Clamp(EmitContext& ctx, Id type, Id value, Id zero, Id one) {
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
return ctx.OpFMin(type, ctx.OpFMax(type, value, zero), one);
|
||||
} else {
|
||||
return ctx.OpFClamp(type, value, zero, one);
|
||||
}
|
||||
}
|
||||
|
||||
Id FPOrdNotEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
if (ctx.profile.ignore_nan_fp_comparisons) {
|
||||
const Id comp{ctx.OpFOrdEqual(ctx.U1, lhs, rhs)};
|
||||
const Id lhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, lhs))};
|
||||
const Id rhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, rhs))};
|
||||
return ctx.OpLogicalAnd(ctx.U1, ctx.OpLogicalAnd(ctx.U1, comp, lhs_not_nan), rhs_not_nan);
|
||||
} else {
|
||||
return ctx.OpFOrdNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
Id FPUnordCompare(Id (EmitContext::*comp_func)(Id, Id, Id), EmitContext& ctx, Id lhs, Id rhs) {
|
||||
if (ctx.profile.ignore_nan_fp_comparisons) {
|
||||
const Id lhs_nan{ctx.OpIsNan(ctx.U1, lhs)};
|
||||
const Id rhs_nan{ctx.OpIsNan(ctx.U1, rhs)};
|
||||
const Id comp{(ctx.*comp_func)(ctx.U1, lhs, rhs)};
|
||||
return ctx.OpLogicalOr(ctx.U1, ctx.OpLogicalOr(ctx.U1, comp, lhs_nan), rhs_nan);
|
||||
} else {
|
||||
return (ctx.*comp_func)(ctx.U1, lhs, rhs);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitFPAbs16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAbs32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAbs64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFAbs(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
|
||||
return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
|
||||
}
|
||||
|
||||
Id EmitFPMax32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMax(ctx.F32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMax64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMax(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMin(ctx.F32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpFMin(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
|
||||
}
|
||||
|
||||
Id EmitFPNeg16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPNeg32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPNeg64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFNegate(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSin(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSin(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCos(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCos(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPExp2(EmitContext& ctx, Id value) {
|
||||
return ctx.OpExp2(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPLog2(EmitContext& ctx, Id value) {
|
||||
return ctx.OpLog2(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRecip32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFDiv(ctx.F32[1], ctx.Const(1.0f), value);
|
||||
}
|
||||
|
||||
Id EmitFPRecip64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value);
|
||||
}
|
||||
|
||||
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpInverseSqrt(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRecipSqrt64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpInverseSqrt(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSqrt(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSqrt(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate16(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Constant(ctx.F16[1], u16{0})};
|
||||
const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})};
|
||||
return Clamp(ctx, ctx.F16[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate32(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Const(f32{0.0})};
|
||||
const Id one{ctx.Const(f32{1.0})};
|
||||
return Clamp(ctx, ctx.F32[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPSaturate64(EmitContext& ctx, Id value) {
|
||||
const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})};
|
||||
const Id one{ctx.Constant(ctx.F64[1], f64{1.0})};
|
||||
return Clamp(ctx, ctx.F64[1], value, zero, one);
|
||||
}
|
||||
|
||||
Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F16[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F32[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value) {
|
||||
return Clamp(ctx, ctx.F64[1], value, min_value, max_value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPRoundEven64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpRoundEven(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPFloor64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFloor(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPCeil64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpCeil(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F16[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPTrunc64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpTrunc(ctx.F64[1], value);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPOrdNotEqual(ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
Id EmitFPIsNan64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpIsNan(ctx.U1, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
457
src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
Executable file
457
src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
Executable file
@@ -0,0 +1,457 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
class ImageOperands {
|
||||
public:
|
||||
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
|
||||
Id lod, const IR::Value& offset) {
|
||||
if (has_bias) {
|
||||
const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
||||
Add(spv::ImageOperandsMask::Bias, bias);
|
||||
}
|
||||
if (has_lod) {
|
||||
const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
||||
Add(spv::ImageOperandsMask::Lod, lod_value);
|
||||
}
|
||||
AddOffset(ctx, offset);
|
||||
if (has_lod_clamp) {
|
||||
const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
|
||||
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
||||
}
|
||||
}
|
||||
|
||||
explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, const IR::Value& offset2) {
|
||||
if (offset2.IsEmpty()) {
|
||||
if (offset.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
|
||||
return;
|
||||
}
|
||||
const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
|
||||
if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) {
|
||||
LOG_WARNING(Shader_SPIRV, "Not all arguments in PTP are immediate, ignoring");
|
||||
return;
|
||||
}
|
||||
const IR::Opcode opcode{values[0]->GetOpcode()};
|
||||
if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) {
|
||||
throw LogicError("Invalid PTP arguments");
|
||||
}
|
||||
auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }};
|
||||
|
||||
const Id offsets{ctx.ConstantComposite(
|
||||
ctx.TypeArray(ctx.U32[2], ctx.Const(4U)), ctx.Const(read(0, 0), read(0, 1)),
|
||||
ctx.Const(read(0, 2), read(0, 3)), ctx.Const(read(1, 0), read(1, 1)),
|
||||
ctx.Const(read(1, 2), read(1, 3)))};
|
||||
Add(spv::ImageOperandsMask::ConstOffsets, offsets);
|
||||
}
|
||||
|
||||
explicit ImageOperands(Id offset, Id lod, Id ms) {
|
||||
if (Sirit::ValidId(lod)) {
|
||||
Add(spv::ImageOperandsMask::Lod, lod);
|
||||
}
|
||||
if (Sirit::ValidId(offset)) {
|
||||
Add(spv::ImageOperandsMask::Offset, offset);
|
||||
}
|
||||
if (Sirit::ValidId(ms)) {
|
||||
Add(spv::ImageOperandsMask::Sample, ms);
|
||||
}
|
||||
}
|
||||
|
||||
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates,
|
||||
Id offset, Id lod_clamp) {
|
||||
if (!Sirit::ValidId(derivates)) {
|
||||
throw LogicError("Derivates must be present");
|
||||
}
|
||||
boost::container::static_vector<Id, 3> deriv_x_accum;
|
||||
boost::container::static_vector<Id, 3> deriv_y_accum;
|
||||
for (u32 i = 0; i < num_derivates; ++i) {
|
||||
deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2));
|
||||
deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1));
|
||||
}
|
||||
const Id derivates_X{ctx.OpCompositeConstruct(
|
||||
ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
|
||||
const Id derivates_Y{ctx.OpCompositeConstruct(
|
||||
ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
|
||||
Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y);
|
||||
if (Sirit::ValidId(offset)) {
|
||||
Add(spv::ImageOperandsMask::Offset, offset);
|
||||
}
|
||||
if (has_lod_clamp) {
|
||||
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
||||
}
|
||||
}
|
||||
|
||||
std::span<const Id> Span() const noexcept {
|
||||
return std::span{operands.data(), operands.size()};
|
||||
}
|
||||
|
||||
std::optional<spv::ImageOperandsMask> MaskOptional() const noexcept {
|
||||
return mask != spv::ImageOperandsMask{} ? std::make_optional(mask) : std::nullopt;
|
||||
}
|
||||
|
||||
spv::ImageOperandsMask Mask() const noexcept {
|
||||
return mask;
|
||||
}
|
||||
|
||||
private:
|
||||
void AddOffset(EmitContext& ctx, const IR::Value& offset) {
|
||||
if (offset.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (offset.IsImmediate()) {
|
||||
Add(spv::ImageOperandsMask::ConstOffset, ctx.SConst(offset.U32()));
|
||||
return;
|
||||
}
|
||||
IR::Inst* const inst{offset.InstRecursive()};
|
||||
if (inst->AreAllArgsImmediates()) {
|
||||
switch (inst->GetOpcode()) {
|
||||
case IR::Opcode::CompositeConstructU32x2:
|
||||
Add(spv::ImageOperandsMask::ConstOffset,
|
||||
ctx.SConst(inst->Arg(0).U32(), inst->Arg(1).U32()));
|
||||
return;
|
||||
case IR::Opcode::CompositeConstructU32x3:
|
||||
Add(spv::ImageOperandsMask::ConstOffset,
|
||||
ctx.SConst(inst->Arg(0).U32(), inst->Arg(1).U32(), inst->Arg(2).U32()));
|
||||
return;
|
||||
case IR::Opcode::CompositeConstructU32x4:
|
||||
Add(spv::ImageOperandsMask::ConstOffset,
|
||||
ctx.SConst(inst->Arg(0).U32(), inst->Arg(1).U32(), inst->Arg(2).U32(),
|
||||
inst->Arg(3).U32()));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
|
||||
}
|
||||
|
||||
void Add(spv::ImageOperandsMask new_mask, Id value) {
|
||||
mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
|
||||
static_cast<unsigned>(new_mask));
|
||||
operands.push_back(value);
|
||||
}
|
||||
|
||||
void Add(spv::ImageOperandsMask new_mask, Id value_1, Id value_2) {
|
||||
mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
|
||||
static_cast<unsigned>(new_mask));
|
||||
operands.push_back(value_1);
|
||||
operands.push_back(value_2);
|
||||
}
|
||||
|
||||
boost::container::static_vector<Id, 4> operands;
|
||||
spv::ImageOperandsMask mask{};
|
||||
};
|
||||
|
||||
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
|
||||
return ctx.OpLoad(def.sampled_type, pointer);
|
||||
} else {
|
||||
return ctx.OpLoad(def.sampled_type, def.id);
|
||||
}
|
||||
}
|
||||
|
||||
Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
throw NotImplementedException("Indirect texture sample");
|
||||
}
|
||||
const Id sampler_id{def.id};
|
||||
const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)};
|
||||
return ctx.OpImage(ctx.image_buffer_type, id);
|
||||
} else {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
throw NotImplementedException("Indirect texture sample");
|
||||
}
|
||||
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
|
||||
}
|
||||
}
|
||||
|
||||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
|
||||
return ctx.OpLoad(def.image_type, def.id);
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
|
||||
return ctx.OpLoad(def.image_type, def.id);
|
||||
}
|
||||
}
|
||||
|
||||
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
if (info.relaxed_precision != 0) {
|
||||
ctx.Decorate(sample, spv::Decoration::RelaxedPrecision);
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
template <typename MethodPtrType, typename... Args>
|
||||
Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst,
|
||||
Id result_type, Args&&... args) {
|
||||
IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
|
||||
if (!sparse) {
|
||||
return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...));
|
||||
}
|
||||
const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
|
||||
const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
|
||||
const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
|
||||
sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
|
||||
sparse->Invalidate();
|
||||
Decorate(ctx, inst, sample);
|
||||
return ctx.OpCompositeExtract(result_type, sample, 1U);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageSampleExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageGather(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageGatherDref(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageFetch(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageQueryDimensions(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageQueryLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageGradient(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageRead(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageWrite(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageGather(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageGatherDref(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageFetch(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageQueryDimensions(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageQueryLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageGradient(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageRead(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageWrite(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id bias_lc, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
||||
bias_lc, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
} else {
|
||||
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
|
||||
// if the lod was explicitly zero. This may change on Turing with implicit compute
|
||||
// derivatives
|
||||
const Id lod{ctx.Const(0.0f)};
|
||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id lod, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc,
|
||||
offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
|
||||
&EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
|
||||
Texture(ctx, info, index), coords, dref, operands.MaskOptional(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id coords, Id dref, Id lod, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
|
||||
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
|
||||
Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, offset, offset2);
|
||||
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
||||
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
|
||||
operands.MaskOptional(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2, Id dref) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, offset, offset2);
|
||||
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
|
||||
ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
|
||||
operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
|
||||
Id lod, Id ms) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
if (info.type == TextureType::Buffer) {
|
||||
lod = Id{};
|
||||
}
|
||||
const ImageOperands operands(offset, lod, ms);
|
||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id image{TextureImage(ctx, info, index)};
|
||||
const Id zero{ctx.u32_zero_value};
|
||||
const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }};
|
||||
switch (info.type) {
|
||||
case TextureType::Color1D:
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod),
|
||||
zero, zero, mips());
|
||||
case TextureType::ColorArray1D:
|
||||
case TextureType::Color2D:
|
||||
case TextureType::ColorCube:
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod),
|
||||
zero, mips());
|
||||
case TextureType::ColorArray2D:
|
||||
case TextureType::Color3D:
|
||||
case TextureType::ColorArrayCube:
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod),
|
||||
mips());
|
||||
case TextureType::Buffer:
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero,
|
||||
zero, mips());
|
||||
}
|
||||
throw LogicError("Unspecified image type {}", info.type.Value());
|
||||
}
|
||||
|
||||
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id zero{ctx.f32_zero_value};
|
||||
const Id sampler{Texture(ctx, info, index)};
|
||||
return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
|
||||
zero, zero);
|
||||
}
|
||||
|
||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id derivates, Id offset, Id lod_clamp) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
|
||||
offset, lod_clamp);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) {
|
||||
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
|
||||
return ctx.ConstantNull(ctx.U32[4]);
|
||||
}
|
||||
return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
|
||||
Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
|
||||
}
|
||||
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
ctx.OpImageWrite(Image(ctx, index, info), coords, color);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
183
src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
Executable file
183
src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
Executable file
@@ -0,0 +1,183 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate()) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
|
||||
return def.id;
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(index.U32())};
|
||||
return def.id;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
||||
const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))};
|
||||
const Id semantics{ctx.u32_zero_value};
|
||||
return {scope, semantics};
|
||||
}
|
||||
|
||||
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id image{Image(ctx, index, info)};
|
||||
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
// TODO: This is not yet implemented
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
// TODO: This is not yet implemented
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange);
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
579
src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
Executable file
579
src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
Executable file
@@ -0,0 +1,579 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
enum class Attribute : u64;
|
||||
enum class Patch : u64;
|
||||
class Inst;
|
||||
class Value;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
using Sirit::Id;
|
||||
|
||||
class EmitContext;
|
||||
|
||||
// Microinstruction emitters
|
||||
Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
|
||||
void EmitVoid(EmitContext& ctx);
|
||||
Id EmitIdentity(EmitContext& ctx, const IR::Value& value);
|
||||
Id EmitConditionRef(EmitContext& ctx, const IR::Value& value);
|
||||
void EmitReference(EmitContext&);
|
||||
void EmitPhiMove(EmitContext&);
|
||||
void EmitJoin(EmitContext& ctx);
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx);
|
||||
void EmitBarrier(EmitContext& ctx);
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx);
|
||||
void EmitPrologue(EmitContext& ctx);
|
||||
void EmitEpilogue(EmitContext& ctx);
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream);
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
|
||||
void EmitGetRegister(EmitContext& ctx);
|
||||
void EmitSetRegister(EmitContext& ctx);
|
||||
void EmitGetPred(EmitContext& ctx);
|
||||
void EmitSetPred(EmitContext& ctx);
|
||||
void EmitSetGotoVariable(EmitContext& ctx);
|
||||
void EmitGetGotoVariable(EmitContext& ctx);
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx);
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx);
|
||||
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
|
||||
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
|
||||
Id EmitGetPatch(EmitContext& ctx, IR::Patch patch);
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value);
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value);
|
||||
void EmitSetSampleMask(EmitContext& ctx, Id value);
|
||||
void EmitSetFragDepth(EmitContext& ctx, Id value);
|
||||
void EmitGetZFlag(EmitContext& ctx);
|
||||
void EmitGetSFlag(EmitContext& ctx);
|
||||
void EmitGetCFlag(EmitContext& ctx);
|
||||
void EmitGetOFlag(EmitContext& ctx);
|
||||
void EmitSetZFlag(EmitContext& ctx);
|
||||
void EmitSetSFlag(EmitContext& ctx);
|
||||
void EmitSetCFlag(EmitContext& ctx);
|
||||
void EmitSetOFlag(EmitContext& ctx);
|
||||
Id EmitWorkgroupId(EmitContext& ctx);
|
||||
Id EmitLocalInvocationId(EmitContext& ctx);
|
||||
Id EmitInvocationId(EmitContext& ctx);
|
||||
Id EmitSampleId(EmitContext& ctx);
|
||||
Id EmitIsHelperInvocation(EmitContext& ctx);
|
||||
Id EmitYDirection(EmitContext& ctx);
|
||||
Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
|
||||
void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
|
||||
Id EmitUndefU1(EmitContext& ctx);
|
||||
Id EmitUndefU8(EmitContext& ctx);
|
||||
Id EmitUndefU16(EmitContext& ctx);
|
||||
Id EmitUndefU32(EmitContext& ctx);
|
||||
Id EmitUndefU64(EmitContext& ctx);
|
||||
void EmitLoadGlobalU8(EmitContext& ctx);
|
||||
void EmitLoadGlobalS8(EmitContext& ctx);
|
||||
void EmitLoadGlobalU16(EmitContext& ctx);
|
||||
void EmitLoadGlobalS16(EmitContext& ctx);
|
||||
Id EmitLoadGlobal32(EmitContext& ctx, Id address);
|
||||
Id EmitLoadGlobal64(EmitContext& ctx, Id address);
|
||||
Id EmitLoadGlobal128(EmitContext& ctx, Id address);
|
||||
void EmitWriteGlobalU8(EmitContext& ctx);
|
||||
void EmitWriteGlobalS8(EmitContext& ctx);
|
||||
void EmitWriteGlobalU16(EmitContext& ctx);
|
||||
void EmitWriteGlobalS16(EmitContext& ctx);
|
||||
void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value);
|
||||
void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value);
|
||||
void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value);
|
||||
Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitLoadSharedU8(EmitContext& ctx, Id offset);
|
||||
Id EmitLoadSharedS8(EmitContext& ctx, Id offset);
|
||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset);
|
||||
Id EmitLoadSharedS16(EmitContext& ctx, Id offset);
|
||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset);
|
||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset);
|
||||
Id EmitLoadSharedU128(EmitContext& ctx, Id offset);
|
||||
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value);
|
||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value);
|
||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value);
|
||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value);
|
||||
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value);
|
||||
Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2);
|
||||
Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3);
|
||||
Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
|
||||
Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2);
|
||||
Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3);
|
||||
Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
|
||||
Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2);
|
||||
Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3);
|
||||
Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4);
|
||||
Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index);
|
||||
Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
void EmitCompositeConstructF64x2(EmitContext& ctx);
|
||||
void EmitCompositeConstructF64x3(EmitContext& ctx);
|
||||
void EmitCompositeConstructF64x4(EmitContext& ctx);
|
||||
void EmitCompositeExtractF64x2(EmitContext& ctx);
|
||||
void EmitCompositeExtractF64x3(EmitContext& ctx);
|
||||
void EmitCompositeExtractF64x4(EmitContext& ctx);
|
||||
Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index);
|
||||
Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectU8(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
void EmitBitCastU16F16(EmitContext& ctx);
|
||||
Id EmitBitCastU32F32(EmitContext& ctx, Id value);
|
||||
void EmitBitCastU64F64(EmitContext& ctx);
|
||||
void EmitBitCastF16U16(EmitContext& ctx);
|
||||
Id EmitBitCastF32U32(EmitContext& ctx, Id value);
|
||||
void EmitBitCastF64U64(EmitContext& ctx);
|
||||
Id EmitPackUint2x32(EmitContext& ctx, Id value);
|
||||
Id EmitUnpackUint2x32(EmitContext& ctx, Id value);
|
||||
Id EmitPackFloat2x16(EmitContext& ctx, Id value);
|
||||
Id EmitUnpackFloat2x16(EmitContext& ctx, Id value);
|
||||
Id EmitPackHalf2x16(EmitContext& ctx, Id value);
|
||||
Id EmitUnpackHalf2x16(EmitContext& ctx, Id value);
|
||||
Id EmitPackDouble2x32(EmitContext& ctx, Id value);
|
||||
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value);
|
||||
void EmitGetZeroFromOp(EmitContext& ctx);
|
||||
void EmitGetSignFromOp(EmitContext& ctx);
|
||||
void EmitGetCarryFromOp(EmitContext& ctx);
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx);
|
||||
void EmitGetSparseFromOp(EmitContext& ctx);
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx);
|
||||
Id EmitFPAbs16(EmitContext& ctx, Id value);
|
||||
Id EmitFPAbs32(EmitContext& ctx, Id value);
|
||||
Id EmitFPAbs64(EmitContext& ctx, Id value);
|
||||
Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
|
||||
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
|
||||
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
|
||||
Id EmitFPMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitFPMax64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitFPMin32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitFPMin64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPNeg16(EmitContext& ctx, Id value);
|
||||
Id EmitFPNeg32(EmitContext& ctx, Id value);
|
||||
Id EmitFPNeg64(EmitContext& ctx, Id value);
|
||||
Id EmitFPSin(EmitContext& ctx, Id value);
|
||||
Id EmitFPCos(EmitContext& ctx, Id value);
|
||||
Id EmitFPExp2(EmitContext& ctx, Id value);
|
||||
Id EmitFPLog2(EmitContext& ctx, Id value);
|
||||
Id EmitFPRecip32(EmitContext& ctx, Id value);
|
||||
Id EmitFPRecip64(EmitContext& ctx, Id value);
|
||||
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value);
|
||||
Id EmitFPRecipSqrt64(EmitContext& ctx, Id value);
|
||||
Id EmitFPSqrt(EmitContext& ctx, Id value);
|
||||
Id EmitFPSaturate16(EmitContext& ctx, Id value);
|
||||
Id EmitFPSaturate32(EmitContext& ctx, Id value);
|
||||
Id EmitFPSaturate64(EmitContext& ctx, Id value);
|
||||
Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value);
|
||||
Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value);
|
||||
Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value);
|
||||
Id EmitFPRoundEven16(EmitContext& ctx, Id value);
|
||||
Id EmitFPRoundEven32(EmitContext& ctx, Id value);
|
||||
Id EmitFPRoundEven64(EmitContext& ctx, Id value);
|
||||
Id EmitFPFloor16(EmitContext& ctx, Id value);
|
||||
Id EmitFPFloor32(EmitContext& ctx, Id value);
|
||||
Id EmitFPFloor64(EmitContext& ctx, Id value);
|
||||
Id EmitFPCeil16(EmitContext& ctx, Id value);
|
||||
Id EmitFPCeil32(EmitContext& ctx, Id value);
|
||||
Id EmitFPCeil64(EmitContext& ctx, Id value);
|
||||
Id EmitFPTrunc16(EmitContext& ctx, Id value);
|
||||
Id EmitFPTrunc32(EmitContext& ctx, Id value);
|
||||
Id EmitFPTrunc64(EmitContext& ctx, Id value);
|
||||
Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitFPIsNan16(EmitContext& ctx, Id value);
|
||||
Id EmitFPIsNan32(EmitContext& ctx, Id value);
|
||||
Id EmitFPIsNan64(EmitContext& ctx, Id value);
|
||||
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitIAdd64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitISub32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitISub64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitIMul32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitINeg32(EmitContext& ctx, Id value);
|
||||
Id EmitINeg64(EmitContext& ctx, Id value);
|
||||
Id EmitIAbs32(EmitContext& ctx, Id value);
|
||||
Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count);
|
||||
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
|
||||
Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
|
||||
Id EmitBitReverse32(EmitContext& ctx, Id value);
|
||||
Id EmitBitCount32(EmitContext& ctx, Id value);
|
||||
Id EmitBitwiseNot32(EmitContext& ctx, Id value);
|
||||
Id EmitFindSMsb32(EmitContext& ctx, Id value);
|
||||
Id EmitFindUMsb32(EmitContext& ctx, Id value);
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUMin32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
|
||||
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
|
||||
Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicSMin32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicUMin32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicSMax32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicUMax32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicInc32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicDec32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicAnd32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicOr32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value);
|
||||
Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicUMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicSMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicOr32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicXor32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicExchange32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicIAdd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicSMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicUMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicSMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicUMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicAnd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicOr64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value);
|
||||
Id EmitGlobalAtomicIAdd32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicSMin32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicUMin32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicSMax32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicUMax32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicInc32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicDec32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicAnd32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicOr32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicXor32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicExchange32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicIAdd64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicSMin64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicUMin64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicSMax64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicUMax64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicInc64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicDec64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicAnd64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicOr64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicXor64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicExchange64(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicAddF32(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicAddF16x2(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicAddF32x2(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicMinF16x2(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicMinF32x2(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
|
||||
Id EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
|
||||
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitLogicalNot(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS16F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS16F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS16F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS32F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS32F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS32F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS64F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS64F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertS64F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU16F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU16F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU16F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU32F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU32F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU32F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU64F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU64F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU64F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU64U32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU32U64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32F16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32F64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64F32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16S8(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16S16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16S32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16S64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16U8(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16U16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16U32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF16U64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32S8(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32S16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32S32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32S64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32U8(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32U16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32U32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF32U64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64S8(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64S16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64S32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64S64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64U8(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64U16(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64U32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64U64(EmitContext& ctx, Id value);
|
||||
Id EmitBindlessImageSampleImplicitLod(EmitContext&);
|
||||
Id EmitBindlessImageSampleExplicitLod(EmitContext&);
|
||||
Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
|
||||
Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
|
||||
Id EmitBindlessImageGather(EmitContext&);
|
||||
Id EmitBindlessImageGatherDref(EmitContext&);
|
||||
Id EmitBindlessImageFetch(EmitContext&);
|
||||
Id EmitBindlessImageQueryDimensions(EmitContext&);
|
||||
Id EmitBindlessImageQueryLod(EmitContext&);
|
||||
Id EmitBindlessImageGradient(EmitContext&);
|
||||
Id EmitBindlessImageRead(EmitContext&);
|
||||
Id EmitBindlessImageWrite(EmitContext&);
|
||||
Id EmitBoundImageSampleImplicitLod(EmitContext&);
|
||||
Id EmitBoundImageSampleExplicitLod(EmitContext&);
|
||||
Id EmitBoundImageSampleDrefImplicitLod(EmitContext&);
|
||||
Id EmitBoundImageSampleDrefExplicitLod(EmitContext&);
|
||||
Id EmitBoundImageGather(EmitContext&);
|
||||
Id EmitBoundImageGatherDref(EmitContext&);
|
||||
Id EmitBoundImageFetch(EmitContext&);
|
||||
Id EmitBoundImageQueryDimensions(EmitContext&);
|
||||
Id EmitBoundImageQueryLod(EmitContext&);
|
||||
Id EmitBoundImageGradient(EmitContext&);
|
||||
Id EmitBoundImageRead(EmitContext&);
|
||||
Id EmitBoundImageWrite(EmitContext&);
|
||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id bias_lc, const IR::Value& offset);
|
||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id lod, const IR::Value& offset);
|
||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id coords, Id dref, Id bias_lc, const IR::Value& offset);
|
||||
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id coords, Id dref, Id lod, const IR::Value& offset);
|
||||
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2);
|
||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2, Id dref);
|
||||
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
|
||||
Id lod, Id ms);
|
||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod);
|
||||
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id derivates, Id offset, Id lod_clamp);
|
||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
|
||||
Id EmitBindlessImageAtomicIAdd32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicSMin32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicUMin32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicSMax32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicUMax32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicInc32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicDec32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicAnd32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicOr32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicXor32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicExchange32(EmitContext&);
|
||||
Id EmitBoundImageAtomicIAdd32(EmitContext&);
|
||||
Id EmitBoundImageAtomicSMin32(EmitContext&);
|
||||
Id EmitBoundImageAtomicUMin32(EmitContext&);
|
||||
Id EmitBoundImageAtomicSMax32(EmitContext&);
|
||||
Id EmitBoundImageAtomicUMax32(EmitContext&);
|
||||
Id EmitBoundImageAtomicInc32(EmitContext&);
|
||||
Id EmitBoundImageAtomicDec32(EmitContext&);
|
||||
Id EmitBoundImageAtomicAnd32(EmitContext&);
|
||||
Id EmitBoundImageAtomicOr32(EmitContext&);
|
||||
Id EmitBoundImageAtomicXor32(EmitContext&);
|
||||
Id EmitBoundImageAtomicExchange32(EmitContext&);
|
||||
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value);
|
||||
Id EmitLaneId(EmitContext& ctx);
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred);
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred);
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred);
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred);
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx);
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx);
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx);
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx);
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx);
|
||||
Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask);
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask);
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask);
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask);
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
|
||||
Id EmitDPdxFine(EmitContext& ctx, Id op_a);
|
||||
Id EmitDPdyFine(EmitContext& ctx, Id op_a);
|
||||
Id EmitDPdxCoarse(EmitContext& ctx, Id op_a);
|
||||
Id EmitDPdyCoarse(EmitContext& ctx, Id op_a);
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
270
src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
Executable file
270
src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
Executable file
@@ -0,0 +1,270 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
|
||||
IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
zero->SetDefinition(ctx.OpIEqual(ctx.U1, result, ctx.u32_zero_value));
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
|
||||
IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
sign->SetDefinition(ctx.OpSLessThan(ctx.U1, result, ctx.u32_zero_value));
|
||||
sign->Invalidate();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
Id result{};
|
||||
if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])};
|
||||
const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)};
|
||||
result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U);
|
||||
|
||||
const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)};
|
||||
carry->SetDefinition(ctx.OpINotEqual(ctx.U1, carry_value, ctx.u32_zero_value));
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
result = ctx.OpIAdd(ctx.U32[1], a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
|
||||
// https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
|
||||
constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
|
||||
const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1, a, ctx.u32_zero_value)};
|
||||
const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)};
|
||||
|
||||
const Id positive_test{ctx.OpSGreaterThan(ctx.U1, b, sub_a)};
|
||||
const Id negative_test{ctx.OpSLessThan(ctx.U1, b, sub_a)};
|
||||
const Id carry_flag{ctx.OpSelect(ctx.U1, is_positive, positive_test, negative_test)};
|
||||
overflow->SetDefinition(carry_flag);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIAdd(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitISub32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpISub(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitISub64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpISub(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIMul(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitINeg32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSNegate(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitINeg64(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSNegate(ctx.U64, value);
|
||||
}
|
||||
|
||||
Id EmitIAbs32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpSAbs(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftLeftLogical(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightLogical(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightLogical(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift);
|
||||
}
|
||||
|
||||
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) {
|
||||
return ctx.OpShiftRightArithmetic(ctx.U64, base, shift);
|
||||
}
|
||||
|
||||
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
|
||||
return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count);
|
||||
}
|
||||
|
||||
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
|
||||
const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
|
||||
const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitReverse32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitReverse(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitBitCount32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitCount(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitBitwiseNot32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpNot(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindSMsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindSMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindUMsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindUMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
|
||||
const bool is_broken{ctx.profile.has_broken_signed_operations};
|
||||
if (is_broken) {
|
||||
a = ctx.OpBitcast(ctx.S32[1], a);
|
||||
b = ctx.OpBitcast(ctx.S32[1], b);
|
||||
}
|
||||
const Id result{ctx.OpSMin(ctx.U32[1], a, b)};
|
||||
return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
|
||||
}
|
||||
|
||||
Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUMin(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
|
||||
const bool is_broken{ctx.profile.has_broken_signed_operations};
|
||||
if (is_broken) {
|
||||
a = ctx.OpBitcast(ctx.S32[1], a);
|
||||
b = ctx.OpBitcast(ctx.S32[1], b);
|
||||
}
|
||||
const Id result{ctx.OpSMax(ctx.U32[1], a, b)};
|
||||
return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
|
||||
}
|
||||
|
||||
Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpUMax(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) {
|
||||
value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
min = ctx.OpBitcast(ctx.S32[1], min);
|
||||
max = ctx.OpBitcast(ctx.S32[1], max);
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
|
||||
} else {
|
||||
result = ctx.OpSClamp(ctx.S32[1], value, min, max);
|
||||
}
|
||||
result = ctx.OpBitcast(ctx.U32[1], result);
|
||||
} else {
|
||||
result = ctx.OpSClamp(ctx.U32[1], value, min, max);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
|
||||
} else {
|
||||
result = ctx.OpUClamp(ctx.U32[1], value, min, max);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpIEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpUGreaterThan(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpINotEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
26
src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
Executable file
26
src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
Executable file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalOr(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalAnd(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpLogicalNotEqual(ctx.U1, a, b);
|
||||
}
|
||||
|
||||
Id EmitLogicalNot(EmitContext& ctx, Id value) {
|
||||
return ctx.OpLogicalNot(ctx.U1, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
275
src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
Executable file
275
src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
Executable file
@@ -0,0 +1,275 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size,
|
||||
u32 index_offset = 0) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 imm_offset{static_cast<u32>(offset.U32() / element_size) + index_offset};
|
||||
return ctx.Const(imm_offset);
|
||||
}
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
|
||||
Id index{ctx.Def(offset)};
|
||||
if (shift != 0) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
index = ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
|
||||
}
|
||||
if (index_offset != 0) {
|
||||
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
Id StoragePointer(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Dynamic storage buffer indexing");
|
||||
}
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
|
||||
const Id index{StorageIndex(ctx, offset, element_size, index_offset)};
|
||||
return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
Id LoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id result_type,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
return ctx.OpLoad(result_type, pointer);
|
||||
}
|
||||
|
||||
Id LoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
u32 index_offset = 0) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[1], ctx.storage_types.U32, sizeof(u32),
|
||||
&StorageDefinitions::U32, index_offset);
|
||||
}
|
||||
|
||||
void WriteStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
u32 index_offset = 0) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32),
|
||||
&StorageDefinitions::U32, index_offset);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u);
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u, 0u);
|
||||
}
|
||||
|
||||
Id EmitLoadGlobal128(EmitContext& ctx, Id address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address);
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
return ctx.Const(0u, 0u, 0u, 0u);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpUConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.U8, ctx.storage_types.U8,
|
||||
sizeof(u8), &StorageDefinitions::U8));
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset8(offset), ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpSConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.S8, ctx.storage_types.S8,
|
||||
sizeof(s8), &StorageDefinitions::S8));
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset8(offset), ctx.Const(8u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpUConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.U16, ctx.storage_types.U16,
|
||||
sizeof(u16), &StorageDefinitions::U16));
|
||||
} else {
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset16(offset), ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) {
|
||||
return ctx.OpSConvert(ctx.U32[1],
|
||||
LoadStorage(ctx, binding, offset, ctx.S16, ctx.storage_types.S16,
|
||||
sizeof(s16), &StorageDefinitions::S16));
|
||||
} else {
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset),
|
||||
ctx.BitOffset16(offset), ctx.Const(16u));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return LoadStorage32(ctx, binding, offset);
|
||||
}
|
||||
|
||||
Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[2], ctx.storage_types.U32x2,
|
||||
sizeof(u32[2]), &StorageDefinitions::U32x2);
|
||||
} else {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], LoadStorage32(ctx, binding, offset, 0),
|
||||
LoadStorage32(ctx, binding, offset, 1));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return LoadStorage(ctx, binding, offset, ctx.U32[4], ctx.storage_types.U32x4,
|
||||
sizeof(u32[4]), &StorageDefinitions::U32x4);
|
||||
} else {
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], LoadStorage32(ctx, binding, offset, 0),
|
||||
LoadStorage32(ctx, binding, offset, 1),
|
||||
LoadStorage32(ctx, binding, offset, 2),
|
||||
LoadStorage32(ctx, binding, offset, 3));
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8,
|
||||
sizeof(u8), &StorageDefinitions::U8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8,
|
||||
sizeof(s8), &StorageDefinitions::S8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16,
|
||||
sizeof(u16), &StorageDefinitions::U16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16,
|
||||
sizeof(s16), &StorageDefinitions::S16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
WriteStorage32(ctx, binding, offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x2, sizeof(u32[2]),
|
||||
&StorageDefinitions::U32x2);
|
||||
} else {
|
||||
for (u32 index = 0; index < 2; ++index) {
|
||||
const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
|
||||
WriteStorage32(ctx, binding, offset, element, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x4, sizeof(u32[4]),
|
||||
&StorageDefinitions::U32x4);
|
||||
} else {
|
||||
for (u32 index = 0; index < 4; ++index) {
|
||||
const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)};
|
||||
WriteStorage32(ctx, binding, offset, element, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
42
src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
Executable file
42
src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
Executable file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U1, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU8(EmitContext&, Id, Id, Id) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U16, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U32[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.U64, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F16[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F32[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value) {
|
||||
return ctx.OpSelect(ctx.F64[1], cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
174
src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
Executable file
174
src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
Executable file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Pointer(EmitContext& ctx, Id pointer_type, Id array, Id offset, u32 shift) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
return ctx.OpAccessChain(pointer_type, array, ctx.u32_zero_value, index);
|
||||
}
|
||||
|
||||
Id Word(EmitContext& ctx, Id offset) {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
|
||||
std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count) {
|
||||
const Id shift{ctx.OpShiftLeftLogical(ctx.U32[1], offset, ctx.Const(3U))};
|
||||
const Id bit{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(mask))};
|
||||
const Id count_id{ctx.Const(count)};
|
||||
return {bit, count_id};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
|
||||
return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)};
|
||||
return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
} else {
|
||||
return Word(ctx, offset);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
return ctx.OpLoad(ctx.U32[2], pointer);
|
||||
} else {
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id next_index{ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(1U))};
|
||||
const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)};
|
||||
const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)};
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer),
|
||||
ctx.OpLoad(ctx.U32[1], rhs_pointer));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
return ctx.OpLoad(ctx.U32[4], pointer);
|
||||
}
|
||||
const Id shift_id{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
std::array<Id, 4> values{};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
values[i] = ctx.OpLoad(ctx.U32[1], pointer);
|
||||
}
|
||||
return ctx.OpCompositeConstruct(ctx.U32[4], values);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value));
|
||||
} else {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u8_func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
|
||||
} else {
|
||||
ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u16_func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||
Id pointer{};
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
|
||||
} else {
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset);
|
||||
}
|
||||
ctx.OpStore(pointer, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
}
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
const Id next_offset{ctx.OpIAdd(ctx.U32[1], word_offset, ctx.Const(1U))};
|
||||
const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)};
|
||||
const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)};
|
||||
ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U));
|
||||
ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U));
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
}
|
||||
const Id shift{ctx.Const(2U)};
|
||||
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||
ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
150
src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
Executable file
150
src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
Executable file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
void ConvertDepthMode(EmitContext& ctx) {
|
||||
const Id type{ctx.F32[1]};
|
||||
const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)};
|
||||
const Id z{ctx.OpCompositeExtract(type, position, 2u)};
|
||||
const Id w{ctx.OpCompositeExtract(type, position, 3u)};
|
||||
const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))};
|
||||
const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)};
|
||||
ctx.OpStore(ctx.output_position, vector);
|
||||
}
|
||||
|
||||
void SetFixedPipelinePointSize(EmitContext& ctx) {
|
||||
if (ctx.runtime_info.fixed_state_point_size) {
|
||||
const float point_size{*ctx.runtime_info.fixed_state_point_size};
|
||||
ctx.OpStore(ctx.output_point_size, ctx.Const(point_size));
|
||||
}
|
||||
}
|
||||
|
||||
Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one,
|
||||
Id default_vector) {
|
||||
switch (num_components) {
|
||||
case 1:
|
||||
return element == 3 ? one : zero;
|
||||
case 2:
|
||||
return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero);
|
||||
case 3:
|
||||
return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero);
|
||||
case 4:
|
||||
return default_vector;
|
||||
}
|
||||
throw InvalidArgument("Bad element");
|
||||
}
|
||||
|
||||
Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1, Id operand_2) {
|
||||
switch (comparison) {
|
||||
case CompareFunction::Never:
|
||||
return ctx.false_value;
|
||||
case CompareFunction::Less:
|
||||
return ctx.OpFOrdLessThan(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Equal:
|
||||
return ctx.OpFOrdEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::LessThanEqual:
|
||||
return ctx.OpFOrdLessThanEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Greater:
|
||||
return ctx.OpFOrdGreaterThan(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::NotEqual:
|
||||
return ctx.OpFOrdNotEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::GreaterThanEqual:
|
||||
return ctx.OpFOrdGreaterThanEqual(ctx.U1, operand_1, operand_2);
|
||||
case CompareFunction::Always:
|
||||
return ctx.true_value;
|
||||
}
|
||||
throw InvalidArgument("Comparison function {}", comparison);
|
||||
}
|
||||
|
||||
void AlphaTest(EmitContext& ctx) {
|
||||
if (!ctx.runtime_info.alpha_test_func) {
|
||||
return;
|
||||
}
|
||||
const auto comparison{*ctx.runtime_info.alpha_test_func};
|
||||
if (comparison == CompareFunction::Always) {
|
||||
return;
|
||||
}
|
||||
if (!Sirit::ValidId(ctx.frag_color[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Id type{ctx.F32[1]};
|
||||
const Id rt0_color{ctx.OpLoad(ctx.F32[4], ctx.frag_color[0])};
|
||||
const Id alpha{ctx.OpCompositeExtract(type, rt0_color, 3u)};
|
||||
|
||||
const Id true_label{ctx.OpLabel()};
|
||||
const Id discard_label{ctx.OpLabel()};
|
||||
const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)};
|
||||
const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)};
|
||||
|
||||
ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
|
||||
ctx.OpBranchConditional(condition, true_label, discard_label);
|
||||
ctx.AddLabel(discard_label);
|
||||
ctx.OpKill();
|
||||
ctx.AddLabel(true_label);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB) {
|
||||
const Id zero{ctx.Const(0.0f)};
|
||||
const Id one{ctx.Const(1.0f)};
|
||||
const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)};
|
||||
ctx.OpStore(ctx.output_position, default_vector);
|
||||
for (const auto& info : ctx.output_generics) {
|
||||
if (info[0].num_components == 0) {
|
||||
continue;
|
||||
}
|
||||
u32 element{0};
|
||||
while (element < 4) {
|
||||
const auto& element_info{info[element]};
|
||||
const u32 num{element_info.num_components};
|
||||
const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)};
|
||||
ctx.OpStore(element_info.id, value);
|
||||
element += num;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
SetFixedPipelinePointSize(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
AlphaTest(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (stream.IsImmediate()) {
|
||||
ctx.OpEmitStreamVertex(ctx.Def(stream));
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
|
||||
ctx.OpEmitStreamVertex(ctx.u32_zero_value);
|
||||
}
|
||||
// Restore fixed pipeline point size after emitting the vertex
|
||||
SetFixedPipelinePointSize(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (stream.IsImmediate()) {
|
||||
ctx.OpEndStreamPrimitive(ctx.Def(stream));
|
||||
} else {
|
||||
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
|
||||
ctx.OpEndStreamPrimitive(ctx.u32_zero_value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
30
src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
Executable file
30
src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
Executable file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
Id EmitUndefU1(EmitContext& ctx) {
|
||||
return ctx.OpUndef(ctx.U1);
|
||||
}
|
||||
|
||||
Id EmitUndefU8(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitUndefU16(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
Id EmitUndefU32(EmitContext& ctx) {
|
||||
return ctx.OpUndef(ctx.U32[1]);
|
||||
}
|
||||
|
||||
Id EmitUndefU64(EmitContext&) {
|
||||
throw NotImplementedException("SPIR-V Instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
203
src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
Executable file
203
src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
Executable file
@@ -0,0 +1,203 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id WarpExtract(EmitContext& ctx, Id value) {
|
||||
const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
|
||||
}
|
||||
|
||||
Id LoadMask(EmitContext& ctx, Id mask) {
|
||||
const Id value{ctx.OpLoad(ctx.U32[4], mask)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], value, 0U);
|
||||
}
|
||||
return WarpExtract(ctx, value);
|
||||
}
|
||||
|
||||
void SetInBoundsFlag(IR::Inst* inst, Id result) {
|
||||
IR::Inst* const in_bounds{inst->GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
in_bounds->SetDefinition(result);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
Id ComputeMinThreadId(EmitContext& ctx, Id thread_id, Id segmentation_mask) {
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
Id ComputeMaxThreadId(EmitContext& ctx, Id min_thread_id, Id clamp, Id not_seg_mask) {
|
||||
return ctx.OpBitwiseOr(ctx.U32[1], min_thread_id,
|
||||
ctx.OpBitwiseAnd(ctx.U32[1], clamp, not_seg_mask));
|
||||
}
|
||||
|
||||
Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
return ctx.OpSelect(ctx.U32[1], in_range,
|
||||
ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
const Id id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return id;
|
||||
}
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], id, ctx.Const(31U));
|
||||
}
|
||||
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAllKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpIEqual(ctx.U1, lhs, active_mask);
|
||||
}
|
||||
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAnyKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
|
||||
}
|
||||
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred);
|
||||
}
|
||||
const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
|
||||
const Id active_mask{WarpExtract(ctx, mask_ballot)};
|
||||
const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
|
||||
const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
|
||||
return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
|
||||
ctx.OpIEqual(ctx.U1, lhs, active_mask));
|
||||
}
|
||||
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
|
||||
}
|
||||
return WarpExtract(ctx, ballot);
|
||||
}
|
||||
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_eq);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_lt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_le);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_gt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
return LoadMask(ctx, ctx.subgroup_mask_ge);
|
||||
}
|
||||
|
||||
Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
|
||||
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||
const Id three{ctx.Const(3U)};
|
||||
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
|
||||
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
|
||||
const Id modifier_a{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_a, mask)};
|
||||
const Id modifier_b{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_b, mask)};
|
||||
|
||||
const Id result_a{ctx.OpFMul(ctx.F32[1], op_a, modifier_a)};
|
||||
const Id result_b{ctx.OpFMul(ctx.F32[1], op_b, modifier_b)};
|
||||
return ctx.OpFAdd(ctx.F32[1], result_a, result_b);
|
||||
}
|
||||
|
||||
Id EmitDPdxFine(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdxFine(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdyFine(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdyFine(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdxCoarse(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdxCoarse(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
Id EmitDPdyCoarse(EmitContext& ctx, Id op_a) {
|
||||
return ctx.OpDPdyCoarse(ctx.F32[1], op_a);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
Reference in New Issue
Block a user