another try
This commit is contained in:
@@ -1,251 +1,251 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
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>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||
return ctx.var_alloc.Consume(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());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwriting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("}}");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("break;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("return;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("for(;;){{");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (Settings::values.disable_shader_loop_safety_checks) {
|
||||
ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
} else {
|
||||
ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
|
||||
ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsPreciseType(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::PrecF32:
|
||||
case GlslVarType::PrecF64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DefineVariables(const EmitContext& ctx, std::string& header) {
|
||||
for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
|
||||
const auto type{static_cast<GlslVarType>(i)};
|
||||
const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
|
||||
const auto type_name{ctx.var_alloc.GetGlslType(type)};
|
||||
const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
|
||||
const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
|
||||
// Temps/return types that are never used are stored at index 0
|
||||
if (tracker.uses_temp) {
|
||||
header += fmt::format("{}{} t{}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(0, type), type_name);
|
||||
}
|
||||
for (u32 index = 0; index < tracker.num_used; ++index) {
|
||||
header += fmt::format("{}{} {}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(index, type), type_name);
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
|
||||
header += fmt::format("int loop{}=0x2000;", i);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
|
||||
ctx.header.insert(0, version);
|
||||
if (program.shared_memory_size > 0) {
|
||||
const auto requested_size{program.shared_memory_size};
|
||||
const auto max_size{profile.gl_max_compute_smem_size};
|
||||
const bool needs_clamp{requested_size > max_size};
|
||||
if (needs_clamp) {
|
||||
LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
|
||||
requested_size, max_size);
|
||||
}
|
||||
const auto smem_size{needs_clamp ? max_size : requested_size};
|
||||
ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
|
||||
}
|
||||
ctx.header += "void main(){\n";
|
||||
if (program.local_memory_size > 0) {
|
||||
ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
|
||||
}
|
||||
DefineVariables(ctx, ctx.header);
|
||||
if (ctx.uses_cc_carry) {
|
||||
ctx.header += "uint carry;";
|
||||
}
|
||||
if (program.info.uses_subgroup_shuffles) {
|
||||
ctx.header += "bool shfl_in_bounds;";
|
||||
}
|
||||
ctx.code.insert(0, ctx.header);
|
||||
ctx.code += '}';
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
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>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||
return ctx.var_alloc.Consume(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());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwriting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("}}");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("break;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("return;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("for(;;){{");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (Settings::values.disable_shader_loop_safety_checks) {
|
||||
ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
} else {
|
||||
ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
|
||||
ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsPreciseType(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::PrecF32:
|
||||
case GlslVarType::PrecF64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DefineVariables(const EmitContext& ctx, std::string& header) {
|
||||
for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
|
||||
const auto type{static_cast<GlslVarType>(i)};
|
||||
const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
|
||||
const auto type_name{ctx.var_alloc.GetGlslType(type)};
|
||||
const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
|
||||
const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
|
||||
// Temps/return types that are never used are stored at index 0
|
||||
if (tracker.uses_temp) {
|
||||
header += fmt::format("{}{} t{}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(0, type), type_name);
|
||||
}
|
||||
for (u32 index = 0; index < tracker.num_used; ++index) {
|
||||
header += fmt::format("{}{} {}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(index, type), type_name);
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
|
||||
header += fmt::format("int loop{}=0x2000;", i);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
|
||||
ctx.header.insert(0, version);
|
||||
if (program.shared_memory_size > 0) {
|
||||
const auto requested_size{program.shared_memory_size};
|
||||
const auto max_size{profile.gl_max_compute_smem_size};
|
||||
const bool needs_clamp{requested_size > max_size};
|
||||
if (needs_clamp) {
|
||||
LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
|
||||
requested_size, max_size);
|
||||
}
|
||||
const auto smem_size{needs_clamp ? max_size : requested_size};
|
||||
ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
|
||||
}
|
||||
ctx.header += "void main(){\n";
|
||||
if (program.local_memory_size > 0) {
|
||||
ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
|
||||
}
|
||||
DefineVariables(ctx, ctx.header);
|
||||
if (ctx.uses_cc_carry) {
|
||||
ctx.header += "uint carry;";
|
||||
}
|
||||
if (program.info.uses_subgroup_shuffles) {
|
||||
ctx.header += "bool shfl_in_bounds;";
|
||||
}
|
||||
ctx.code.insert(0, ctx.header);
|
||||
ctx.code += '}';
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,23 +1,23 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLSL(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLSL(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("barrier();");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("groupMemoryBarrier();");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("memoryBarrier();");
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("barrier();");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("groupMemoryBarrier();");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("memoryBarrier();");
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,95 +1,95 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{*value.InstRecursive()};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real variable out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=ftou({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=utof({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=uint64BitsToDouble({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=packUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=packDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{*value.InstRecursive()};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real variable out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=ftou({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=utof({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=uint64BitsToDouble({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=packUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackUint2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=packHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=packDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32x2("{}=unpackDouble2x32({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,218 +1,218 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr std::string_view SWIZZLE{"xyzw"};
|
||||
void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
if (result == composite) {
|
||||
// The result is aliased with the composite
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
} else {
|
||||
ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddU32x3("{}=uvec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddU32x4("{}=uvec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3,
|
||||
[[maybe_unused]] std::string_view e4) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddF32x3("{}=vec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddF32x4("{}=vec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr std::string_view SWIZZLE{"xyzw"};
|
||||
void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
if (result == composite) {
|
||||
// The result is aliased with the composite
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
} else {
|
||||
ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddU32x3("{}=uvec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddU32x4("{}=uvec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view e1,
|
||||
[[maybe_unused]] std::string_view e2,
|
||||
[[maybe_unused]] std::string_view e3,
|
||||
[[maybe_unused]] std::string_view e4) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view composite,
|
||||
[[maybe_unused]] std::string_view object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2) {
|
||||
ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3) {
|
||||
ctx.AddF32x3("{}=vec3({},{},{});", inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
|
||||
std::string_view e2, std::string_view e3, std::string_view e4) {
|
||||
ctx.AddF32x4("{}=vec4({},{},{},{});", inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
u32 index) {
|
||||
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x2)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x3)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
|
||||
std::string_view object, u32 index) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
|
||||
CompositeInsert(ctx, ret, composite, object, index);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
|
||||
u32 index) {
|
||||
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,427 +1,427 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char SWIZZLE[]{"xyzw"};
|
||||
|
||||
u32 CbufIndex(u32 offset) {
|
||||
return (offset / 4) % 4;
|
||||
}
|
||||
|
||||
char OffsetSwizzle(u32 offset) {
|
||||
return SWIZZLE[CbufIndex(offset)];
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
|
||||
if (binding.IsImmediate()) {
|
||||
return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
|
||||
} else {
|
||||
const auto binding_var{ctx.var_alloc.Consume(binding)};
|
||||
return fmt::format("GetCbufIndirect({},{})", binding_var, index);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
|
||||
const IR::Value& offset, u32 num_bits, std::string_view cast = {},
|
||||
std::string_view bit_offset = {}) {
|
||||
const bool is_immediate{offset.IsImmediate()};
|
||||
const bool component_indexing_bug{!is_immediate && ctx.profile.has_gl_component_indexing_bug};
|
||||
if (is_immediate) {
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
if (signed_offset < 0 || offset.U32() > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.Add("{}=0u;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto index{is_immediate ? fmt::format("{}", offset.U32() / 16)
|
||||
: fmt::format("{}>>4", offset_var)};
|
||||
const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
|
||||
: fmt::format("[({}>>2)%4]", offset_var)};
|
||||
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, index)};
|
||||
const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
|
||||
const auto extraction{num_bits == 32 ? cbuf_cast
|
||||
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
|
||||
bit_offset, num_bits)};
|
||||
if (!component_indexing_bug) {
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
|
||||
ctx.Add("{}={};", ret, result);
|
||||
return;
|
||||
}
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const auto swizzle_string{fmt::format(".{}", "xyzw"[i])};
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle_string)};
|
||||
ctx.Add("if(({}&3)=={}){}={};", cbuf_offset, i, ret, result);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", (offset.U32() % 4) * 8)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("({}%4)*8", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", ((offset.U32() / 2) % 2) * 16)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("(({}>>1)%2)*16", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
if (signed_offset < 0 || u32_offset > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.AddU32x2("{}=uvec2(0u);", inst);
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
|
||||
offset_var, cast, cbuf, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
|
||||
cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
|
||||
if (element == 3) {
|
||||
ctx.AddF32("{}=1.f;", inst, attr);
|
||||
} else {
|
||||
ctx.AddF32("{}=0.f;", inst, attr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW: {
|
||||
const bool is_array{IsInputArray(ctx.stage)};
|
||||
const auto input_decorator{is_array ? fmt::format("gl_in[{}].", vertex) : ""};
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddF32("{}=itof(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddF32("{}=itof(gl_VertexID);", inst);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const u32 attr_element{IR::GenericAttributeElement(attr)};
|
||||
const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
if (info.num_components == 1) {
|
||||
ctx.Add("{}{}={};", info.name, output_decorator, value);
|
||||
} else {
|
||||
const u32 index_element{attr_element - info.first_element};
|
||||
ctx.Add("{}{}.{}={};", info.name, output_decorator, "xyzw"[index_element], value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport layer but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_Layer=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport index but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportIndex=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
if (ctx.stage != Stage::Geometry && !ctx.profile.support_viewport_mask) {
|
||||
LOG_WARNING(
|
||||
Shader_GLSL,
|
||||
"Shader stores viewport mask but device does not support viewport mask extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportMask[0]=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("gl_PointSize={};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
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 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("gl_ClipDistance[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
std::string_view vertex) {
|
||||
const bool is_array{ctx.stage == Stage::Geometry};
|
||||
const auto vertex_arg{is_array ? fmt::format(",{}", vertex) : ""};
|
||||
ctx.AddF32("{}=IndexedAttrLoad(int({}){});", inst, offset, vertex_arg);
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view offset,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
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)};
|
||||
ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("gl_TessLevelInner[0]={};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("gl_TessLevelInner[1]={};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
|
||||
const char swizzle{"xyzw"[component]};
|
||||
ctx.Add("frag_color{}.{}={};", index, swizzle, value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_SampleMask[0]=int({});", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_FragDepth={};", value);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_LocalInvocationID;", inst);
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_WorkGroupID;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_InvocationID);", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_SampleID);", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=gl_HelperInvocation;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst);
|
||||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddF32("{}=scaling.z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
|
||||
ctx.AddU32("{}=lmem[{}];", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value) {
|
||||
ctx.Add("lmem[{}]={};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char SWIZZLE[]{"xyzw"};
|
||||
|
||||
u32 CbufIndex(u32 offset) {
|
||||
return (offset / 4) % 4;
|
||||
}
|
||||
|
||||
char OffsetSwizzle(u32 offset) {
|
||||
return SWIZZLE[CbufIndex(offset)];
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
|
||||
if (binding.IsImmediate()) {
|
||||
return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
|
||||
} else {
|
||||
const auto binding_var{ctx.var_alloc.Consume(binding)};
|
||||
return fmt::format("GetCbufIndirect({},{})", binding_var, index);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
|
||||
const IR::Value& offset, u32 num_bits, std::string_view cast = {},
|
||||
std::string_view bit_offset = {}) {
|
||||
const bool is_immediate{offset.IsImmediate()};
|
||||
const bool component_indexing_bug{!is_immediate && ctx.profile.has_gl_component_indexing_bug};
|
||||
if (is_immediate) {
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
if (signed_offset < 0 || offset.U32() > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.Add("{}=0u;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto index{is_immediate ? fmt::format("{}", offset.U32() / 16)
|
||||
: fmt::format("{}>>4", offset_var)};
|
||||
const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
|
||||
: fmt::format("[({}>>2)%4]", offset_var)};
|
||||
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, index)};
|
||||
const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
|
||||
const auto extraction{num_bits == 32 ? cbuf_cast
|
||||
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
|
||||
bit_offset, num_bits)};
|
||||
if (!component_indexing_bug) {
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
|
||||
ctx.Add("{}={};", ret, result);
|
||||
return;
|
||||
}
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
const auto swizzle_string{fmt::format(".{}", "xyzw"[i])};
|
||||
const auto result{fmt::format(fmt::runtime(extraction), swizzle_string)};
|
||||
ctx.Add("if(({}&3)=={}){}={};", cbuf_offset, i, ret, result);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", (offset.U32() % 4) * 8)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("({}%4)*8", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view cast) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto bit_offset{fmt::format("{}", ((offset.U32() / 2) % 2) * 16)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
} else {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("(({}>>1)%2)*16", offset_var)};
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
const s32 signed_offset{static_cast<s32>(offset.U32())};
|
||||
if (signed_offset < 0 || u32_offset > cbuf_size) {
|
||||
LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
|
||||
ctx.AddU32x2("{}=uvec2(0u);", inst);
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
|
||||
offset_var, cast, cbuf, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
|
||||
cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
|
||||
if (element == 3) {
|
||||
ctx.AddF32("{}=1.f;", inst, attr);
|
||||
} else {
|
||||
ctx.AddF32("{}=0.f;", inst, attr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW: {
|
||||
const bool is_array{IsInputArray(ctx.stage)};
|
||||
const auto input_decorator{is_array ? fmt::format("gl_in[{}].", vertex) : ""};
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddF32("{}=itof(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddF32("{}=itof(gl_VertexID);", inst);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const u32 attr_element{IR::GenericAttributeElement(attr)};
|
||||
const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
if (info.num_components == 1) {
|
||||
ctx.Add("{}{}={};", info.name, output_decorator, value);
|
||||
} else {
|
||||
const u32 index_element{attr_element - info.first_element};
|
||||
ctx.Add("{}{}.{}={};", info.name, output_decorator, "xyzw"[index_element], value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport layer but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_Layer=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
!ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
LOG_WARNING(Shader_GLSL, "Shader stores viewport index but device does not support "
|
||||
"viewport layer extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportIndex=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
if (ctx.stage != Stage::Geometry && !ctx.profile.support_viewport_mask) {
|
||||
LOG_WARNING(
|
||||
Shader_GLSL,
|
||||
"Shader stores viewport mask but device does not support viewport mask extension");
|
||||
break;
|
||||
}
|
||||
ctx.Add("gl_ViewportMask[0]=ftoi({});", value);
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("gl_PointSize={};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
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 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("gl_ClipDistance[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
std::string_view vertex) {
|
||||
const bool is_array{ctx.stage == Stage::Geometry};
|
||||
const auto vertex_arg{is_array ? fmt::format(",{}", vertex) : ""};
|
||||
ctx.AddF32("{}=IndexedAttrLoad(int({}){});", inst, offset, vertex_arg);
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view offset,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
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)};
|
||||
ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("gl_TessLevelInner[0]={};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("gl_TessLevelInner[1]={};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
|
||||
const char swizzle{"xyzw"[component]};
|
||||
ctx.Add("frag_color{}.{}={};", index, swizzle, value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_SampleMask[0]=int({});", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, std::string_view value) {
|
||||
ctx.Add("gl_FragDepth={};", value);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_LocalInvocationID;", inst);
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32x3("{}=gl_WorkGroupID;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_InvocationID);", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=uint(gl_SampleID);", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=gl_HelperInvocation;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst);
|
||||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddF32("{}=scaling.z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
|
||||
ctx.AddU32("{}=lmem[{}];", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value) {
|
||||
ctx.Add("lmem[{}]={};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,18 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("discard;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitJoin(EmitContext&) {
|
||||
throw NotImplementedException("Join shouldn't be emitted");
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("discard;");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,229 +1,229 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=(int({})&0xffff)|(bitfieldExtract(int({}),31,1)<<15);", inst, value, value);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({}&0xffff);", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=(int({})&0xffff)|(bitfieldExtract(int({}),31,1)<<15);", inst, value, value);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=int({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=int64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({}&0xffff);", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=float({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,455 +1,455 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs,
|
||||
std::string_view op, bool ordered) {
|
||||
const auto nan_op{ordered ? "&&!" : "||"};
|
||||
ctx.AddU1("{}={}{}{}"
|
||||
"{}isnan({}){}isnan({});",
|
||||
inst, lhs, op, rhs, nan_op, lhs, nan_op, rhs);
|
||||
}
|
||||
|
||||
bool IsPrecise(const IR::Inst& inst) {
|
||||
return inst.Flags<IR::FpControl>().no_contraction;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b,
|
||||
[[maybe_unused]] std::string_view c) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sin({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=cos({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=exp2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=log2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=(1.0f)/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=1.0/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
ctx.AddF32("{}=inversesqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view min_value,
|
||||
[[maybe_unused]] std::string_view max_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF32("{}=min(max({},float({})),float({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF64("{}=min(max({},double({})),double({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs,
|
||||
std::string_view op, bool ordered) {
|
||||
const auto nan_op{ordered ? "&&!" : "||"};
|
||||
ctx.AddU1("{}={}{}{}"
|
||||
"{}isnan({}){}isnan({});",
|
||||
inst, lhs, op, rhs, nan_op, lhs, nan_op, rhs);
|
||||
}
|
||||
|
||||
bool IsPrecise(const IR::Inst& inst) {
|
||||
return inst.Flags<IR::FpControl>().no_contraction;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=abs({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}+{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b,
|
||||
[[maybe_unused]] std::string_view c) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF32("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
std::string_view c) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
} else {
|
||||
ctx.AddF64("{}=fma({},{},{});", inst, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=max({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF32("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddF64("{}=min({},{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF32("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF32("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
if (IsPrecise(inst)) {
|
||||
ctx.AddPrecF64("{}={}*{};", inst, a, b);
|
||||
} else {
|
||||
ctx.AddF64("{}={}*{};", inst, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sin({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=cos({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=exp2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=log2({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=(1.0f)/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=1.0/{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
ctx.AddF32("{}=inversesqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=sqrt({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value,
|
||||
[[maybe_unused]] std::string_view min_value,
|
||||
[[maybe_unused]] std::string_view max_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF32("{}=min(max({},float({})),float({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view min_value, std::string_view max_value) {
|
||||
// GLSL's clamp does not produce desirable results
|
||||
ctx.AddF64("{}=min(max({},double({})),double({}));", inst, value, min_value, max_value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=roundEven({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=floor({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=ceil({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=trunc({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "==", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "!=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "<=", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] std::string_view lhs,
|
||||
[[maybe_unused]] std::string_view rhs) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, ">=", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] std::string_view value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=isnan({});", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,258 +1,258 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const zero{inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}={}==0;", *zero, result);
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const sign{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=int({})<0;", *sign, result);
|
||||
sign->Invalidate();
|
||||
}
|
||||
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
char lop) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}={}{}{};", result, a, lop, b);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
// Compute the overflow CC first as it requires the original operand values,
|
||||
// which may be overwritten by the result of the addition
|
||||
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 auto sub_a{fmt::format("{}u-{}", s32_max, a)};
|
||||
const auto positive_result{fmt::format("int({})>int({})", b, sub_a)};
|
||||
const auto negative_result{fmt::format("int({})<int({})", b, sub_a)};
|
||||
ctx.AddU1("{}=int({})>=0?{}:{};", *overflow, a, positive_result, negative_result);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
ctx.uses_cc_carry = true;
|
||||
ctx.Add("{}=uaddCarry({},{},carry);", result, a, b);
|
||||
ctx.AddU1("{}=carry!=0;", *carry);
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
ctx.Add("{}={}+{};", result, a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint({}*{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint(int({})/int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}/{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=abs(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}=int({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}=int64_t({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '&');
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '|');
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '^');
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view insert, std::string_view offset, std::string_view count) {
|
||||
ctx.AddU32("{}=bitfieldInsert({},{},int({}),int({}));", inst, base, insert, offset, count);
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitfieldReverse({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitCount({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=~{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(uint({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}=={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}!={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
void SetZeroFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const zero{inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
|
||||
if (!zero) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}={}==0;", *zero, result);
|
||||
zero->Invalidate();
|
||||
}
|
||||
|
||||
void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
|
||||
IR::Inst* const sign{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=int({})<0;", *sign, result);
|
||||
sign->Invalidate();
|
||||
}
|
||||
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
|
||||
char lop) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}={}{}{};", result, a, lop, b);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
// Compute the overflow CC first as it requires the original operand values,
|
||||
// which may be overwritten by the result of the addition
|
||||
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 auto sub_a{fmt::format("{}u-{}", s32_max, a)};
|
||||
const auto positive_result{fmt::format("int({})>int({})", b, sub_a)};
|
||||
const auto negative_result{fmt::format("int({})<int({})", b, sub_a)};
|
||||
ctx.AddU1("{}=int({})>=0?{}:{};", *overflow, a, positive_result, negative_result);
|
||||
overflow->Invalidate();
|
||||
}
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
|
||||
ctx.uses_cc_carry = true;
|
||||
ctx.Add("{}=uaddCarry({},{},carry);", result, a, b);
|
||||
ctx.AddU1("{}=carry!=0;", *carry);
|
||||
carry->Invalidate();
|
||||
} else {
|
||||
ctx.Add("{}={}+{};", result, a, b);
|
||||
}
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}+{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU64("{}={}-{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint({}*{});", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=uint(int({})/int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}={}/{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=abs(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}<<{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}={}>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU32("{}=int({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view shift) {
|
||||
ctx.AddU64("{}=int64_t({})>>{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '&');
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '|');
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, '^');
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view insert, std::string_view offset, std::string_view count) {
|
||||
ctx.AddU32("{}=bitfieldInsert({},{},int({}),int({}));", inst, base, insert, offset, count);
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
|
||||
std::string_view offset, std::string_view count) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitfieldReverse({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=bitCount({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=~{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=findMSB(uint({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=min(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(int({}),int({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU32("{}=max(uint({}),uint({}));", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
|
||||
std::string_view max) {
|
||||
const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max);
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}=={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})<=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})<=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>uint({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
|
||||
ctx.AddU1("{}={}!={};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=int({})>=int({});", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
|
||||
std::string_view rhs) {
|
||||
ctx.AddU1("{}=uint({})>=uint({});", inst, lhs, rhs);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,25 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}||{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}&&{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}^^{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=!{};", inst, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}||{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}&&{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
|
||||
ctx.AddU1("{}={}^^{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU1("{}=!{};", inst, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,201 +1,201 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
|
||||
std::string_view value, std::string_view bit_offset, u32 num_bits) {
|
||||
const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
|
||||
ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32("{}=LoadGlobal32({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x2("{}=LoadGlobal64({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x2("{}=uvec2(0);", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x4("{}=LoadGlobal128({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x4("{}=uvec4(0);", inst);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal32({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal64({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal128({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int(({}>>1)%2)*16,16);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int(({}>>1)%2)*16,16);", inst,
|
||||
ctx.stage_name, binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}={}_ssbo{}[{}>>2];", inst, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2]);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x4("{}=uvec4({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2],{}_ssbo{}[({}+8)>>2],{}_ssbo{}[({}"
|
||||
"+12)>>2]);",
|
||||
inst, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name, binding.U32(),
|
||||
offset_var, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name,
|
||||
binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={};", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+8)>>2]={}.z;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+12)>>2]={}.w;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
|
||||
std::string_view value, std::string_view bit_offset, u32 num_bits) {
|
||||
const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
|
||||
ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32("{}=LoadGlobal32({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x2("{}=LoadGlobal64({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x2("{}=uvec2(0);", inst);
|
||||
}
|
||||
|
||||
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.AddU32x4("{}=LoadGlobal128({});", inst, address);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
ctx.AddU32x4("{}=uvec4(0);", inst);
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal32({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal64({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value) {
|
||||
if (ctx.profile.support_int64) {
|
||||
return ctx.Add("WriteGlobal128({},{});", address, value);
|
||||
}
|
||||
LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
|
||||
}
|
||||
|
||||
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int({}%4)*8,8);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int(({}>>1)%2)*16,16);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int(({}>>1)%2)*16,16);", inst,
|
||||
ctx.stage_name, binding.U32(), offset_var, offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32("{}={}_ssbo{}[{}>>2];", inst, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2]);", inst, ctx.stage_name,
|
||||
binding.U32(), offset_var, ctx.stage_name, binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.AddU32x4("{}=uvec4({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2],{}_ssbo{}[({}+8)>>2],{}_ssbo{}[({}"
|
||||
"+12)>>2]);",
|
||||
inst, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name, binding.U32(),
|
||||
offset_var, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name,
|
||||
binding.U32(), offset_var);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
|
||||
SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={};", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
std::string_view value) {
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+8)>>2]={}.z;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
ctx.Add("{}_ssbo{}[({}+12)>>2]={}.w;", ctx.stage_name, binding.U32(), offset_var, value);
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,100 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,54 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU1("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU1("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddU64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
|
||||
[[maybe_unused]] std::string_view true_value,
|
||||
[[maybe_unused]] std::string_view false_value) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF32("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
|
||||
std::string_view true_value, std::string_view false_value) {
|
||||
ctx.AddF64("{}={}?{}:{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,76 +1,76 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
|
||||
std::string_view bit_offset, u32 num_bits) {
|
||||
const auto smem{fmt::format("smem[{}>>2]", offset)};
|
||||
ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=smem[{}>>2];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x4("{}=uvec4(smem[{}>>2],smem[({}+4)>>2],smem[({}+8)>>2],smem[({}+12)>>2]);", inst,
|
||||
offset, offset, offset, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={};", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
ctx.Add("smem[({}+8)>>2]={}.z;", offset, value);
|
||||
ctx.Add("smem[({}+12)>>2]={}.w;", offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
|
||||
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
|
||||
"if(cas_result==old_value){{break;}}}}"};
|
||||
|
||||
void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
|
||||
std::string_view bit_offset, u32 num_bits) {
|
||||
const auto smem{fmt::format("smem[{}>>2]", offset)};
|
||||
ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int({}%4)*8,8);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int(({}>>1)%2)*16,16);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32("{}=smem[{}>>2];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
|
||||
ctx.AddU32x4("{}=uvec4(smem[{}>>2],smem[({}+4)>>2],smem[({}+8)>>2],smem[({}+12)>>2]);", inst,
|
||||
offset, offset, offset, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int({}%4)*8", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 8);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset)};
|
||||
SharedWriteCas(ctx, offset, value, bit_offset, 16);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={};", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value) {
|
||||
ctx.Add("smem[{}>>2]={}.x;", offset, value);
|
||||
ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
|
||||
ctx.Add("smem[({}+8)>>2]={}.z;", offset, value);
|
||||
ctx.Add("smem[({}+12)>>2]={}.w;", offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,110 +1,110 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
void InitializeOutputVaryings(EmitContext& ctx) {
|
||||
if (ctx.uses_geometry_passthrough) {
|
||||
return;
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
ctx.Add("gl_Position=vec4(0,0,0,1);");
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!ctx.info.stores.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
const auto& info_array{ctx.output_generics.at(index)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
size_t element{};
|
||||
while (element < info_array.size()) {
|
||||
const auto& info{info_array.at(element)};
|
||||
const auto varying_name{fmt::format("{}{}", info.name, output_decorator)};
|
||||
switch (info.num_components) {
|
||||
case 1: {
|
||||
const char value{element == 3 ? '1' : '0'};
|
||||
ctx.Add("{}={}.f;", varying_name, value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3:
|
||||
if (element + info.num_components < 4) {
|
||||
ctx.Add("{}=vec{}(0);", varying_name, info.num_components);
|
||||
} else {
|
||||
// last element is the w component, must be initialized to 1
|
||||
const auto zeros{info.num_components == 3 ? "0,0," : "0,"};
|
||||
ctx.Add("{}=vec{}({}1);", varying_name, info.num_components, zeros);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
ctx.Add("{}=vec4(0,0,0,1);", varying_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
element += info.num_components;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.var_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.var_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{*phi_value.InstRecursive()};
|
||||
const auto phi_type{phi.Type()};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi_type);
|
||||
}
|
||||
const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})};
|
||||
const auto val_reg{ctx.var_alloc.Consume(value)};
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext&) {}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream));
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EndStreamPrimitive(int({}));", ctx.var_alloc.Consume(stream));
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string_view OutputVertexIndex(EmitContext& ctx) {
|
||||
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
|
||||
}
|
||||
|
||||
void InitializeOutputVaryings(EmitContext& ctx) {
|
||||
if (ctx.uses_geometry_passthrough) {
|
||||
return;
|
||||
}
|
||||
if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
|
||||
ctx.Add("gl_Position=vec4(0,0,0,1);");
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!ctx.info.stores.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
const auto& info_array{ctx.output_generics.at(index)};
|
||||
const auto output_decorator{OutputVertexIndex(ctx)};
|
||||
size_t element{};
|
||||
while (element < info_array.size()) {
|
||||
const auto& info{info_array.at(element)};
|
||||
const auto varying_name{fmt::format("{}{}", info.name, output_decorator)};
|
||||
switch (info.num_components) {
|
||||
case 1: {
|
||||
const char value{element == 3 ? '1' : '0'};
|
||||
ctx.Add("{}={}.f;", varying_name, value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3:
|
||||
if (element + info.num_components < 4) {
|
||||
ctx.Add("{}=vec{}(0);", varying_name, info.num_components);
|
||||
} else {
|
||||
// last element is the w component, must be initialized to 1
|
||||
const auto zeros{info.num_components == 3 ? "0,0," : "0,"};
|
||||
ctx.Add("{}=vec{}({}1);", varying_name, info.num_components, zeros);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
ctx.Add("{}=vec4(0,0,0,1);", varying_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
element += info.num_components;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.var_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.var_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{*phi_value.InstRecursive()};
|
||||
const auto phi_type{phi.Type()};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
ctx.var_alloc.PhiDefine(phi, phi_type);
|
||||
}
|
||||
const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})};
|
||||
const auto val_reg{ctx.var_alloc.Consume(value)};
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext&) {}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream));
|
||||
InitializeOutputVaryings(ctx);
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
ctx.Add("EndStreamPrimitive(int({}));", ctx.var_alloc.Consume(stream));
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,29 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=false;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU64("{}=0u;", inst);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU1("{}=false;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}=0u;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU64("{}=0u;", inst);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,242 +1,242 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
|
||||
|
||||
void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
|
||||
return fmt::format("({}&{})", thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
|
||||
std::string_view not_seg_mask) {
|
||||
return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
|
||||
std::string_view segmentation_mask) {
|
||||
const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
|
||||
std::string_view value, std::string_view index,
|
||||
[[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
|
||||
const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
|
||||
ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
}
|
||||
|
||||
std::string_view BallotIndex(EmitContext& ctx) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ".x";
|
||||
}
|
||||
return "[gl_SubGroupInvocationARB>>5]";
|
||||
}
|
||||
|
||||
std::string GetMask(EmitContext& ctx, std::string_view mask) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
return fmt::format("uint(uvec2({}){})", mask, ballot_index);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
const auto value{fmt::format("({}^{})", ballot, active_mask)};
|
||||
ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
|
||||
const auto max_thread_id{
|
||||
ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
|
||||
|
||||
const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
|
||||
std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||
std::string_view swizzle) {
|
||||
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
||||
const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
|
||||
const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
|
||||
ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
|
||||
|
||||
void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
return;
|
||||
}
|
||||
ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
|
||||
std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
|
||||
return fmt::format("({}&{})", thread_id, segmentation_mask);
|
||||
}
|
||||
|
||||
std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
|
||||
std::string_view not_seg_mask) {
|
||||
return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
|
||||
std::string_view segmentation_mask) {
|
||||
const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
|
||||
return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
|
||||
std::string_view value, std::string_view index,
|
||||
[[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
|
||||
const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
|
||||
ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
}
|
||||
|
||||
std::string_view BallotIndex(EmitContext& ctx) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ".x";
|
||||
}
|
||||
return "[gl_SubGroupInvocationARB>>5]";
|
||||
}
|
||||
|
||||
std::string GetMask(EmitContext& ctx, std::string_view mask) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
return fmt::format("uint(uvec2({}){})", mask, ballot_index);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
const auto value{fmt::format("({}^{})", ballot, active_mask)};
|
||||
ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
|
||||
const auto max_thread_id{
|
||||
ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
|
||||
|
||||
const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
|
||||
std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view seg_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||
std::string_view swizzle) {
|
||||
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
||||
const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
|
||||
const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
|
||||
ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyFine({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
|
||||
ctx.AddF32("{}=dFdx({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
|
||||
if (ctx.profile.support_gl_derivative_control) {
|
||||
ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
|
||||
ctx.AddF32("{}=dFdy({});", inst, op_a);
|
||||
}
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,174 +1,174 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
struct GenericElementInfo {
|
||||
std::string name;
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
struct TextureImageDefinition {
|
||||
u32 binding;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <GlslVarType type, typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
const auto var_def{var_alloc.AddDefine(inst, type)};
|
||||
if (var_def.empty()) {
|
||||
// skip assigment.
|
||||
code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
|
||||
} else {
|
||||
code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
|
||||
}
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U1>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F16x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string header;
|
||||
std::string code;
|
||||
VarAlloc var_alloc;
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view position_name = "gl_Position";
|
||||
|
||||
std::vector<TextureImageDefinition> texture_buffers;
|
||||
std::vector<TextureImageDefinition> image_buffers;
|
||||
std::vector<TextureImageDefinition> textures;
|
||||
std::vector<TextureImageDefinition> images;
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
|
||||
bool uses_y_direction{};
|
||||
bool uses_cc_carry{};
|
||||
bool uses_geometry_passthrough{};
|
||||
|
||||
private:
|
||||
void SetupExtensions();
|
||||
void DefineConstantBuffers(Bindings& bindings);
|
||||
void DefineConstantBufferIndirect();
|
||||
void DefineStorageBuffers(Bindings& bindings);
|
||||
void DefineGenericOutput(size_t index, u32 invocations);
|
||||
void DefineHelperFunctions();
|
||||
void DefineConstants();
|
||||
std::string DefineGlobalMemoryFunctions();
|
||||
void SetupImages(Bindings& bindings);
|
||||
void SetupTextures(Bindings& bindings);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
struct GenericElementInfo {
|
||||
std::string name;
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
struct TextureImageDefinition {
|
||||
u32 binding;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <GlslVarType type, typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
const auto var_def{var_alloc.AddDefine(inst, type)};
|
||||
if (var_def.empty()) {
|
||||
// skip assigment.
|
||||
code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
|
||||
} else {
|
||||
code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
|
||||
}
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U1>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F16x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string header;
|
||||
std::string code;
|
||||
VarAlloc var_alloc;
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view position_name = "gl_Position";
|
||||
|
||||
std::vector<TextureImageDefinition> texture_buffers;
|
||||
std::vector<TextureImageDefinition> image_buffers;
|
||||
std::vector<TextureImageDefinition> textures;
|
||||
std::vector<TextureImageDefinition> images;
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
|
||||
bool uses_y_direction{};
|
||||
bool uses_cc_carry{};
|
||||
bool uses_geometry_passthrough{};
|
||||
|
||||
private:
|
||||
void SetupExtensions();
|
||||
void DefineConstantBuffers(Bindings& bindings);
|
||||
void DefineConstantBufferIndirect();
|
||||
void DefineStorageBuffers(Bindings& bindings);
|
||||
void DefineGenericOutput(size_t index, u32 invocations);
|
||||
void DefineHelperFunctions();
|
||||
void DefineConstants();
|
||||
std::string DefineGlobalMemoryFunctions();
|
||||
void SetupImages(Bindings& bindings);
|
||||
void SetupTextures(Bindings& bindings);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,306 +1,306 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string TypePrefix(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "b_";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16x2_";
|
||||
case GlslVarType::U32:
|
||||
return "u_";
|
||||
case GlslVarType::F32:
|
||||
return "f_";
|
||||
case GlslVarType::U64:
|
||||
return "u64_";
|
||||
case GlslVarType::F64:
|
||||
return "d_";
|
||||
case GlslVarType::U32x2:
|
||||
return "u2_";
|
||||
case GlslVarType::F32x2:
|
||||
return "f2_";
|
||||
case GlslVarType::U32x3:
|
||||
return "u3_";
|
||||
case GlslVarType::F32x3:
|
||||
return "f3_";
|
||||
case GlslVarType::U32x4:
|
||||
return "u4_";
|
||||
case GlslVarType::F32x4:
|
||||
return "f4_";
|
||||
case GlslVarType::PrecF32:
|
||||
return "pf_";
|
||||
case GlslVarType::PrecF64:
|
||||
return "pd_";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatFloat(std::string_view value, IR::Type type) {
|
||||
// TODO: Confirm FP64 nan/inf
|
||||
if (type == IR::Type::F32) {
|
||||
if (value == "nan") {
|
||||
return "utof(0x7fc00000)";
|
||||
}
|
||||
if (value == "inf") {
|
||||
return "utof(0x7f800000)";
|
||||
}
|
||||
if (value == "-inf") {
|
||||
return "utof(0xff800000)";
|
||||
}
|
||||
}
|
||||
if (value.find_first_of('e') != std::string_view::npos) {
|
||||
// scientific notation
|
||||
const auto cast{type == IR::Type::F32 ? "float" : "double"};
|
||||
return fmt::format("{}({})", cast, value);
|
||||
}
|
||||
const bool needs_dot{value.find_first_of('.') == std::string_view::npos};
|
||||
const bool needs_suffix{!value.ends_with('f')};
|
||||
const auto suffix{type == IR::Type::F32 ? "f" : "lf"};
|
||||
return fmt::format("{}{}{}", value, needs_dot ? "." : "", needs_suffix ? suffix : "");
|
||||
}
|
||||
|
||||
std::string MakeImm(const IR::Value& value) {
|
||||
switch (value.Type()) {
|
||||
case IR::Type::U1:
|
||||
return fmt::format("{}", value.U1() ? "true" : "false");
|
||||
case IR::Type::U32:
|
||||
return fmt::format("{}u", value.U32());
|
||||
case IR::Type::F32:
|
||||
return FormatFloat(fmt::format("{}", value.F32()), IR::Type::F32);
|
||||
case IR::Type::U64:
|
||||
return fmt::format("{}ul", value.U64());
|
||||
case IR::Type::F64:
|
||||
return FormatFloat(fmt::format("{}", value.F64()), IR::Type::F64);
|
||||
case IR::Type::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string VarAlloc::Representation(u32 index, GlslVarType type) const {
|
||||
const auto prefix{TypePrefix(type)};
|
||||
return fmt::format("{}{}", prefix, index);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Representation(Id id) const {
|
||||
return Representation(id.index, id.type);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
Id id{};
|
||||
id.type.Assign(type);
|
||||
GetUseTracker(type).uses_temp = true;
|
||||
inst.SetDefinition<Id>(id);
|
||||
return 't' + Representation(inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) {
|
||||
return Define(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) {
|
||||
return AddDefine(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Consume(const IR::Value& value) {
|
||||
return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive());
|
||||
}
|
||||
|
||||
std::string VarAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
inst.DestructiveRemoveUsage();
|
||||
if (!inst.HasUses()) {
|
||||
Free(inst.Definition<Id>());
|
||||
}
|
||||
return Representation(inst.Definition<Id>());
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(IR::Type type) const {
|
||||
return GetGlslType(RegType(type));
|
||||
}
|
||||
|
||||
Id VarAlloc::Alloc(GlslVarType type) {
|
||||
auto& use_tracker{GetUseTracker(type)};
|
||||
const auto num_vars{use_tracker.var_use.size()};
|
||||
for (size_t var = 0; var < num_vars; ++var) {
|
||||
if (use_tracker.var_use[var]) {
|
||||
continue;
|
||||
}
|
||||
use_tracker.num_used = std::max(use_tracker.num_used, var + 1);
|
||||
use_tracker.var_use[var] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(var));
|
||||
return ret;
|
||||
}
|
||||
// Allocate a new variable
|
||||
use_tracker.var_use.push_back(true);
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(use_tracker.num_used));
|
||||
++use_tracker.num_used;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VarAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid variable");
|
||||
}
|
||||
auto& use_tracker{GetUseTracker(id.type)};
|
||||
use_tracker.var_use[id.index] = false;
|
||||
}
|
||||
|
||||
GlslVarType VarAlloc::RegType(IR::Type type) const {
|
||||
switch (type) {
|
||||
case IR::Type::U1:
|
||||
return GlslVarType::U1;
|
||||
case IR::Type::U32:
|
||||
return GlslVarType::U32;
|
||||
case IR::Type::F32:
|
||||
return GlslVarType::F32;
|
||||
case IR::Type::U64:
|
||||
return GlslVarType::U64;
|
||||
case IR::Type::F64:
|
||||
return GlslVarType::F64;
|
||||
default:
|
||||
throw NotImplementedException("IR type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "bool";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16vec2";
|
||||
case GlslVarType::U32:
|
||||
return "uint";
|
||||
case GlslVarType::F32:
|
||||
case GlslVarType::PrecF32:
|
||||
return "float";
|
||||
case GlslVarType::U64:
|
||||
return "uint64_t";
|
||||
case GlslVarType::F64:
|
||||
case GlslVarType::PrecF64:
|
||||
return "double";
|
||||
case GlslVarType::U32x2:
|
||||
return "uvec2";
|
||||
case GlslVarType::F32x2:
|
||||
return "vec2";
|
||||
case GlslVarType::U32x3:
|
||||
return "uvec3";
|
||||
case GlslVarType::F32x3:
|
||||
return "vec3";
|
||||
case GlslVarType::U32x4:
|
||||
return "uvec4";
|
||||
case GlslVarType::F32x4:
|
||||
return "vec4";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
const VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
std::string TypePrefix(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "b_";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16x2_";
|
||||
case GlslVarType::U32:
|
||||
return "u_";
|
||||
case GlslVarType::F32:
|
||||
return "f_";
|
||||
case GlslVarType::U64:
|
||||
return "u64_";
|
||||
case GlslVarType::F64:
|
||||
return "d_";
|
||||
case GlslVarType::U32x2:
|
||||
return "u2_";
|
||||
case GlslVarType::F32x2:
|
||||
return "f2_";
|
||||
case GlslVarType::U32x3:
|
||||
return "u3_";
|
||||
case GlslVarType::F32x3:
|
||||
return "f3_";
|
||||
case GlslVarType::U32x4:
|
||||
return "u4_";
|
||||
case GlslVarType::F32x4:
|
||||
return "f4_";
|
||||
case GlslVarType::PrecF32:
|
||||
return "pf_";
|
||||
case GlslVarType::PrecF64:
|
||||
return "pd_";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatFloat(std::string_view value, IR::Type type) {
|
||||
// TODO: Confirm FP64 nan/inf
|
||||
if (type == IR::Type::F32) {
|
||||
if (value == "nan") {
|
||||
return "utof(0x7fc00000)";
|
||||
}
|
||||
if (value == "inf") {
|
||||
return "utof(0x7f800000)";
|
||||
}
|
||||
if (value == "-inf") {
|
||||
return "utof(0xff800000)";
|
||||
}
|
||||
}
|
||||
if (value.find_first_of('e') != std::string_view::npos) {
|
||||
// scientific notation
|
||||
const auto cast{type == IR::Type::F32 ? "float" : "double"};
|
||||
return fmt::format("{}({})", cast, value);
|
||||
}
|
||||
const bool needs_dot{value.find_first_of('.') == std::string_view::npos};
|
||||
const bool needs_suffix{!value.ends_with('f')};
|
||||
const auto suffix{type == IR::Type::F32 ? "f" : "lf"};
|
||||
return fmt::format("{}{}{}", value, needs_dot ? "." : "", needs_suffix ? suffix : "");
|
||||
}
|
||||
|
||||
std::string MakeImm(const IR::Value& value) {
|
||||
switch (value.Type()) {
|
||||
case IR::Type::U1:
|
||||
return fmt::format("{}", value.U1() ? "true" : "false");
|
||||
case IR::Type::U32:
|
||||
return fmt::format("{}u", value.U32());
|
||||
case IR::Type::F32:
|
||||
return FormatFloat(fmt::format("{}", value.F32()), IR::Type::F32);
|
||||
case IR::Type::U64:
|
||||
return fmt::format("{}ul", value.U64());
|
||||
case IR::Type::F64:
|
||||
return FormatFloat(fmt::format("{}", value.F64()), IR::Type::F64);
|
||||
case IR::Type::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string VarAlloc::Representation(u32 index, GlslVarType type) const {
|
||||
const auto prefix{TypePrefix(type)};
|
||||
return fmt::format("{}{}", prefix, index);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Representation(Id id) const {
|
||||
return Representation(id.index, id.type);
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
Id id{};
|
||||
id.type.Assign(type);
|
||||
GetUseTracker(type).uses_temp = true;
|
||||
inst.SetDefinition<Id>(id);
|
||||
return 't' + Representation(inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) {
|
||||
return Define(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) {
|
||||
return AddDefine(inst, RegType(type));
|
||||
}
|
||||
|
||||
std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(type));
|
||||
return Representation(inst.Definition<Id>());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::Consume(const IR::Value& value) {
|
||||
return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive());
|
||||
}
|
||||
|
||||
std::string VarAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
inst.DestructiveRemoveUsage();
|
||||
if (!inst.HasUses()) {
|
||||
Free(inst.Definition<Id>());
|
||||
}
|
||||
return Representation(inst.Definition<Id>());
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(IR::Type type) const {
|
||||
return GetGlslType(RegType(type));
|
||||
}
|
||||
|
||||
Id VarAlloc::Alloc(GlslVarType type) {
|
||||
auto& use_tracker{GetUseTracker(type)};
|
||||
const auto num_vars{use_tracker.var_use.size()};
|
||||
for (size_t var = 0; var < num_vars; ++var) {
|
||||
if (use_tracker.var_use[var]) {
|
||||
continue;
|
||||
}
|
||||
use_tracker.num_used = std::max(use_tracker.num_used, var + 1);
|
||||
use_tracker.var_use[var] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(var));
|
||||
return ret;
|
||||
}
|
||||
// Allocate a new variable
|
||||
use_tracker.var_use.push_back(true);
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.type.Assign(type);
|
||||
ret.index.Assign(static_cast<u32>(use_tracker.num_used));
|
||||
++use_tracker.num_used;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VarAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid variable");
|
||||
}
|
||||
auto& use_tracker{GetUseTracker(id.type)};
|
||||
use_tracker.var_use[id.index] = false;
|
||||
}
|
||||
|
||||
GlslVarType VarAlloc::RegType(IR::Type type) const {
|
||||
switch (type) {
|
||||
case IR::Type::U1:
|
||||
return GlslVarType::U1;
|
||||
case IR::Type::U32:
|
||||
return GlslVarType::U32;
|
||||
case IR::Type::F32:
|
||||
return GlslVarType::F32;
|
||||
case IR::Type::U64:
|
||||
return GlslVarType::U64;
|
||||
case IR::Type::F64:
|
||||
return GlslVarType::F64;
|
||||
default:
|
||||
throw NotImplementedException("IR type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string VarAlloc::GetGlslType(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return "bool";
|
||||
case GlslVarType::F16x2:
|
||||
return "f16vec2";
|
||||
case GlslVarType::U32:
|
||||
return "uint";
|
||||
case GlslVarType::F32:
|
||||
case GlslVarType::PrecF32:
|
||||
return "float";
|
||||
case GlslVarType::U64:
|
||||
return "uint64_t";
|
||||
case GlslVarType::F64:
|
||||
case GlslVarType::PrecF64:
|
||||
return "double";
|
||||
case GlslVarType::U32x2:
|
||||
return "uvec2";
|
||||
case GlslVarType::F32x2:
|
||||
return "vec2";
|
||||
case GlslVarType::U32x3:
|
||||
return "uvec3";
|
||||
case GlslVarType::F32x3:
|
||||
return "vec3";
|
||||
case GlslVarType::U32x4:
|
||||
return "uvec4";
|
||||
case GlslVarType::F32x4:
|
||||
return "vec4";
|
||||
case GlslVarType::Void:
|
||||
return "";
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
const VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) const {
|
||||
switch (type) {
|
||||
case GlslVarType::U1:
|
||||
return var_bool;
|
||||
case GlslVarType::F16x2:
|
||||
return var_f16x2;
|
||||
case GlslVarType::U32:
|
||||
return var_u32;
|
||||
case GlslVarType::F32:
|
||||
return var_f32;
|
||||
case GlslVarType::U64:
|
||||
return var_u64;
|
||||
case GlslVarType::F64:
|
||||
return var_f64;
|
||||
case GlslVarType::U32x2:
|
||||
return var_u32x2;
|
||||
case GlslVarType::F32x2:
|
||||
return var_f32x2;
|
||||
case GlslVarType::U32x3:
|
||||
return var_u32x3;
|
||||
case GlslVarType::F32x3:
|
||||
return var_f32x3;
|
||||
case GlslVarType::U32x4:
|
||||
return var_u32x4;
|
||||
case GlslVarType::F32x4:
|
||||
return var_f32x4;
|
||||
case GlslVarType::PrecF32:
|
||||
return var_precf32;
|
||||
case GlslVarType::PrecF64:
|
||||
return var_precf64;
|
||||
default:
|
||||
throw NotImplementedException("Type {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
@@ -1,104 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
enum class Type;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
enum class GlslVarType : u32 {
|
||||
U1,
|
||||
F16x2,
|
||||
U32,
|
||||
F32,
|
||||
U64,
|
||||
F64,
|
||||
U32x2,
|
||||
F32x2,
|
||||
U32x3,
|
||||
F32x3,
|
||||
U32x4,
|
||||
F32x4,
|
||||
PrecF32,
|
||||
PrecF64,
|
||||
Void,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 4, GlslVarType> type;
|
||||
BitField<6, 26, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
class VarAlloc {
|
||||
public:
|
||||
struct UseTracker {
|
||||
bool uses_temp{};
|
||||
size_t num_used{};
|
||||
std::vector<bool> var_use;
|
||||
};
|
||||
|
||||
/// Used for explicit usages of variables, may revert to temporaries
|
||||
std::string Define(IR::Inst& inst, GlslVarType type);
|
||||
std::string Define(IR::Inst& inst, IR::Type type);
|
||||
|
||||
/// Used to assign variables used by the IR. May return a blank string if
|
||||
/// the instruction's result is unused in the IR.
|
||||
std::string AddDefine(IR::Inst& inst, GlslVarType type);
|
||||
std::string PhiDefine(IR::Inst& inst, IR::Type type);
|
||||
|
||||
std::string Consume(const IR::Value& value);
|
||||
std::string ConsumeInst(IR::Inst& inst);
|
||||
|
||||
std::string GetGlslType(GlslVarType type) const;
|
||||
std::string GetGlslType(IR::Type type) const;
|
||||
|
||||
const UseTracker& GetUseTracker(GlslVarType type) const;
|
||||
std::string Representation(u32 index, GlslVarType type) const;
|
||||
|
||||
private:
|
||||
GlslVarType RegType(IR::Type type) const;
|
||||
Id Alloc(GlslVarType type);
|
||||
void Free(Id id);
|
||||
UseTracker& GetUseTracker(GlslVarType type);
|
||||
std::string Representation(Id id) const;
|
||||
|
||||
UseTracker var_bool{};
|
||||
UseTracker var_f16x2{};
|
||||
UseTracker var_u32{};
|
||||
UseTracker var_u32x2{};
|
||||
UseTracker var_u32x3{};
|
||||
UseTracker var_u32x4{};
|
||||
UseTracker var_f32{};
|
||||
UseTracker var_f32x2{};
|
||||
UseTracker var_f32x3{};
|
||||
UseTracker var_f32x4{};
|
||||
UseTracker var_u64{};
|
||||
UseTracker var_f64{};
|
||||
UseTracker var_precf32{};
|
||||
UseTracker var_precf64{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
enum class Type;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
enum class GlslVarType : u32 {
|
||||
U1,
|
||||
F16x2,
|
||||
U32,
|
||||
F32,
|
||||
U64,
|
||||
F64,
|
||||
U32x2,
|
||||
F32x2,
|
||||
U32x3,
|
||||
F32x3,
|
||||
U32x4,
|
||||
F32x4,
|
||||
PrecF32,
|
||||
PrecF64,
|
||||
Void,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 4, GlslVarType> type;
|
||||
BitField<6, 26, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
class VarAlloc {
|
||||
public:
|
||||
struct UseTracker {
|
||||
bool uses_temp{};
|
||||
size_t num_used{};
|
||||
std::vector<bool> var_use;
|
||||
};
|
||||
|
||||
/// Used for explicit usages of variables, may revert to temporaries
|
||||
std::string Define(IR::Inst& inst, GlslVarType type);
|
||||
std::string Define(IR::Inst& inst, IR::Type type);
|
||||
|
||||
/// Used to assign variables used by the IR. May return a blank string if
|
||||
/// the instruction's result is unused in the IR.
|
||||
std::string AddDefine(IR::Inst& inst, GlslVarType type);
|
||||
std::string PhiDefine(IR::Inst& inst, IR::Type type);
|
||||
|
||||
std::string Consume(const IR::Value& value);
|
||||
std::string ConsumeInst(IR::Inst& inst);
|
||||
|
||||
std::string GetGlslType(GlslVarType type) const;
|
||||
std::string GetGlslType(IR::Type type) const;
|
||||
|
||||
const UseTracker& GetUseTracker(GlslVarType type) const;
|
||||
std::string Representation(u32 index, GlslVarType type) const;
|
||||
|
||||
private:
|
||||
GlslVarType RegType(IR::Type type) const;
|
||||
Id Alloc(GlslVarType type);
|
||||
void Free(Id id);
|
||||
UseTracker& GetUseTracker(GlslVarType type);
|
||||
std::string Representation(Id id) const;
|
||||
|
||||
UseTracker var_bool{};
|
||||
UseTracker var_f16x2{};
|
||||
UseTracker var_u32{};
|
||||
UseTracker var_u32x2{};
|
||||
UseTracker var_u32x3{};
|
||||
UseTracker var_u32x4{};
|
||||
UseTracker var_f32{};
|
||||
UseTracker var_f32x2{};
|
||||
UseTracker var_f32x3{};
|
||||
UseTracker var_f32x4{};
|
||||
UseTracker var_u64{};
|
||||
UseTracker var_f64{};
|
||||
UseTracker var_precf32{};
|
||||
UseTracker var_precf64{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
|
Reference in New Issue
Block a user