From 7d5a38ea6cb692cdeb5565b36b283d1a34d5d27f Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Fri, 1 Apr 2022 17:08:40 -0400
Subject: [PATCH] shader_compiler: support const buffer indirect addressing in
 GLSL

---
 .../glsl/emit_glsl_context_get_set.cpp        | 25 +++++++++++++------
 .../backend/glsl/glsl_emit_context.cpp        | 19 ++++++++++++++
 .../backend/glsl/glsl_emit_context.h          |  1 +
 src/shader_recompiler/shader_info.h           |  2 +-
 4 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 0c1fbc7b1f..282668b363 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -35,6 +35,15 @@ 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 = {}) {
@@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
     const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
                                     : fmt::format("[({}>>2)%4]", offset_var)};
 
-    const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
-    const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)};
+    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)};
@@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
 
 void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                       const IR::Value& offset) {
-    const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
     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())};
@@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
         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({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
-                     cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
+        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({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
-                swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
-                "xyzw"[(swizzle + 1) % 4]);
+        ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
+                cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
     }
 }
 
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index e816a93ecb..17266f40d3 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -359,6 +359,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
         header += "layout(location=0) uniform vec4 scaling;";
     }
     DefineConstantBuffers(bindings);
+    DefineConstantBufferIndirect();
     DefineStorageBuffers(bindings);
     SetupImages(bindings);
     SetupTextures(bindings);
@@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
     }
 }
 
+void EmitContext::DefineConstantBufferIndirect() {
+    if (!info.uses_cbuf_indirect) {
+        return;
+    }
+
+    header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 ";
+    header += "GetCbufIndirect(uint binding, uint offset){"
+              "switch(binding){"
+              "default:";
+
+    for (const auto& desc : info.constant_buffer_descriptors) {
+        header +=
+            fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index);
+    }
+
+    header += "}}";
+}
+
 void EmitContext::DefineStorageBuffers(Bindings& bindings) {
     if (info.storage_buffers_descriptors.empty()) {
         return;
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
index d9b639d290..2b13db6e65 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.h
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
@@ -162,6 +162,7 @@ public:
 private:
     void SetupExtensions();
     void DefineConstantBuffers(Bindings& bindings);
+    void DefineConstantBufferIndirect();
     void DefineStorageBuffers(Bindings& bindings);
     void DefineGenericOutput(size_t index, u32 invocations);
     void DefineHelperFunctions();
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index c14856dd02..dca7205c3b 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -105,7 +105,7 @@ struct ImageDescriptor {
 using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
 
 struct Info {
-    static constexpr size_t MAX_INDIRECT_CBUFS{16};
+    static constexpr size_t MAX_INDIRECT_CBUFS{15};
     static constexpr size_t MAX_CBUFS{18};
     static constexpr size_t MAX_SSBOS{32};