glsl: Rework variable allocator to allow for variable reuse
This commit is contained in:
		| @@ -48,8 +48,8 @@ add_library(shader_recompiler STATIC | |||||||
|     backend/glsl/emit_glsl_special.cpp |     backend/glsl/emit_glsl_special.cpp | ||||||
|     backend/glsl/emit_glsl_undefined.cpp |     backend/glsl/emit_glsl_undefined.cpp | ||||||
|     backend/glsl/emit_glsl_warp.cpp |     backend/glsl/emit_glsl_warp.cpp | ||||||
|     backend/glsl/reg_alloc.cpp |     backend/glsl/var_alloc.cpp | ||||||
|     backend/glsl/reg_alloc.h |     backend/glsl/var_alloc.h | ||||||
|     backend/spirv/emit_context.cpp |     backend/spirv/emit_context.cpp | ||||||
|     backend/spirv/emit_context.h |     backend/spirv/emit_context.h | ||||||
|     backend/spirv/emit_spirv.cpp |     backend/spirv/emit_spirv.cpp | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
|  |  | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
|  |  | ||||||
| #include "shader_recompiler/backend/glsl/reg_alloc.h" | #include "shader_recompiler/backend/glsl/var_alloc.h" | ||||||
| #include "shader_recompiler/stage.h" | #include "shader_recompiler/stage.h" | ||||||
|  |  | ||||||
| namespace Shader { | namespace Shader { | ||||||
| @@ -35,81 +35,81 @@ public: | |||||||
|     explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, |     explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, | ||||||
|                          const RuntimeInfo& runtime_info_); |                          const RuntimeInfo& runtime_info_); | ||||||
|  |  | ||||||
|     template <Type type, typename... Args> |     template <GlslVarType type, typename... Args> | ||||||
|     void Add(const char* format_str, IR::Inst& inst, Args&&... args) { |     void Add(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         code += fmt::format(format_str, reg_alloc.Define(inst, type), std::forward<Args>(args)...); |         code += fmt::format(format_str, var_alloc.Define(inst, type), std::forward<Args>(args)...); | ||||||
|         // TODO: Remove this |         // TODO: Remove this | ||||||
|         code += '\n'; |         code += '\n'; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::U1>(format_str, inst, args...); |         Add<GlslVarType::U1>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::F16x2>(format_str, inst, args...); |         Add<GlslVarType::F16x2>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::U32>(format_str, inst, args...); |         Add<GlslVarType::U32>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddS32(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddS32(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::S32>(format_str, inst, args...); |         Add<GlslVarType::S32>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::F32>(format_str, inst, args...); |         Add<GlslVarType::F32>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddS64(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddS64(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::S64>(format_str, inst, args...); |         Add<GlslVarType::S64>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::U64>(format_str, inst, args...); |         Add<GlslVarType::U64>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::F64>(format_str, inst, args...); |         Add<GlslVarType::F64>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::U32x2>(format_str, inst, args...); |         Add<GlslVarType::U32x2>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::F32x2>(format_str, inst, args...); |         Add<GlslVarType::F32x2>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::U32x3>(format_str, inst, args...); |         Add<GlslVarType::U32x3>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::F32x3>(format_str, inst, args...); |         Add<GlslVarType::F32x3>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::U32x4>(format_str, inst, args...); |         Add<GlslVarType::U32x4>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) { |     void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
|         Add<Type::F32x4>(format_str, inst, args...); |         Add<GlslVarType::F32x4>(format_str, inst, args...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
| @@ -121,7 +121,7 @@ public: | |||||||
|  |  | ||||||
|     std::string header; |     std::string header; | ||||||
|     std::string code; |     std::string code; | ||||||
|     RegAlloc reg_alloc; |     VarAlloc var_alloc; | ||||||
|     const Info& info; |     const Info& info; | ||||||
|     const Profile& profile; |     const Profile& profile; | ||||||
|     const RuntimeInfo& runtime_info; |     const RuntimeInfo& runtime_info; | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) { | |||||||
| template <typename ArgType> | template <typename ArgType> | ||||||
| auto Arg(EmitContext& ctx, const IR::Value& arg) { | auto Arg(EmitContext& ctx, const IR::Value& arg) { | ||||||
|     if constexpr (std::is_same_v<ArgType, std::string_view>) { |     if constexpr (std::is_same_v<ArgType, std::string_view>) { | ||||||
|         return ctx.reg_alloc.Consume(arg); |         return ctx.var_alloc.Consume(arg); | ||||||
|     } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) { |     } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) { | ||||||
|         return arg; |         return arg; | ||||||
|     } else if constexpr (std::is_same_v<ArgType, u32>) { |     } else if constexpr (std::is_same_v<ArgType, u32>) { | ||||||
| @@ -131,7 +131,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) { | |||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case IR::AbstractSyntaxNode::Type::If: |         case IR::AbstractSyntaxNode::Type::If: | ||||||
|             ctx.Add("if ({}){{", ctx.reg_alloc.Consume(node.data.if_node.cond)); |             ctx.Add("if ({}){{", ctx.var_alloc.Consume(node.data.if_node.cond)); | ||||||
|             break; |             break; | ||||||
|         case IR::AbstractSyntaxNode::Type::EndIf: |         case IR::AbstractSyntaxNode::Type::EndIf: | ||||||
|             ctx.Add("}}"); |             ctx.Add("}}"); | ||||||
| @@ -142,7 +142,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) { | |||||||
|                     ctx.Add("break;"); |                     ctx.Add("break;"); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 ctx.Add("if({}){{break;}}", ctx.reg_alloc.Consume(node.data.break_node.cond)); |                 ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond)); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case IR::AbstractSyntaxNode::Type::Return: |         case IR::AbstractSyntaxNode::Type::Return: | ||||||
| @@ -153,7 +153,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) { | |||||||
|             ctx.Add("for(;;){{"); |             ctx.Add("for(;;){{"); | ||||||
|             break; |             break; | ||||||
|         case IR::AbstractSyntaxNode::Type::Repeat: |         case IR::AbstractSyntaxNode::Type::Repeat: | ||||||
|             ctx.Add("if({}){{", ctx.reg_alloc.Consume(node.data.repeat.cond)); |             ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.repeat.cond)); | ||||||
|             ctx.Add("continue;\n}}else{{"); |             ctx.Add("continue;\n}}else{{"); | ||||||
|             ctx.Add("break;\n}}\n}}"); |             ctx.Add("break;\n}}\n}}"); | ||||||
|             break; |             break; | ||||||
| @@ -171,6 +171,23 @@ std::string GlslVersionSpecifier(const EmitContext& ctx) { | |||||||
|     } |     } | ||||||
|     return ""; |     return ""; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | 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)}; | ||||||
|  |         // Temps/return types that are never used are stored at index 0 | ||||||
|  |         if (tracker.uses_temp) { | ||||||
|  |             header += fmt::format("{}{}={}(0);", type_name, ctx.var_alloc.Representation(0, type), | ||||||
|  |                                   type_name); | ||||||
|  |         } | ||||||
|  |         for (u32 index = 1; index <= tracker.num_used; ++index) { | ||||||
|  |             header += fmt::format("{}{}={}(0);", type_name, | ||||||
|  |                                   ctx.var_alloc.Representation(index, type), type_name); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
|  |  | ||||||
| std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, | std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, | ||||||
| @@ -190,9 +207,7 @@ std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR | |||||||
|     if (program.stage == Stage::VertexA || program.stage == Stage::VertexB) { |     if (program.stage == Stage::VertexA || program.stage == Stage::VertexB) { | ||||||
|         ctx.header += "gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);"; |         ctx.header += "gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);"; | ||||||
|     } |     } | ||||||
|     for (size_t index = 0; index < ctx.reg_alloc.num_used_registers; ++index) { |     DefineVariables(ctx, ctx.header); | ||||||
|         ctx.header += fmt::format("{} R{};", ctx.reg_alloc.reg_types[index], index); |  | ||||||
|     } |  | ||||||
|     if (ctx.uses_cc_carry) { |     if (ctx.uses_cc_carry) { | ||||||
|         ctx.header += "uint carry;"; |         ctx.header += "uint carry;"; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -20,14 +20,14 @@ for (;;){{ | |||||||
|  |  | ||||||
| void SharedCasFunction(EmitContext& ctx, IR::Inst& inst, std::string_view offset, | void SharedCasFunction(EmitContext& ctx, IR::Inst& inst, std::string_view offset, | ||||||
|                        std::string_view value, std::string_view function) { |                        std::string_view value, std::string_view function) { | ||||||
|     const auto ret{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     const std::string smem{fmt::format("smem[{}/4]", offset)}; |     const std::string smem{fmt::format("smem[{}/4]", offset)}; | ||||||
|     ctx.Add(cas_loop.data(), ret, smem, ret, smem, function, smem, value, ret); |     ctx.Add(cas_loop.data(), ret, smem, ret, smem, function, smem, value, ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SsboCasFunction(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | void SsboCasFunction(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||||||
|                      const IR::Value& offset, std::string_view value, std::string_view function) { |                      const IR::Value& offset, std::string_view value, std::string_view function) { | ||||||
|     const auto ret{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     const std::string ssbo{fmt::format("ssbo{}[{}]", binding.U32(), offset.U32())}; |     const std::string ssbo{fmt::format("ssbo{}[{}]", binding.U32(), offset.U32())}; | ||||||
|     ctx.Add(cas_loop.data(), ret, ssbo, ret, ssbo, function, ssbo, value, ret); |     ctx.Add(cas_loop.data(), ret, ssbo, ret, ssbo, function, ssbo, value, ret); | ||||||
| } | } | ||||||
| @@ -36,7 +36,7 @@ void SsboCasFunctionF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& bindi | |||||||
|                         const IR::Value& offset, std::string_view value, |                         const IR::Value& offset, std::string_view value, | ||||||
|                         std::string_view function) { |                         std::string_view function) { | ||||||
|     const std::string ssbo{fmt::format("ssbo{}[{}]", binding.U32(), offset.U32())}; |     const std::string ssbo{fmt::format("ssbo{}[{}]", binding.U32(), offset.U32())}; | ||||||
|     const auto ret{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     ctx.Add(cas_loop.data(), ret, ssbo, ret, ssbo, function, ssbo, value, ret); |     ctx.Add(cas_loop.data(), ret, ssbo, ret, ssbo, function, ssbo, value, ret); | ||||||
|     ctx.AddF32("{}=uintBitsToFloat({});", inst, ret); |     ctx.AddF32("{}=uintBitsToFloat({});", inst, ret); | ||||||
| } | } | ||||||
| @@ -102,9 +102,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_vi | |||||||
| void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||||||
|                                 std::string_view value) { |                                 std::string_view value) { | ||||||
|     // LOG_WARNING("Int64 Atomics not supported, fallback to non-atomic"); |     // LOG_WARNING("Int64 Atomics not supported, fallback to non-atomic"); | ||||||
|     const auto ret{ctx.reg_alloc.Define(inst, Type::U64)}; |     ctx.AddU64("{}=packUint2x32(uvec2(smem[{}/4],smem[({}+4)/4]));", inst, pointer_offset, | ||||||
|     ctx.Add("{}=packUint2x32(uvec2(smem[{}/4],smem[({}+4)/4]));", ret, pointer_offset, |                pointer_offset); | ||||||
|             pointer_offset); |  | ||||||
|     ctx.Add("smem[{}/4]=unpackUint2x32({}).x;smem[({}+4)/4]=unpackUint2x32({}).y;", pointer_offset, |     ctx.Add("smem[{}/4]=unpackUint2x32({}).x;smem[({}+4)/4]=unpackUint2x32({}).y;", pointer_offset, | ||||||
|             value, pointer_offset, value); |             value, pointer_offset, value); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) { | void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) { | ||||||
|     ctx.AddU1("{}={};", inst, ctx.reg_alloc.Consume(value)); |     ctx.AddU1("{}={};", inst, ctx.var_alloc.Consume(value)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { | void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ void EmitGetCbufU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& | |||||||
|                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), |                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), | ||||||
|                    (offset.U32() % 4) * 8); |                    (offset.U32() % 4) * 8); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddU32( |         ctx.AddU32( | ||||||
|             "{}=bitfieldExtract(floatBitsToUint({}_cbuf{}[{}/16][({}/4)%4]),int(({}%4)*8),8);", |             "{}=bitfieldExtract(floatBitsToUint({}_cbuf{}[{}/16][({}/4)%4]),int(({}%4)*8),8);", | ||||||
|             inst, ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); |             inst, ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); | ||||||
| @@ -44,7 +44,7 @@ void EmitGetCbufS8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& | |||||||
|                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), |                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), | ||||||
|                    (offset.U32() % 4) * 8); |                    (offset.U32() % 4) * 8); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddU32( |         ctx.AddU32( | ||||||
|             "{}=bitfieldExtract(floatBitsToInt({}_cbuf{}[{}/16][({}/4)%4]),int(({}%4)*8),8);", inst, |             "{}=bitfieldExtract(floatBitsToInt({}_cbuf{}[{}/16][({}/4)%4]),int(({}%4)*8),8);", inst, | ||||||
|             ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); |             ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); | ||||||
| @@ -59,7 +59,7 @@ void EmitGetCbufU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst | |||||||
|                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), |                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), | ||||||
|                    ((offset.U32() / 2) % 2) * 16); |                    ((offset.U32() / 2) % 2) * 16); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddU32("{}=bitfieldExtract(floatBitsToUint({}_cbuf{}[{}/16][({}/4)%4]),int((({}/" |         ctx.AddU32("{}=bitfieldExtract(floatBitsToUint({}_cbuf{}[{}/16][({}/4)%4]),int((({}/" | ||||||
|                    "2)%2)*16),16);", |                    "2)%2)*16),16);", | ||||||
|                    inst, ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); |                    inst, ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); | ||||||
| @@ -74,7 +74,7 @@ void EmitGetCbufS16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst | |||||||
|                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), |                    ctx.stage_name, binding.U32(), offset.U32() / 16, OffsetSwizzle(offset.U32()), | ||||||
|                    ((offset.U32() / 2) % 2) * 16); |                    ((offset.U32() / 2) % 2) * 16); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddU32( |         ctx.AddU32( | ||||||
|             "{}=bitfieldExtract(floatBitsToInt({}_cbuf{}[{}/16][({}/4)%4]),int((({}/2)%2)*16),16);", |             "{}=bitfieldExtract(floatBitsToInt({}_cbuf{}[{}/16][({}/4)%4]),int((({}/2)%2)*16),16);", | ||||||
|             inst, ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); |             inst, ctx.stage_name, binding.U32(), offset_var, offset_var, offset_var); | ||||||
| @@ -87,7 +87,7 @@ void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | |||||||
|         ctx.AddU32("{}=floatBitsToUint({}_cbuf{}[{}].{});", inst, ctx.stage_name, binding.U32(), |         ctx.AddU32("{}=floatBitsToUint({}_cbuf{}[{}].{});", inst, ctx.stage_name, binding.U32(), | ||||||
|                    offset.U32() / 16, OffsetSwizzle(offset.U32())); |                    offset.U32() / 16, OffsetSwizzle(offset.U32())); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddU32("{}=floatBitsToUint({}_cbuf{}[{}/16][({}/4)%4]);", inst, ctx.stage_name, |         ctx.AddU32("{}=floatBitsToUint({}_cbuf{}[{}/16][({}/4)%4]);", inst, ctx.stage_name, | ||||||
|                    binding.U32(), offset_var, offset_var); |                    binding.U32(), offset_var, offset_var); | ||||||
|     } |     } | ||||||
| @@ -99,7 +99,7 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | |||||||
|         ctx.AddF32("{}={}_cbuf{}[{}].{};", inst, ctx.stage_name, binding.U32(), offset.U32() / 16, |         ctx.AddF32("{}={}_cbuf{}[{}].{};", inst, ctx.stage_name, binding.U32(), offset.U32() / 16, | ||||||
|                    OffsetSwizzle(offset.U32())); |                    OffsetSwizzle(offset.U32())); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddF32("{}={}_cbuf{}[{}/16][({}/4)%4];", inst, ctx.stage_name, binding.U32(), |         ctx.AddF32("{}={}_cbuf{}[{}/16][({}/4)%4];", inst, ctx.stage_name, binding.U32(), | ||||||
|                    offset_var, offset_var); |                    offset_var, offset_var); | ||||||
|     } |     } | ||||||
| @@ -114,7 +114,7 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding | |||||||
|             ctx.stage_name, binding.U32(), (offset.U32() + 4) / 16, |             ctx.stage_name, binding.U32(), (offset.U32() + 4) / 16, | ||||||
|             OffsetSwizzle(offset.U32() + 4)); |             OffsetSwizzle(offset.U32() + 4)); | ||||||
|     } else { |     } else { | ||||||
|         const auto offset_var{ctx.reg_alloc.Consume(offset)}; |         const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|         ctx.AddU32x2("{}=uvec2(floatBitsToUint({}_cbuf{}[{}/16][({}/" |         ctx.AddU32x2("{}=uvec2(floatBitsToUint({}_cbuf{}[{}/16][({}/" | ||||||
|                      "4)%4]),floatBitsToUint({}_cbuf{}[({}+4)/16][(({}+4)/4)%4]));", |                      "4)%4]),floatBitsToUint({}_cbuf{}[({}+4)/16][(({}+4)/4)%4]));", | ||||||
|                      inst, ctx.stage_name, binding.U32(), offset_var, offset_var, ctx.stage_name, |                      inst, ctx.stage_name, binding.U32(), offset_var, offset_var, ctx.stage_name, | ||||||
|   | |||||||
| @@ -104,12 +104,12 @@ void EmitImageSampleImplicitLod([[maybe_unused]] EmitContext& ctx, [[maybe_unuse | |||||||
|     } |     } | ||||||
|     const auto texture{Texture(ctx, info, index)}; |     const auto texture{Texture(ctx, info, index)}; | ||||||
|     const auto bias{info.has_bias ? fmt::format(",{}", bias_lc) : ""}; |     const auto bias{info.has_bias ? fmt::format(",{}", bias_lc) : ""}; | ||||||
|     const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)}; |     const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; | ||||||
|     const auto sparse_inst{PrepareSparse(inst)}; |     const auto sparse_inst{PrepareSparse(inst)}; | ||||||
|     if (!sparse_inst) { |     if (!sparse_inst) { | ||||||
|         if (!offset.IsEmpty()) { |         if (!offset.IsEmpty()) { | ||||||
|             ctx.Add("{}=textureOffset({},{},{}{});", texel, texture, coords, |             ctx.Add("{}=textureOffset({},{},{}{});", texel, texture, coords, | ||||||
|                     CastToIntVec(ctx.reg_alloc.Consume(offset), info), bias); |                     CastToIntVec(ctx.var_alloc.Consume(offset), info), bias); | ||||||
|         } else { |         } else { | ||||||
|             if (ctx.stage == Stage::Fragment) { |             if (ctx.stage == Stage::Fragment) { | ||||||
|                 ctx.Add("{}=texture({},{}{});", texel, texture, coords, bias); |                 ctx.Add("{}=texture({},{}{});", texel, texture, coords, bias); | ||||||
| @@ -122,7 +122,7 @@ void EmitImageSampleImplicitLod([[maybe_unused]] EmitContext& ctx, [[maybe_unuse | |||||||
|     // TODO: Query sparseTexels extension support |     // TODO: Query sparseTexels extension support | ||||||
|     if (!offset.IsEmpty()) { |     if (!offset.IsEmpty()) { | ||||||
|         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureOffsetARB({},{},{},{}{}));", |         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureOffsetARB({},{},{},{}{}));", | ||||||
|                   *sparse_inst, texture, coords, CastToIntVec(ctx.reg_alloc.Consume(offset), info), |                   *sparse_inst, texture, coords, CastToIntVec(ctx.var_alloc.Consume(offset), info), | ||||||
|                   texel, bias); |                   texel, bias); | ||||||
|     } else { |     } else { | ||||||
|         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureARB({},{},{}{}));", *sparse_inst, |         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureARB({},{},{}{}));", *sparse_inst, | ||||||
| @@ -143,12 +143,12 @@ void EmitImageSampleExplicitLod([[maybe_unused]] EmitContext& ctx, [[maybe_unuse | |||||||
|         throw NotImplementedException("Lod clamp samples"); |         throw NotImplementedException("Lod clamp samples"); | ||||||
|     } |     } | ||||||
|     const auto texture{Texture(ctx, info, index)}; |     const auto texture{Texture(ctx, info, index)}; | ||||||
|     const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)}; |     const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; | ||||||
|     const auto sparse_inst{PrepareSparse(inst)}; |     const auto sparse_inst{PrepareSparse(inst)}; | ||||||
|     if (!sparse_inst) { |     if (!sparse_inst) { | ||||||
|         if (!offset.IsEmpty()) { |         if (!offset.IsEmpty()) { | ||||||
|             ctx.Add("{}=textureLodOffset({},{},{},{});", texel, texture, coords, lod_lc, |             ctx.Add("{}=textureLodOffset({},{},{},{});", texel, texture, coords, lod_lc, | ||||||
|                     CastToIntVec(ctx.reg_alloc.Consume(offset), info)); |                     CastToIntVec(ctx.var_alloc.Consume(offset), info)); | ||||||
|         } else { |         } else { | ||||||
|             ctx.Add("{}=textureLod({},{},{});", texel, texture, coords, lod_lc); |             ctx.Add("{}=textureLod({},{},{});", texel, texture, coords, lod_lc); | ||||||
|         } |         } | ||||||
| @@ -158,7 +158,7 @@ void EmitImageSampleExplicitLod([[maybe_unused]] EmitContext& ctx, [[maybe_unuse | |||||||
|     if (!offset.IsEmpty()) { |     if (!offset.IsEmpty()) { | ||||||
|         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", |         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", | ||||||
|                   *sparse_inst, texture, CastToIntVec(coords, info), lod_lc, |                   *sparse_inst, texture, CastToIntVec(coords, info), lod_lc, | ||||||
|                   CastToIntVec(ctx.reg_alloc.Consume(offset), info), texel); |                   CastToIntVec(ctx.var_alloc.Consume(offset), info), texel); | ||||||
|     } else { |     } else { | ||||||
|         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureLodARB({},{},{},{}));", *sparse_inst, |         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureLodARB({},{},{},{}));", *sparse_inst, | ||||||
|                   texture, coords, lod_lc, texel); |                   texture, coords, lod_lc, texel); | ||||||
| @@ -232,7 +232,7 @@ void EmitImageGather([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Ins | |||||||
|                      [[maybe_unused]] const IR::Value& offset2) { |                      [[maybe_unused]] const IR::Value& offset2) { | ||||||
|     const auto info{inst.Flags<IR::TextureInstInfo>()}; |     const auto info{inst.Flags<IR::TextureInstInfo>()}; | ||||||
|     const auto texture{Texture(ctx, info, index)}; |     const auto texture{Texture(ctx, info, index)}; | ||||||
|     const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)}; |     const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; | ||||||
|     const auto sparse_inst{PrepareSparse(inst)}; |     const auto sparse_inst{PrepareSparse(inst)}; | ||||||
|     if (!sparse_inst) { |     if (!sparse_inst) { | ||||||
|         if (offset.IsEmpty()) { |         if (offset.IsEmpty()) { | ||||||
| @@ -242,7 +242,7 @@ void EmitImageGather([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Ins | |||||||
|         } |         } | ||||||
|         if (offset2.IsEmpty()) { |         if (offset2.IsEmpty()) { | ||||||
|             ctx.Add("{}=textureGatherOffset({},{},{},int({}));", texel, texture, coords, |             ctx.Add("{}=textureGatherOffset({},{},{},int({}));", texel, texture, coords, | ||||||
|                     CastToIntVec(ctx.reg_alloc.Consume(offset), info), info.gather_component); |                     CastToIntVec(ctx.var_alloc.Consume(offset), info), info.gather_component); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         // PTP |         // PTP | ||||||
| @@ -259,7 +259,7 @@ void EmitImageGather([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Ins | |||||||
|     if (offset2.IsEmpty()) { |     if (offset2.IsEmpty()) { | ||||||
|         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));", |         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));", | ||||||
|                   *sparse_inst, texture, CastToIntVec(coords, info), |                   *sparse_inst, texture, CastToIntVec(coords, info), | ||||||
|                   CastToIntVec(ctx.reg_alloc.Consume(offset), info), texel, info.gather_component); |                   CastToIntVec(ctx.var_alloc.Consume(offset), info), texel, info.gather_component); | ||||||
|     } |     } | ||||||
|     // PTP |     // PTP | ||||||
|     const auto offsets{PtpOffsets(offset, offset2)}; |     const auto offsets{PtpOffsets(offset, offset2)}; | ||||||
| @@ -276,7 +276,7 @@ void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR: | |||||||
|                          [[maybe_unused]] std::string_view dref) { |                          [[maybe_unused]] std::string_view dref) { | ||||||
|     const auto info{inst.Flags<IR::TextureInstInfo>()}; |     const auto info{inst.Flags<IR::TextureInstInfo>()}; | ||||||
|     const auto texture{Texture(ctx, info, index)}; |     const auto texture{Texture(ctx, info, index)}; | ||||||
|     const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)}; |     const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; | ||||||
|     const auto sparse_inst{PrepareSparse(inst)}; |     const auto sparse_inst{PrepareSparse(inst)}; | ||||||
|     if (!sparse_inst) { |     if (!sparse_inst) { | ||||||
|         if (offset.IsEmpty()) { |         if (offset.IsEmpty()) { | ||||||
| @@ -285,7 +285,7 @@ void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR: | |||||||
|         } |         } | ||||||
|         if (offset2.IsEmpty()) { |         if (offset2.IsEmpty()) { | ||||||
|             ctx.Add("{}=textureGatherOffset({},{},{},{});", texel, texture, coords, dref, |             ctx.Add("{}=textureGatherOffset({},{},{},{});", texel, texture, coords, dref, | ||||||
|                     CastToIntVec(ctx.reg_alloc.Consume(offset), info)); |                     CastToIntVec(ctx.var_alloc.Consume(offset), info)); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         // PTP |         // PTP | ||||||
| @@ -301,7 +301,7 @@ void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR: | |||||||
|     if (offset2.IsEmpty()) { |     if (offset2.IsEmpty()) { | ||||||
|         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));", |         ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));", | ||||||
|                   *sparse_inst, texture, CastToIntVec(coords, info), dref, |                   *sparse_inst, texture, CastToIntVec(coords, info), dref, | ||||||
|                   CastToIntVec(ctx.reg_alloc.Consume(offset), info), texel); |                   CastToIntVec(ctx.var_alloc.Consume(offset), info), texel); | ||||||
|     } |     } | ||||||
|     // PTP |     // PTP | ||||||
|     const auto offsets{PtpOffsets(offset, offset2)}; |     const auto offsets{PtpOffsets(offset, offset2)}; | ||||||
| @@ -323,7 +323,7 @@ void EmitImageFetch([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst | |||||||
|     } |     } | ||||||
|     const auto texture{Texture(ctx, info, index)}; |     const auto texture{Texture(ctx, info, index)}; | ||||||
|     const auto sparse_inst{PrepareSparse(inst)}; |     const auto sparse_inst{PrepareSparse(inst)}; | ||||||
|     const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)}; |     const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; | ||||||
|     if (!sparse_inst) { |     if (!sparse_inst) { | ||||||
|         if (!offset.empty()) { |         if (!offset.empty()) { | ||||||
|             ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, |             ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) { | |||||||
| } | } | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
| void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||||||
|     const auto result{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) { |     if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) { | ||||||
|         ctx.uses_cc_carry = true; |         ctx.uses_cc_carry = true; | ||||||
|         ctx.Add("{}=uaddCarry({},{},carry);", result, a, b); |         ctx.Add("{}=uaddCarry({},{},carry);", result, a, b); | ||||||
| @@ -130,7 +130,7 @@ void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base, | |||||||
|  |  | ||||||
| void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base, | void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||||||
|                           std::string_view offset, std::string_view count) { |                           std::string_view offset, std::string_view count) { | ||||||
|     const auto result{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count); |     ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count); | ||||||
|     SetZeroFlag(ctx, inst, result); |     SetZeroFlag(ctx, inst, result); | ||||||
|     SetSignFlag(ctx, inst, result); |     SetSignFlag(ctx, inst, result); | ||||||
| @@ -138,7 +138,7 @@ void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view bas | |||||||
|  |  | ||||||
| void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base, | void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||||||
|                           std::string_view offset, std::string_view count) { |                           std::string_view offset, std::string_view count) { | ||||||
|     const auto result{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count); |     ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count); | ||||||
|     SetZeroFlag(ctx, inst, result); |     SetZeroFlag(ctx, inst, result); | ||||||
|     SetSignFlag(ctx, inst, result); |     SetSignFlag(ctx, inst, result); | ||||||
| @@ -184,7 +184,7 @@ void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::strin | |||||||
|  |  | ||||||
| void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min, | void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min, | ||||||
|                   std::string_view max) { |                   std::string_view max) { | ||||||
|     const auto result{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max); |     ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max); | ||||||
|     SetZeroFlag(ctx, inst, result); |     SetZeroFlag(ctx, inst, result); | ||||||
|     SetSignFlag(ctx, inst, result); |     SetSignFlag(ctx, inst, result); | ||||||
| @@ -192,7 +192,7 @@ void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std: | |||||||
|  |  | ||||||
| void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min, | void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min, | ||||||
|                   std::string_view max) { |                   std::string_view max) { | ||||||
|     const auto result{ctx.reg_alloc.Define(inst, Type::U32)}; |     const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)}; | ||||||
|     ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max); |     ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max); | ||||||
|     SetZeroFlag(ctx, inst, result); |     SetZeroFlag(ctx, inst, result); | ||||||
|     SetSignFlag(ctx, inst, result); |     SetSignFlag(ctx, inst, result); | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ namespace Shader::Backend::GLSL { | |||||||
| void EmitLoadStorageU8([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | void EmitLoadStorageU8([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | ||||||
|                        [[maybe_unused]] const IR::Value& binding, |                        [[maybe_unused]] const IR::Value& binding, | ||||||
|                        [[maybe_unused]] const IR::Value& offset) { |                        [[maybe_unused]] const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddU32("{}=bitfieldExtract(ssbo{}[{}/4],int({}%4)*8,8);", inst, binding.U32(), offset_var, |     ctx.AddU32("{}=bitfieldExtract(ssbo{}[{}/4],int({}%4)*8,8);", inst, binding.U32(), offset_var, | ||||||
|                offset_var); |                offset_var); | ||||||
| } | } | ||||||
| @@ -20,7 +20,7 @@ void EmitLoadStorageU8([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | |||||||
| void EmitLoadStorageS8([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | void EmitLoadStorageS8([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | ||||||
|                        [[maybe_unused]] const IR::Value& binding, |                        [[maybe_unused]] const IR::Value& binding, | ||||||
|                        [[maybe_unused]] const IR::Value& offset) { |                        [[maybe_unused]] const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddS32("{}=bitfieldExtract(int(ssbo{}[{}/4]),int({}%4)*8,8);", inst, binding.U32(), |     ctx.AddS32("{}=bitfieldExtract(int(ssbo{}[{}/4]),int({}%4)*8,8);", inst, binding.U32(), | ||||||
|                offset_var, offset_var); |                offset_var, offset_var); | ||||||
| } | } | ||||||
| @@ -28,7 +28,7 @@ void EmitLoadStorageS8([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | |||||||
| void EmitLoadStorageU16([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | void EmitLoadStorageU16([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | ||||||
|                         [[maybe_unused]] const IR::Value& binding, |                         [[maybe_unused]] const IR::Value& binding, | ||||||
|                         [[maybe_unused]] const IR::Value& offset) { |                         [[maybe_unused]] const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddU32("{}=bitfieldExtract(ssbo{}[{}/4],int(({}/2)%2)*16,16);", inst, binding.U32(), |     ctx.AddU32("{}=bitfieldExtract(ssbo{}[{}/4],int(({}/2)%2)*16,16);", inst, binding.U32(), | ||||||
|                offset_var, offset_var); |                offset_var, offset_var); | ||||||
| } | } | ||||||
| @@ -36,27 +36,27 @@ void EmitLoadStorageU16([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | |||||||
| void EmitLoadStorageS16([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | void EmitLoadStorageS16([[maybe_unused]] EmitContext& ctx, IR::Inst& inst, | ||||||
|                         [[maybe_unused]] const IR::Value& binding, |                         [[maybe_unused]] const IR::Value& binding, | ||||||
|                         [[maybe_unused]] const IR::Value& offset) { |                         [[maybe_unused]] const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddS32("{}=bitfieldExtract(int(ssbo{}[{}/4]),int(({}/2)%2)*16,16);", inst, binding.U32(), |     ctx.AddS32("{}=bitfieldExtract(int(ssbo{}[{}/4]),int(({}/2)%2)*16,16);", inst, binding.U32(), | ||||||
|                offset_var, offset_var); |                offset_var, offset_var); | ||||||
| } | } | ||||||
|  |  | ||||||
| void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||||||
|                        const IR::Value& offset) { |                        const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddU32("{}=ssbo{}[{}/4];", inst, binding.U32(), offset_var); |     ctx.AddU32("{}=ssbo{}[{}/4];", inst, binding.U32(), offset_var); | ||||||
| } | } | ||||||
|  |  | ||||||
| void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||||||
|                        const IR::Value& offset) { |                        const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddU32x2("{}=uvec2(ssbo{}[{}/4],ssbo{}[({}+4)/4]);", inst, binding.U32(), offset_var, |     ctx.AddU32x2("{}=uvec2(ssbo{}[{}/4],ssbo{}[({}+4)/4]);", inst, binding.U32(), offset_var, | ||||||
|                  binding.U32(), offset_var); |                  binding.U32(), offset_var); | ||||||
| } | } | ||||||
|  |  | ||||||
| void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||||||
|                         const IR::Value& offset) { |                         const IR::Value& offset) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.AddU32x4("{}=uvec4(ssbo{}[{}/4],ssbo{}[({}+4)/4],ssbo{}[({}+8)/4],ssbo{}[({}+12)/4]);", |     ctx.AddU32x4("{}=uvec4(ssbo{}[{}/4],ssbo{}[({}+4)/4],ssbo{}[({}+8)/4],ssbo{}[({}+12)/4]);", | ||||||
|                  inst, binding.U32(), offset_var, binding.U32(), offset_var, binding.U32(), |                  inst, binding.U32(), offset_var, binding.U32(), offset_var, binding.U32(), | ||||||
|                  offset_var, binding.U32(), offset_var); |                  offset_var, binding.U32(), offset_var); | ||||||
| @@ -66,7 +66,7 @@ void EmitWriteStorageU8([[maybe_unused]] EmitContext& ctx, | |||||||
|                         [[maybe_unused]] const IR::Value& binding, |                         [[maybe_unused]] const IR::Value& binding, | ||||||
|                         [[maybe_unused]] const IR::Value& offset, |                         [[maybe_unused]] const IR::Value& offset, | ||||||
|                         [[maybe_unused]] std::string_view value) { |                         [[maybe_unused]] std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int({}%4)*8,8);", binding.U32(), |     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int({}%4)*8,8);", binding.U32(), | ||||||
|             offset_var, binding.U32(), offset_var, value, offset_var); |             offset_var, binding.U32(), offset_var, value, offset_var); | ||||||
| } | } | ||||||
| @@ -75,7 +75,7 @@ void EmitWriteStorageS8([[maybe_unused]] EmitContext& ctx, | |||||||
|                         [[maybe_unused]] const IR::Value& binding, |                         [[maybe_unused]] const IR::Value& binding, | ||||||
|                         [[maybe_unused]] const IR::Value& offset, |                         [[maybe_unused]] const IR::Value& offset, | ||||||
|                         [[maybe_unused]] std::string_view value) { |                         [[maybe_unused]] std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int({}%4)*8,8);", binding.U32(), |     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int({}%4)*8,8);", binding.U32(), | ||||||
|             offset_var, binding.U32(), offset_var, value, offset_var); |             offset_var, binding.U32(), offset_var, value, offset_var); | ||||||
| } | } | ||||||
| @@ -84,7 +84,7 @@ void EmitWriteStorageU16([[maybe_unused]] EmitContext& ctx, | |||||||
|                          [[maybe_unused]] const IR::Value& binding, |                          [[maybe_unused]] const IR::Value& binding, | ||||||
|                          [[maybe_unused]] const IR::Value& offset, |                          [[maybe_unused]] const IR::Value& offset, | ||||||
|                          [[maybe_unused]] std::string_view value) { |                          [[maybe_unused]] std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int(({}/2)%2)*16,16);", binding.U32(), |     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int(({}/2)%2)*16,16);", binding.U32(), | ||||||
|             offset_var, binding.U32(), offset_var, value, offset_var); |             offset_var, binding.U32(), offset_var, value, offset_var); | ||||||
| } | } | ||||||
| @@ -93,20 +93,20 @@ void EmitWriteStorageS16([[maybe_unused]] EmitContext& ctx, | |||||||
|                          [[maybe_unused]] const IR::Value& binding, |                          [[maybe_unused]] const IR::Value& binding, | ||||||
|                          [[maybe_unused]] const IR::Value& offset, |                          [[maybe_unused]] const IR::Value& offset, | ||||||
|                          [[maybe_unused]] std::string_view value) { |                          [[maybe_unused]] std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int(({}/2)%2)*16,16);", binding.U32(), |     ctx.Add("ssbo{}[{}/4]=bitfieldInsert(ssbo{}[{}/4],{},int(({}/2)%2)*16,16);", binding.U32(), | ||||||
|             offset_var, binding.U32(), offset_var, value, offset_var); |             offset_var, binding.U32(), offset_var, value, offset_var); | ||||||
| } | } | ||||||
|  |  | ||||||
| void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||||||
|                         std::string_view value) { |                         std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]={};", binding.U32(), offset_var, value); |     ctx.Add("ssbo{}[{}/4]={};", binding.U32(), offset_var, value); | ||||||
| } | } | ||||||
|  |  | ||||||
| void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||||||
|                         std::string_view value) { |                         std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]={}.x;", binding.U32(), offset_var, value); |     ctx.Add("ssbo{}[{}/4]={}.x;", binding.U32(), offset_var, value); | ||||||
|     ctx.Add("ssbo{}[({}+4)/4]={}.y;", binding.U32(), offset_var, value); |     ctx.Add("ssbo{}[({}+4)/4]={}.y;", binding.U32(), offset_var, value); | ||||||
| } | } | ||||||
| @@ -115,7 +115,7 @@ void EmitWriteStorage128([[maybe_unused]] EmitContext& ctx, | |||||||
|                          [[maybe_unused]] const IR::Value& binding, |                          [[maybe_unused]] const IR::Value& binding, | ||||||
|                          [[maybe_unused]] const IR::Value& offset, |                          [[maybe_unused]] const IR::Value& offset, | ||||||
|                          [[maybe_unused]] std::string_view value) { |                          [[maybe_unused]] std::string_view value) { | ||||||
|     const auto offset_var{ctx.reg_alloc.Consume(offset)}; |     const auto offset_var{ctx.var_alloc.Consume(offset)}; | ||||||
|     ctx.Add("ssbo{}[{}/4]={}.x;", binding.U32(), offset_var, value); |     ctx.Add("ssbo{}[{}/4]={}.x;", binding.U32(), offset_var, value); | ||||||
|     ctx.Add("ssbo{}[({}+4)/4]={}.y;", binding.U32(), offset_var, value); |     ctx.Add("ssbo{}[({}+4)/4]={}.y;", binding.U32(), offset_var, value); | ||||||
|     ctx.Add("ssbo{}[({}+8)/4]={}.z;", binding.U32(), offset_var, value); |     ctx.Add("ssbo{}[({}+8)/4]={}.z;", binding.U32(), offset_var, value); | ||||||
|   | |||||||
| @@ -21,11 +21,11 @@ static void NotImplemented() { | |||||||
| void EmitPhi(EmitContext& ctx, IR::Inst& phi) { | void EmitPhi(EmitContext& ctx, IR::Inst& phi) { | ||||||
|     const size_t num_args{phi.NumArgs()}; |     const size_t num_args{phi.NumArgs()}; | ||||||
|     for (size_t i = 0; i < num_args; ++i) { |     for (size_t i = 0; i < num_args; ++i) { | ||||||
|         ctx.reg_alloc.Consume(phi.Arg(i)); |         ctx.var_alloc.Consume(phi.Arg(i)); | ||||||
|     } |     } | ||||||
|     if (!phi.Definition<Id>().is_valid) { |     if (!phi.Definition<Id>().is_valid) { | ||||||
|         // The phi node wasn't forward defined |         // The phi node wasn't forward defined | ||||||
|         ctx.Add("{};", ctx.reg_alloc.Define(phi, phi.Arg(0).Type())); |         ctx.Add("{};", ctx.var_alloc.Define(phi, phi.Arg(0).Type())); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -42,10 +42,10 @@ void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& | |||||||
|     const auto phi_type{phi.Arg(0).Type()}; |     const auto phi_type{phi.Arg(0).Type()}; | ||||||
|     if (!phi.Definition<Id>().is_valid) { |     if (!phi.Definition<Id>().is_valid) { | ||||||
|         // The phi node wasn't forward defined |         // The phi node wasn't forward defined | ||||||
|         ctx.Add("{};", ctx.reg_alloc.Define(phi, phi_type)); |         ctx.Add("{};", ctx.var_alloc.Define(phi, phi_type)); | ||||||
|     } |     } | ||||||
|     const auto phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})}; |     const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})}; | ||||||
|     const auto val_reg{ctx.reg_alloc.Consume(value)}; |     const auto val_reg{ctx.var_alloc.Consume(value)}; | ||||||
|     if (phi_reg == val_reg) { |     if (phi_reg == val_reg) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,191 +0,0 @@ | |||||||
| // Copyright 2021 yuzu Emulator Project |  | ||||||
| // Licensed under GPLv2 or any later version |  | ||||||
| // Refer to the license.txt file included. |  | ||||||
|  |  | ||||||
| #include <string> |  | ||||||
| #include <string_view> |  | ||||||
|  |  | ||||||
| #include <fmt/format.h> |  | ||||||
|  |  | ||||||
| #include "shader_recompiler/backend/glsl/reg_alloc.h" |  | ||||||
| #include "shader_recompiler/exception.h" |  | ||||||
| #include "shader_recompiler/frontend/ir/value.h" |  | ||||||
|  |  | ||||||
| namespace Shader::Backend::GLSL { |  | ||||||
| namespace { |  | ||||||
| std::string Representation(Id id) { |  | ||||||
|     if (id.is_condition_code != 0) { |  | ||||||
|         throw NotImplementedException("Condition code"); |  | ||||||
|     } |  | ||||||
|     if (id.is_spill != 0) { |  | ||||||
|         throw NotImplementedException("Spilling"); |  | ||||||
|     } |  | ||||||
|     const u32 index{static_cast<u32>(id.index)}; |  | ||||||
|     return fmt::format("R{}", index); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string FormatFloat(std::string_view value, IR::Type type) { |  | ||||||
|     // TODO: Confirm FP64 nan/inf |  | ||||||
|     if (type == IR::Type::F32) { |  | ||||||
|         if (value == "nan") { |  | ||||||
|             return "uintBitsToFloat(0x7fc00000)"; |  | ||||||
|         } |  | ||||||
|         if (value == "inf") { |  | ||||||
|             return "uintBitsToFloat(0x7f800000)"; |  | ||||||
|         } |  | ||||||
|         if (value == "-inf") { |  | ||||||
|             return "uintBitsToFloat(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 RegAlloc::Define(IR::Inst& inst) { |  | ||||||
|     const Id id{Alloc()}; |  | ||||||
|     inst.SetDefinition<Id>(id); |  | ||||||
|     return Representation(id); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RegAlloc::Define(IR::Inst& inst, Type type) { |  | ||||||
|     const Id id{Alloc()}; |  | ||||||
|     std::string type_str = ""; |  | ||||||
|     if (!register_defined[id.index]) { |  | ||||||
|         register_defined[id.index] = true; |  | ||||||
|         // type_str = GetGlslType(type); |  | ||||||
|         reg_types.push_back(GetGlslType(type)); |  | ||||||
|         ++num_used_registers; |  | ||||||
|     } |  | ||||||
|     inst.SetDefinition<Id>(id); |  | ||||||
|     return type_str + Representation(id); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RegAlloc::Define(IR::Inst& inst, IR::Type type) { |  | ||||||
|     return Define(inst, RegType(type)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RegAlloc::Consume(const IR::Value& value) { |  | ||||||
|     return value.IsImmediate() ? MakeImm(value) : Consume(*value.InstRecursive()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RegAlloc::Consume(IR::Inst& inst) { |  | ||||||
|     inst.DestructiveRemoveUsage(); |  | ||||||
|     // TODO: reuse variables of same type if possible |  | ||||||
|     // if (!inst.HasUses()) { |  | ||||||
|     //     Free(id); |  | ||||||
|     // } |  | ||||||
|     return Representation(inst.Definition<Id>()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Type RegAlloc::RegType(IR::Type type) { |  | ||||||
|     switch (type) { |  | ||||||
|     case IR::Type::U1: |  | ||||||
|         return Type::U1; |  | ||||||
|     case IR::Type::U32: |  | ||||||
|         return Type::U32; |  | ||||||
|     case IR::Type::F32: |  | ||||||
|         return Type::F32; |  | ||||||
|     case IR::Type::U64: |  | ||||||
|         return Type::U64; |  | ||||||
|     case IR::Type::F64: |  | ||||||
|         return Type::F64; |  | ||||||
|     default: |  | ||||||
|         throw NotImplementedException("IR type {}", type); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RegAlloc::GetGlslType(Type type) { |  | ||||||
|     switch (type) { |  | ||||||
|     case Type::U1: |  | ||||||
|         return "bool "; |  | ||||||
|     case Type::F16x2: |  | ||||||
|         return "f16vec2 "; |  | ||||||
|     case Type::U32: |  | ||||||
|         return "uint "; |  | ||||||
|     case Type::S32: |  | ||||||
|         return "int "; |  | ||||||
|     case Type::F32: |  | ||||||
|         return "float "; |  | ||||||
|     case Type::S64: |  | ||||||
|         return "int64_t "; |  | ||||||
|     case Type::U64: |  | ||||||
|         return "uint64_t "; |  | ||||||
|     case Type::F64: |  | ||||||
|         return "double "; |  | ||||||
|     case Type::U32x2: |  | ||||||
|         return "uvec2 "; |  | ||||||
|     case Type::F32x2: |  | ||||||
|         return "vec2 "; |  | ||||||
|     case Type::U32x3: |  | ||||||
|         return "uvec3 "; |  | ||||||
|     case Type::F32x3: |  | ||||||
|         return "vec3 "; |  | ||||||
|     case Type::U32x4: |  | ||||||
|         return "uvec4 "; |  | ||||||
|     case Type::F32x4: |  | ||||||
|         return "vec4 "; |  | ||||||
|     case Type::Void: |  | ||||||
|         return ""; |  | ||||||
|     default: |  | ||||||
|         throw NotImplementedException("Type {}", type); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RegAlloc::GetGlslType(IR::Type type) { |  | ||||||
|     return GetGlslType(RegType(type)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Id RegAlloc::Alloc() { |  | ||||||
|     if (num_used_registers < NUM_REGS) { |  | ||||||
|         for (size_t reg = 0; reg < NUM_REGS; ++reg) { |  | ||||||
|             if (register_use[reg]) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             register_use[reg] = true; |  | ||||||
|             Id ret{}; |  | ||||||
|             ret.is_valid.Assign(1); |  | ||||||
|             ret.is_long.Assign(0); |  | ||||||
|             ret.is_spill.Assign(0); |  | ||||||
|             ret.is_condition_code.Assign(0); |  | ||||||
|             ret.index.Assign(static_cast<u32>(reg)); |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     throw NotImplementedException("Register spilling"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void RegAlloc::Free(Id id) { |  | ||||||
|     if (id.is_spill != 0) { |  | ||||||
|         throw NotImplementedException("Free spill"); |  | ||||||
|     } |  | ||||||
|     register_use[id.index] = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace Shader::Backend::GLSL |  | ||||||
| @@ -1,84 +0,0 @@ | |||||||
| // Copyright 2021 yuzu Emulator Project |  | ||||||
| // Licensed under GPLv2 or any later version |  | ||||||
| // Refer to the license.txt file included. |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <bitset> |  | ||||||
| #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 Type : u32 { |  | ||||||
|     U1, |  | ||||||
|     F16x2, |  | ||||||
|     S32, |  | ||||||
|     U32, |  | ||||||
|     F32, |  | ||||||
|     S64, |  | ||||||
|     U64, |  | ||||||
|     F64, |  | ||||||
|     U32x2, |  | ||||||
|     F32x2, |  | ||||||
|     U32x3, |  | ||||||
|     F32x3, |  | ||||||
|     U32x4, |  | ||||||
|     F32x4, |  | ||||||
|     Void, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct Id { |  | ||||||
|     union { |  | ||||||
|         u32 raw; |  | ||||||
|         BitField<0, 1, u32> is_valid; |  | ||||||
|         BitField<1, 1, u32> is_long; |  | ||||||
|         BitField<2, 1, u32> is_spill; |  | ||||||
|         BitField<3, 1, u32> is_condition_code; |  | ||||||
|         BitField<4, 1, u32> is_null; |  | ||||||
|         BitField<5, 27, 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 RegAlloc { |  | ||||||
| public: |  | ||||||
|     std::string Define(IR::Inst& inst); |  | ||||||
|     std::string Define(IR::Inst& inst, Type type); |  | ||||||
|     std::string Define(IR::Inst& inst, IR::Type type); |  | ||||||
|  |  | ||||||
|     std::string Consume(const IR::Value& value); |  | ||||||
|     std::string Consume(IR::Inst& inst); |  | ||||||
|  |  | ||||||
|     std::string GetGlslType(Type type); |  | ||||||
|     std::string GetGlslType(IR::Type type); |  | ||||||
|  |  | ||||||
|     size_t num_used_registers{}; |  | ||||||
|     std::vector<std::string> reg_types; |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     static constexpr size_t NUM_REGS = 4096; |  | ||||||
|  |  | ||||||
|     Type RegType(IR::Type type); |  | ||||||
|     Id Alloc(); |  | ||||||
|     void Free(Id id); |  | ||||||
|  |  | ||||||
|     std::bitset<NUM_REGS> register_use{}; |  | ||||||
|     std::bitset<NUM_REGS> register_defined{}; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| } // namespace Shader::Backend::GLSL |  | ||||||
							
								
								
									
										290
									
								
								src/shader_recompiler/backend/glsl/var_alloc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								src/shader_recompiler/backend/glsl/var_alloc.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,290 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #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::S32: | ||||||
|  |         return "s_"; | ||||||
|  |     case GlslVarType::F32: | ||||||
|  |         return "f_"; | ||||||
|  |     case GlslVarType::S64: | ||||||
|  |         return "s64_"; | ||||||
|  |     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::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 "uintBitsToFloat(0x7fc00000)"; | ||||||
|  |         } | ||||||
|  |         if (value == "inf") { | ||||||
|  |             return "uintBitsToFloat(0x7f800000)"; | ||||||
|  |         } | ||||||
|  |         if (value == "-inf") { | ||||||
|  |             return "uintBitsToFloat(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); | ||||||
|  |         // id.is_null.Assign(1); | ||||||
|  |         GetUseTracker(type).uses_temp = true; | ||||||
|  |         inst.SetDefinition<Id>(id); | ||||||
|  |     } | ||||||
|  |     return Representation(inst.Definition<Id>()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) { | ||||||
|  |     return Define(inst, RegType(type)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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)}; | ||||||
|  |     if (use_tracker.num_used < NUM_VARS) { | ||||||
|  |         for (size_t var = 1; 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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     throw NotImplementedException("Variable spilling"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void VarAlloc::Free(Id id) { | ||||||
|  |     if (id.is_valid == 0) { | ||||||
|  |         // throw LogicError("Freeing invalid variable"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     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::S32: | ||||||
|  |         return "int "; | ||||||
|  |     case GlslVarType::F32: | ||||||
|  |         return "float "; | ||||||
|  |     case GlslVarType::S64: | ||||||
|  |         return "int64_t "; | ||||||
|  |     case GlslVarType::U64: | ||||||
|  |         return "uint64_t "; | ||||||
|  |     case GlslVarType::F64: | ||||||
|  |         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::U32: | ||||||
|  |         return var_u32; | ||||||
|  |     case GlslVarType::S32: | ||||||
|  |         return var_s32; | ||||||
|  |     case GlslVarType::F32: | ||||||
|  |         return var_f32; | ||||||
|  |     case GlslVarType::S64: | ||||||
|  |         return var_s64; | ||||||
|  |     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; | ||||||
|  |     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::S32: | ||||||
|  |         return var_s32; | ||||||
|  |     case GlslVarType::F32: | ||||||
|  |         return var_f32; | ||||||
|  |     case GlslVarType::S64: | ||||||
|  |         return var_s64; | ||||||
|  |     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; | ||||||
|  |     default: | ||||||
|  |         throw NotImplementedException("Type {}", type); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Backend::GLSL | ||||||
							
								
								
									
										100
									
								
								src/shader_recompiler/backend/glsl/var_alloc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/shader_recompiler/backend/glsl/var_alloc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #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, | ||||||
|  |     S32, | ||||||
|  |     U32, | ||||||
|  |     F32, | ||||||
|  |     S64, | ||||||
|  |     U64, | ||||||
|  |     F64, | ||||||
|  |     U32x2, | ||||||
|  |     F32x2, | ||||||
|  |     U32x3, | ||||||
|  |     F32x3, | ||||||
|  |     U32x4, | ||||||
|  |     F32x4, | ||||||
|  |     Void, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct Id { | ||||||
|  |     union { | ||||||
|  |         u32 raw; | ||||||
|  |         BitField<0, 1, u32> is_valid; | ||||||
|  |         BitField<1, 4, GlslVarType> type; | ||||||
|  |         BitField<5, 27, 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: | ||||||
|  |     static constexpr size_t NUM_VARS = 511; | ||||||
|  |     struct UseTracker { | ||||||
|  |         size_t num_used{}; | ||||||
|  |         std::bitset<NUM_VARS> var_use{}; | ||||||
|  |         bool uses_temp{}; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     std::string Define(IR::Inst& inst, GlslVarType type); | ||||||
|  |     std::string Define(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_s32{}; | ||||||
|  |     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_s64{}; | ||||||
|  |     UseTracker var_f64{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::Backend::GLSL | ||||||
		Reference in New Issue
	
	Block a user