summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/backend/glasm/emit_glasm_image.cpp')
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp103
1 files changed, 92 insertions, 11 deletions
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index 77da3400b..9da4da331 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <utility>
+
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
@@ -95,6 +97,33 @@ std::string Offset(EmitContext& ctx, const IR::Value& offset) {
return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
}
+std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
+ const IR::Value& offset2) {
+ if (offset2.IsEmpty()) {
+ return {};
+ } else {
+ return {ctx.reg_alloc, ctx.reg_alloc};
+ }
+}
+
+void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
+ const IR::Value& offset2) {
+ const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
+ const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
+ // Input swizzle: [XYXY] [XYXY]
+ // Output swizzle: [XXXX] [YYYY]
+ ctx.Add("MOV {}.x,{}.x;"
+ "MOV {}.y,{}.z;"
+ "MOV {}.z,{}.x;"
+ "MOV {}.w,{}.z;"
+ "MOV {}.x,{}.y;"
+ "MOV {}.y,{}.w;"
+ "MOV {}.z,{}.y;"
+ "MOV {}.w,{}.w;",
+ off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
+ offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
+}
+
std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
if (coord.IsImmediate()) {
ScopedRegister scoped_reg(ctx.reg_alloc);
@@ -326,19 +355,71 @@ void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::
StoreSparse(ctx, sparse_inst);
}
-void EmitImageGather([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
- [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
- [[maybe_unused]] const IR::Value& offset,
- [[maybe_unused]] const IR::Value& offset2) {
- throw NotImplementedException("GLASM instruction");
+void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
+ const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
+ // Allocate offsets early so they don't overwrite any consumed register
+ const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
+ const auto info{inst.Flags<IR::TextureInstInfo>()};
+ const char comp{"xyzw"[info.gather_component]};
+ const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
+ const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
+ const std::string_view type{TextureType(info)};
+ const std::string texture{Texture(ctx, info, index)};
+ const Register coord_vec{ctx.reg_alloc.Consume(coord)};
+ const Register ret{ctx.reg_alloc.Define(inst)};
+ if (offset2.IsEmpty()) {
+ const std::string offset_vec{Offset(ctx, offset)};
+ ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
+ offset_vec);
+ } else {
+ SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
+ ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
+ texture, comp, type);
+ }
+ StoreSparse(ctx, sparse_inst);
}
-void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
- [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
- [[maybe_unused]] const IR::Value& offset,
- [[maybe_unused]] const IR::Value& offset2,
- [[maybe_unused]] Register dref) {
- throw NotImplementedException("GLASM instruction");
+void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
+ const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
+ const IR::Value& dref) {
+ // FIXME: This instruction is not working as expected
+
+ // Allocate offsets early so they don't overwrite any consumed register
+ const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
+ const auto info{inst.Flags<IR::TextureInstInfo>()};
+ const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
+ const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
+ const std::string_view type{ShadowTextureType(info)};
+ const std::string texture{Texture(ctx, info, index)};
+ const Register coord_vec{ctx.reg_alloc.Consume(coord)};
+ const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
+ const Register ret{ctx.reg_alloc.Define(inst)};
+ std::string args;
+ switch (info.type) {
+ case TextureType::Color2D:
+ ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
+ args = fmt::to_string(coord_vec);
+ break;
+ case TextureType::ColorArray2D:
+ case TextureType::ColorCube:
+ ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
+ args = fmt::to_string(coord_vec);
+ break;
+ case TextureType::ColorArrayCube:
+ args = fmt::format("{},{}", coord_vec, dref_value);
+ break;
+ default:
+ throw NotImplementedException("Invalid type {}", info.type.Value());
+ }
+ if (offset2.IsEmpty()) {
+ const std::string offset_vec{Offset(ctx, offset)};
+ ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
+ } else {
+ SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
+ ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
+ type);
+ }
+ StoreSparse(ctx, sparse_inst);
}
void EmitImageFetch([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,