From dd860b684c7695097107c1186e96a70e754e5990 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 20 Apr 2021 19:48:45 -0300
Subject: shader: Implement D3D samplers

---
 src/shader_recompiler/ir_opt/texture_pass.cpp | 80 +++++++++++++++++++++++----
 1 file changed, 69 insertions(+), 11 deletions(-)

(limited to 'src/shader_recompiler/ir_opt/texture_pass.cpp')

diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index e1d5a2ce1c..5ac485522f 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -19,6 +19,9 @@ namespace {
 struct ConstBufferAddr {
     u32 index;
     u32 offset;
+    u32 secondary_index;
+    u32 secondary_offset;
+    bool has_secondary;
 };
 
 struct TextureInst {
@@ -109,9 +112,38 @@ bool IsTextureInstruction(const IR::Inst& inst) {
     return IndexedInstruction(inst) != IR::Opcode::Void;
 }
 
+std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst);
+
+std::optional<ConstBufferAddr> Track(const IR::Value& value) {
+    return IR::BreadthFirstSearch(value, TryGetConstBuffer);
+}
+
 std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
-    if (inst->GetOpcode() != IR::Opcode::GetCbufU32) {
+    switch (inst->GetOpcode()) {
+    default:
         return std::nullopt;
+    case IR::Opcode::BitwiseOr32: {
+        std::optional lhs{Track(inst->Arg(0))};
+        std::optional rhs{Track(inst->Arg(1))};
+        if (!lhs || !rhs) {
+            return std::nullopt;
+        }
+        if (lhs->has_secondary || rhs->has_secondary) {
+            return std::nullopt;
+        }
+        if (lhs->index > rhs->index || lhs->offset > rhs->offset) {
+            std::swap(lhs, rhs);
+        }
+        return ConstBufferAddr{
+            .index = lhs->index,
+            .offset = lhs->offset,
+            .secondary_index = rhs->index,
+            .secondary_offset = rhs->offset,
+            .has_secondary = true,
+        };
+    }
+    case IR::Opcode::GetCbufU32:
+        break;
     }
     const IR::Value index{inst->Arg(0)};
     const IR::Value offset{inst->Arg(1)};
@@ -127,13 +159,12 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
     return ConstBufferAddr{
         .index{index.U32()},
         .offset{offset.U32()},
+        .secondary_index = 0,
+        .secondary_offset = 0,
+        .has_secondary = false,
     };
 }
 
-std::optional<ConstBufferAddr> Track(const IR::Value& value) {
-    return IR::BreadthFirstSearch(value, TryGetConstBuffer);
-}
-
 TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
     ConstBufferAddr addr;
     if (IsBindless(inst)) {
@@ -146,6 +177,9 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
         addr = ConstBufferAddr{
             .index = env.TextureBoundBuffer(),
             .offset = inst.Arg(0).U32(),
+            .secondary_index = 0,
+            .secondary_offset = 0,
+            .has_secondary = false,
         };
     }
     return TextureInst{
@@ -155,6 +189,14 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
     };
 }
 
+TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
+    const u32 secondary_index{cbuf.has_secondary ? cbuf.index : cbuf.secondary_index};
+    const u32 secondary_offset{cbuf.has_secondary ? cbuf.offset : cbuf.secondary_offset};
+    const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
+    const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
+    return env.ReadTextureType(lhs_raw | rhs_raw);
+}
+
 class Descriptors {
 public:
     explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -167,8 +209,11 @@ public:
 
     u32 Add(const TextureBufferDescriptor& desc) {
         return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
-            return desc.cbuf_index == existing.cbuf_index &&
-                   desc.cbuf_offset == existing.cbuf_offset;
+            return desc.has_secondary == existing.has_secondary &&
+                   desc.cbuf_index == existing.cbuf_index &&
+                   desc.cbuf_offset == existing.cbuf_offset &&
+                   desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
+                   desc.secondary_cbuf_offset == existing.secondary_cbuf_offset;
         });
     }
 
@@ -181,8 +226,12 @@ public:
 
     u32 Add(const TextureDescriptor& desc) {
         return Add(texture_descriptors, desc, [&desc](const auto& existing) {
-            return desc.cbuf_index == existing.cbuf_index &&
-                   desc.cbuf_offset == existing.cbuf_offset && desc.type == existing.type;
+            return desc.type == existing.type && desc.is_depth == existing.is_depth &&
+                   desc.has_secondary == existing.has_secondary &&
+                   desc.cbuf_index == existing.cbuf_index &&
+                   desc.cbuf_offset == existing.cbuf_offset &&
+                   desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
+                   desc.secondary_cbuf_offset == existing.secondary_cbuf_offset;
         });
     }
 
@@ -247,14 +296,14 @@ void TexturePass(Environment& env, IR::Program& program) {
         auto flags{inst->Flags<IR::TextureInstInfo>()};
         switch (inst->GetOpcode()) {
         case IR::Opcode::ImageQueryDimensions:
-            flags.type.Assign(env.ReadTextureType(cbuf.index, cbuf.offset));
+            flags.type.Assign(ReadTextureType(env, cbuf));
             inst->SetFlags(flags);
             break;
         case IR::Opcode::ImageFetch:
             if (flags.type != TextureType::Color1D) {
                 break;
             }
-            if (env.ReadTextureType(cbuf.index, cbuf.offset) == TextureType::Buffer) {
+            if (ReadTextureType(env, cbuf) == TextureType::Buffer) {
                 // Replace with the bound texture type only when it's a texture buffer
                 // If the instruction is 1D and the bound type is 2D, don't change the code and let
                 // the rasterizer robustness handle it
@@ -270,6 +319,9 @@ void TexturePass(Environment& env, IR::Program& program) {
         switch (inst->GetOpcode()) {
         case IR::Opcode::ImageRead:
         case IR::Opcode::ImageWrite: {
+            if (cbuf.has_secondary) {
+                throw NotImplementedException("Unexpected separate sampler");
+            }
             const bool is_written{inst->GetOpcode() == IR::Opcode::ImageWrite};
             if (flags.type == TextureType::Buffer) {
                 index = descriptors.Add(ImageBufferDescriptor{
@@ -294,16 +346,22 @@ void TexturePass(Environment& env, IR::Program& program) {
         default:
             if (flags.type == TextureType::Buffer) {
                 index = descriptors.Add(TextureBufferDescriptor{
+                    .has_secondary = cbuf.has_secondary,
                     .cbuf_index = cbuf.index,
                     .cbuf_offset = cbuf.offset,
+                    .secondary_cbuf_index = cbuf.secondary_index,
+                    .secondary_cbuf_offset = cbuf.secondary_offset,
                     .count = 1,
                 });
             } else {
                 index = descriptors.Add(TextureDescriptor{
                     .type = flags.type,
                     .is_depth = flags.is_depth != 0,
+                    .has_secondary = cbuf.has_secondary,
                     .cbuf_index = cbuf.index,
                     .cbuf_offset = cbuf.offset,
+                    .secondary_cbuf_index = cbuf.secondary_index,
+                    .secondary_cbuf_offset = cbuf.secondary_offset,
                     .count = 1,
                 });
             }
-- 
cgit v1.2.3-70-g09d2