From ab2108fb2a485abd50c4ea9459c807f37e8bc3dd Mon Sep 17 00:00:00 2001
From: Marcos <marcosvitali@users.noreply.github.com>
Date: Tue, 4 Dec 2018 14:24:35 -0300
Subject: [PATCH] Rewrited TEX/TEXS (TEX Scalar). (#1826)

* Rewrited TEX/TEXS (TEX Scalar).

* Style fixes.

* Styles issues.
---
 .../renderer_opengl/gl_shader_decompiler.cpp  | 436 +++++++-----------
 1 file changed, 177 insertions(+), 259 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index d235bfcd4c..8d68156bf4 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1515,6 +1515,161 @@ private:
         }
     }
 
+    std::pair<size_t, std::string> ValidateAndGetCoordinateElement(
+        const Tegra::Shader::TextureType texture_type, const bool depth_compare,
+        const bool is_array, const bool lod_bias_enabled, size_t max_coords, size_t max_inputs) {
+        const size_t coord_count = TextureCoordinates(texture_type);
+
+        size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0);
+        const size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0);
+        if (total_coord_count > max_coords || total_reg_count > max_inputs) {
+            UNIMPLEMENTED_MSG("Unsupported Texture operation");
+            total_coord_count = std::min(total_coord_count, max_coords);
+        }
+        // 1D.DC opengl is using a vec3 but 2nd component is ignored later.
+        total_coord_count +=
+            (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D)
+                ? 1
+                : 0;
+
+        constexpr std::array<const char*, 5> coord_container{
+            {"", "float coord = (", "vec2 coord = vec2(", "vec3 coord = vec3(",
+             "vec4 coord = vec4("}};
+
+        return std::pair<size_t, std::string>(coord_count, coord_container[total_coord_count]);
+    }
+
+    std::string GetTextureCode(const Tegra::Shader::Instruction& instr,
+                               const Tegra::Shader::TextureType texture_type,
+                               const Tegra::Shader::TextureProcessMode process_mode,
+                               const bool depth_compare, const bool is_array,
+                               const size_t bias_offset) {
+
+        if ((texture_type == Tegra::Shader::TextureType::Texture3D &&
+             (is_array || depth_compare)) ||
+            (texture_type == Tegra::Shader::TextureType::TextureCube && is_array &&
+             depth_compare)) {
+            UNIMPLEMENTED_MSG("This method is not supported.");
+        }
+
+        const std::string sampler =
+            GetSampler(instr.sampler, texture_type, is_array, depth_compare);
+
+        const bool lod_needed = process_mode == Tegra::Shader::TextureProcessMode::LZ ||
+                                process_mode == Tegra::Shader::TextureProcessMode::LL ||
+                                process_mode == Tegra::Shader::TextureProcessMode::LLA;
+
+        const bool gl_lod_supported = !(
+            (texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
+            (texture_type == Tegra::Shader::TextureType::TextureCube && !is_array &&
+             depth_compare));
+
+        const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture(";
+        std::string texture = read_method + sampler + ", coord";
+
+        if (process_mode != Tegra::Shader::TextureProcessMode::None) {
+            if (process_mode == Tegra::Shader::TextureProcessMode::LZ) {
+                if (gl_lod_supported) {
+                    texture += ", 0";
+                } else {
+                    // Lod 0 is emulated by a big negative bias
+                    // in scenarios that are not supported by glsl
+                    texture += ", -1000";
+                }
+            } else {
+                // If present, lod or bias are always stored in the register indexed by the
+                // gpr20
+                // field with an offset depending on the usage of the other registers
+                texture += ',' + regs.GetRegisterAsFloat(instr.gpr20.Value() + bias_offset);
+            }
+        }
+        texture += ")";
+        return texture;
+    }
+
+    std::pair<std::string, std::string> GetTEXCode(
+        const Instruction& instr, const Tegra::Shader::TextureType texture_type,
+        const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare,
+        const bool is_array) {
+        const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None &&
+                                       process_mode != Tegra::Shader::TextureProcessMode::LZ);
+
+        const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement(
+            texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5);
+        // If enabled arrays index is always stored in the gpr8 field
+        const u64 array_register = instr.gpr8.Value();
+        // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
+        const u64 coord_register = array_register + (is_array ? 1 : 0);
+
+        std::string coord = coord_dcl;
+        for (size_t i = 0; i < coord_count;) {
+            coord += regs.GetRegisterAsFloat(coord_register + i);
+            ++i;
+            if (i != coord_count) {
+                coord += ',';
+            }
+        }
+        // 1D.DC in opengl the 2nd component is ignored.
+        if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) {
+            coord += ",0.0";
+        }
+        if (depth_compare) {
+            // Depth is always stored in the register signaled by gpr20
+            // or in the next register if lod or bias are used
+            const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
+            coord += ',' + regs.GetRegisterAsFloat(depth_register);
+        }
+        if (is_array) {
+            coord += ',' + regs.GetRegisterAsInteger(array_register);
+        }
+        coord += ");";
+        return std::make_pair(
+            coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0));
+    }
+
+    std::pair<std::string, std::string> GetTEXSCode(
+        const Instruction& instr, const Tegra::Shader::TextureType texture_type,
+        const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare,
+        const bool is_array) {
+        const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None &&
+                                       process_mode != Tegra::Shader::TextureProcessMode::LZ);
+
+        const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement(
+            texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4);
+        // If enabled arrays index is always stored in the gpr8 field
+        const u64 array_register = instr.gpr8.Value();
+        // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used
+        const u64 coord_register = array_register + (is_array ? 1 : 0);
+        const u64 last_coord_register =
+            (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2))
+                ? static_cast<u64>(instr.gpr20.Value())
+                : coord_register + 1;
+
+        std::string coord = coord_dcl;
+        for (size_t i = 0; i < coord_count; ++i) {
+            const bool last = (i == (coord_count - 1)) && (coord_count > 1);
+            coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i);
+            if (!last) {
+                coord += ',';
+            }
+        }
+
+        if (depth_compare) {
+            // Depth is always stored in the register signaled by gpr20
+            // or in the next register if lod or bias are used
+            const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
+            coord += ',' + regs.GetRegisterAsFloat(depth_register);
+        }
+        if (is_array) {
+            coord += ',' + regs.GetRegisterAsInteger(array_register);
+        }
+        coord += ");";
+
+        return std::make_pair(coord,
+                              GetTextureCode(instr, texture_type, process_mode, depth_compare,
+                                             is_array, (coord_count > 2 ? 1 : 0)));
+    }
+
     /**
      * Compiles a single instruction from Tegra to GLSL.
      * @param offset the offset of the Tegra shader instruction.
@@ -2574,168 +2729,32 @@ private:
             case OpCode::Id::TEX: {
                 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
                 const bool is_array = instr.tex.array != 0;
-
+                const bool depth_compare =
+                    instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
+                const auto process_mode = instr.tex.GetTextureProcessMode();
                 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
                                      "NODEP is not implemented");
                 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
                                      "AOFFI is not implemented");
 
-                const bool depth_compare =
-                    instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
-                u32 num_coordinates = TextureCoordinates(texture_type);
-                u32 start_index = 0;
-                std::string array_elem;
-                if (is_array) {
-                    array_elem = regs.GetRegisterAsInteger(instr.gpr8);
-                    start_index = 1;
-                }
-                const auto process_mode = instr.tex.GetTextureProcessMode();
-                u32 start_index_b = 0;
-                std::string lod_value;
-                if (process_mode != Tegra::Shader::TextureProcessMode::LZ &&
-                    process_mode != Tegra::Shader::TextureProcessMode::None) {
-                    start_index_b = 1;
-                    lod_value = regs.GetRegisterAsFloat(instr.gpr20);
-                }
-
-                std::string depth_value;
-                if (depth_compare) {
-                    depth_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + start_index_b);
-                }
-
-                bool depth_compare_extra = false;
+                const auto [coord, texture] =
+                    GetTEXCode(instr, texture_type, process_mode, depth_compare, is_array);
 
                 const auto scope = shader.Scope();
-
-                switch (num_coordinates) {
-                case 1: {
-                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
-                    if (is_array) {
-                        if (depth_compare) {
-                            shader.AddLine("vec3 coords = vec3(" + x + ", " + depth_value + ", " +
-                                           array_elem + ");");
-                        } else {
-                            shader.AddLine("vec2 coords = vec2(" + x + ", " + array_elem + ");");
-                        }
-                    } else {
-                        if (depth_compare) {
-                            shader.AddLine("vec2 coords = vec2(" + x + ", " + depth_value + ");");
-                        } else {
-                            shader.AddLine("float coords = " + x + ';');
-                        }
-                    }
-                    break;
-                }
-                case 2: {
-                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
-                    const std::string y =
-                        regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1);
-                    if (is_array) {
-                        if (depth_compare) {
-                            shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " +
-                                           depth_value + ", " + array_elem + ");");
-                        } else {
-                            shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " +
-                                           array_elem + ");");
-                        }
-                    } else {
-                        if (depth_compare) {
-                            shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " +
-                                           depth_value + ");");
-                        } else {
-                            shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
-                        }
-                    }
-                    break;
-                }
-                case 3: {
-                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
-                    const std::string y =
-                        regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1);
-                    const std::string z =
-                        regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 2);
-                    if (is_array) {
-                        depth_compare_extra = depth_compare;
-                        shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
-                                       array_elem + ");");
-                    } else {
-                        if (depth_compare) {
-                            shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
-                                           depth_value + ");");
-                        } else {
-                            shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
-                        }
-                    }
-                    break;
-                }
-                default:
-                    UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
-                                      static_cast<u32>(num_coordinates));
-
-                    // Fallback to interpreting as a 2D texture for now
-                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                    const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                    shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
-                    texture_type = Tegra::Shader::TextureType::Texture2D;
-                }
-
-                const std::string sampler =
-                    GetSampler(instr.sampler, texture_type, is_array, depth_compare);
-                // Add an extra scope and declare the texture coords inside to prevent
-                // overwriting them in case they are used as outputs of the texs instruction.
-
-                const std::string texture = [&]() {
-                    switch (instr.tex.GetTextureProcessMode()) {
-                    case Tegra::Shader::TextureProcessMode::None:
-                        if (depth_compare_extra) {
-                            return "texture(" + sampler + ", coords, " + depth_value + ')';
-                        }
-                        return "texture(" + sampler + ", coords)";
-                    case Tegra::Shader::TextureProcessMode::LZ:
-                        if (depth_compare_extra) {
-                            return "texture(" + sampler + ", coords, " + depth_value + ')';
-                        }
-                        return "textureLod(" + sampler + ", coords, 0.0)";
-                    case Tegra::Shader::TextureProcessMode::LB:
-                    case Tegra::Shader::TextureProcessMode::LBA:
-                        // TODO: Figure if A suffix changes the equation at all.
-                        if (depth_compare_extra) {
-                            LOG_WARNING(
-                                HW_GPU,
-                                "OpenGL Limitation: can't set bias value along depth compare");
-                            return "texture(" + sampler + ", coords, " + depth_value + ')';
-                        }
-                        return "texture(" + sampler + ", coords, " + lod_value + ')';
-                    case Tegra::Shader::TextureProcessMode::LL:
-                    case Tegra::Shader::TextureProcessMode::LLA:
-                        // TODO: Figure if A suffix changes the equation at all.
-                        if (depth_compare_extra) {
-                            LOG_WARNING(
-                                HW_GPU,
-                                "OpenGL Limitation: can't set lod value along depth compare");
-                            return "texture(" + sampler + ", coords, " + depth_value + ')';
-                        }
-                        return "textureLod(" + sampler + ", coords, " + lod_value + ')';
-                    default:
-                        UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
-                                          static_cast<u32>(instr.tex.GetTextureProcessMode()));
-                        if (depth_compare_extra) {
-                            return "texture(" + sampler + ", coords, " + depth_value + ')';
-                        }
-                        return "texture(" + sampler + ", coords)";
-                    }
-                }();
+                shader.AddLine(coord);
 
                 if (depth_compare) {
                     regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
                 } else {
+                    shader.AddLine("vec4 texture_tmp = " + texture + ';');
                     std::size_t dest_elem{};
                     for (std::size_t elem = 0; elem < 4; ++elem) {
                         if (!instr.tex.IsComponentEnabled(elem)) {
                             // Skip disabled components
                             continue;
                         }
-                        regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
+                        regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
+                                                dest_elem);
                         ++dest_elem;
                     }
                 }
@@ -2743,129 +2762,28 @@ private:
             }
             case OpCode::Id::TEXS: {
                 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
-                bool is_array{instr.texs.IsArrayTexture()};
-
+                const bool is_array{instr.texs.IsArrayTexture()};
+                const bool depth_compare =
+                    instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
+                const auto process_mode = instr.texs.GetTextureProcessMode();
                 UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
                                      "NODEP is not implemented");
 
                 const auto scope = shader.Scope();
 
-                const bool depth_compare =
-                    instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
-                u32 num_coordinates = TextureCoordinates(texture_type);
-                const auto process_mode = instr.texs.GetTextureProcessMode();
-                u32 lod_offset = 0;
-                if (process_mode == Tegra::Shader::TextureProcessMode::LL) {
-                    if (num_coordinates > 2) {
-                        shader.AddLine("float lod_value = " +
-                                       regs.GetRegisterAsFloat(instr.gpr20.Value() + 1) + ';');
-                        lod_offset = 2;
-                    } else {
-                        shader.AddLine("float lod_value = " + regs.GetRegisterAsFloat(instr.gpr20) +
-                                       ';');
-                        lod_offset = 1;
-                    }
-                }
+                const auto [coord, texture] =
+                    GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array);
 
-                switch (num_coordinates) {
-                case 1: {
-                    shader.AddLine("float coords = " + regs.GetRegisterAsFloat(instr.gpr8) + ';');
-                    break;
-                }
-                case 2: {
-                    if (is_array) {
-                        if (depth_compare) {
-                            const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
-                            const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                            const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
-                            const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
-                            shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
-                                           index + ");");
-                        } else {
-                            const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
-                            const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                            const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
-                            shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + index +
-                                           ");");
-                        }
-                    } else {
-                        if (lod_offset != 0) {
-                            if (depth_compare) {
-                                const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                                const std::string y =
-                                    regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                                const std::string z =
-                                    regs.GetRegisterAsFloat(instr.gpr20.Value() + lod_offset);
-                                shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z +
-                                               ");");
-                            } else {
-                                const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                                const std::string y =
-                                    regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                                shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
-                            }
-                        } else {
-                            if (depth_compare) {
-                                const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                                const std::string y =
-                                    regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                                const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
-                                shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z +
-                                               ");");
-                            } else {
-                                const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                                const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
-                                shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
-                            }
-                        }
-                    }
-                    break;
-                }
-                case 3: {
-                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                    const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                    const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
-                    shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
-                    break;
-                }
-                default:
-                    UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
-                                      static_cast<u32>(num_coordinates));
+                shader.AddLine(coord);
 
-                    // Fallback to interpreting as a 2D texture for now
-                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
-                    const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
-                    shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
-                    texture_type = Tegra::Shader::TextureType::Texture2D;
-                    is_array = false;
-                }
-                const std::string sampler =
-                    GetSampler(instr.sampler, texture_type, is_array, depth_compare);
+                if (!depth_compare) {
+                    shader.AddLine("vec4 texture_tmp = " + texture + ';');
 
-                std::string texture = [&]() {
-                    switch (process_mode) {
-                    case Tegra::Shader::TextureProcessMode::None:
-                        return "texture(" + sampler + ", coords)";
-                    case Tegra::Shader::TextureProcessMode::LZ:
-                        if (depth_compare && is_array) {
-                            return "texture(" + sampler + ", coords)";
-                        } else {
-                            return "textureLod(" + sampler + ", coords, 0.0)";
-                        }
-                        break;
-                    case Tegra::Shader::TextureProcessMode::LL:
-                        return "textureLod(" + sampler + ", coords, lod_value)";
-                    default:
-                        UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
-                                          static_cast<u32>(instr.texs.GetTextureProcessMode()));
-                        return "texture(" + sampler + ", coords)";
-                    }
-                }();
-                if (depth_compare) {
-                    texture = "vec4(" + texture + ')';
+                } else {
+                    shader.AddLine("vec4 texture_tmp = vec4(" + texture + ");");
                 }
 
-                WriteTexsInstruction(instr, texture);
+                WriteTexsInstruction(instr, "texture_tmp");
                 break;
             }
             case OpCode::Id::TLDS: {
@@ -3934,4 +3852,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
     return {};
 }
 
-} // namespace OpenGL::GLShader::Decompiler
+} // namespace OpenGL::GLShader::Decompiler
\ No newline at end of file
-- 
GitLab