From 69b910e9e7c2b9c361f4389cb1d136105b991bc0 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 May 2021 17:19:35 -0300
Subject: [PATCH] video_core: Abstract transform feedback translation utility

---
 src/video_core/CMakeLists.txt                 |  2 +
 .../renderer_vulkan/fixed_pipeline_state.cpp  | 25 +++--
 .../renderer_vulkan/fixed_pipeline_state.h    | 15 +--
 .../renderer_vulkan/vk_pipeline_cache.cpp     | 86 +---------------
 src/video_core/transform_feedback.cpp         | 98 +++++++++++++++++++
 src/video_core/transform_feedback.h           | 30 ++++++
 6 files changed, 145 insertions(+), 111 deletions(-)
 create mode 100644 src/video_core/transform_feedback.cpp
 create mode 100644 src/video_core/transform_feedback.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index b008c37c0c..8250f736ca 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -187,6 +187,8 @@ add_library(video_core STATIC
     textures/decoders.h
     textures/texture.cpp
     textures/texture.h
+    transform_feedback.cpp
+    transform_feedback.h
     video_core.cpp
     video_core.h
     vulkan_common/vulkan_debug_callback.cpp
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 24834e0f72..3a43c329fb 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -15,9 +15,7 @@
 #include "video_core/renderer_vulkan/vk_state_tracker.h"
 
 namespace Vulkan {
-
 namespace {
-
 constexpr size_t POINT = 0;
 constexpr size_t LINE = 1;
 constexpr size_t POLYGON = 2;
@@ -39,6 +37,16 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
     POLYGON, // Patches
 };
 
+void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
+    std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) {
+        return VideoCommon::TransformFeedbackState::Layout{
+            .stream = layout.stream,
+            .varying_count = layout.varying_count,
+            .stride = layout.stride,
+        };
+    });
+    state.varyings = regs.tfb_varying_locs;
+}
 } // Anonymous namespace
 
 void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
@@ -121,7 +129,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
         dynamic_state.Refresh(regs);
     }
     if (xfb_enabled != 0) {
-        xfb_state.Refresh(regs);
+        RefreshXfbState(xfb_state, regs);
     }
 }
 
@@ -164,17 +172,6 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
     enable.Assign(1);
 }
 
-void FixedPipelineState::TransformFeedbackState::Refresh(const Maxwell& regs) {
-    std::ranges::transform(regs.tfb_layouts, layouts.begin(), [](const auto& layout) {
-        return Layout{
-            .stream = layout.stream,
-            .varying_count = layout.varying_count,
-            .stride = layout.stride,
-        };
-    });
-    varyings = regs.tfb_varying_locs;
-}
-
 void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
     u32 packed_front_face = PackFrontFace(regs.front_face);
     if (regs.screen_y_control.triangle_rast_flip != 0) {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 31de6b2c89..0f1eff9cd6 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -12,6 +12,7 @@
 
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/surface.h"
+#include "video_core/transform_feedback.h"
 
 namespace Vulkan {
 
@@ -130,18 +131,6 @@ struct FixedPipelineState {
         }
     };
 
-    struct TransformFeedbackState {
-        struct Layout {
-            u32 stream;
-            u32 varying_count;
-            u32 stride;
-        };
-        std::array<Layout, Maxwell::NumTransformFeedbackBuffers> layouts;
-        std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> varyings;
-
-        void Refresh(const Maxwell& regs);
-    };
-
     struct DynamicState {
         union {
             u32 raw1;
@@ -213,7 +202,7 @@ struct FixedPipelineState {
     std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
     std::array<u16, Maxwell::NumViewports> viewport_swizzles;
     DynamicState dynamic_state;
-    TransformFeedbackState xfb_state;
+    VideoCommon::TransformFeedbackState xfb_state;
 
     void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state);
 
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 88db10b752..f86bf9c306 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -109,88 +109,6 @@ static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexA
     return Shader::AttributeType::Float;
 }
 
-std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
-    const GraphicsPipelineCacheKey& key) {
-    static constexpr std::array VECTORS{
-        28,  // gl_Position
-        32,  // Generic 0
-        36,  // Generic 1
-        40,  // Generic 2
-        44,  // Generic 3
-        48,  // Generic 4
-        52,  // Generic 5
-        56,  // Generic 6
-        60,  // Generic 7
-        64,  // Generic 8
-        68,  // Generic 9
-        72,  // Generic 10
-        76,  // Generic 11
-        80,  // Generic 12
-        84,  // Generic 13
-        88,  // Generic 14
-        92,  // Generic 15
-        96,  // Generic 16
-        100, // Generic 17
-        104, // Generic 18
-        108, // Generic 19
-        112, // Generic 20
-        116, // Generic 21
-        120, // Generic 22
-        124, // Generic 23
-        128, // Generic 24
-        132, // Generic 25
-        136, // Generic 26
-        140, // Generic 27
-        144, // Generic 28
-        148, // Generic 29
-        152, // Generic 30
-        156, // Generic 31
-        160, // gl_FrontColor
-        164, // gl_FrontSecondaryColor
-        160, // gl_BackColor
-        164, // gl_BackSecondaryColor
-        192, // gl_TexCoord[0]
-        196, // gl_TexCoord[1]
-        200, // gl_TexCoord[2]
-        204, // gl_TexCoord[3]
-        208, // gl_TexCoord[4]
-        212, // gl_TexCoord[5]
-        216, // gl_TexCoord[6]
-        220, // gl_TexCoord[7]
-    };
-    std::vector<Shader::TransformFeedbackVarying> xfb(256);
-    for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) {
-        const auto& locations = key.state.xfb_state.varyings[buffer];
-        const auto& layout = key.state.xfb_state.layouts[buffer];
-        const u32 varying_count = layout.varying_count;
-        u32 highest = 0;
-        for (u32 offset = 0; offset < varying_count; ++offset) {
-            const u32 base_offset = offset;
-            const u8 location = locations[offset];
-
-            Shader::TransformFeedbackVarying varying;
-            varying.buffer = layout.stream;
-            varying.stride = layout.stride;
-            varying.offset = offset * 4;
-            varying.components = 1;
-
-            if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) {
-                UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB");
-
-                const u8 base_index = location / 4;
-                while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) {
-                    ++offset;
-                    ++varying.components;
-                }
-            }
-            xfb[location] = varying;
-            highest = std::max(highest, (base_offset + varying.components) * 4);
-        }
-        UNIMPLEMENTED_IF(highest != layout.stride);
-    }
-    return xfb;
-}
-
 Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
                                     const Shader::IR::Program& program) {
     Shader::RuntimeInfo info;
@@ -206,7 +124,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
                 info.fixed_state_point_size = point_size;
             }
             if (key.state.xfb_enabled != 0) {
-                info.xfb_varyings = MakeTransformFeedbackVaryings(key);
+                info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
             }
             info.convert_depth_mode = gl_ndc;
         }
@@ -248,7 +166,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
             info.fixed_state_point_size = point_size;
         }
         if (key.state.xfb_enabled != 0) {
-            info.xfb_varyings = MakeTransformFeedbackVaryings(key);
+            info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
         }
         info.convert_depth_mode = gl_ndc;
         break;
diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp
new file mode 100644
index 0000000000..db52fff937
--- /dev/null
+++ b/src/video_core/transform_feedback.cpp
@@ -0,0 +1,98 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <vector>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "shader_recompiler/shader_info.h"
+#include "video_core/transform_feedback.h"
+
+namespace VideoCommon {
+
+std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
+    const TransformFeedbackState& state) {
+    static constexpr std::array VECTORS{
+        28,  // gl_Position
+        32,  // Generic 0
+        36,  // Generic 1
+        40,  // Generic 2
+        44,  // Generic 3
+        48,  // Generic 4
+        52,  // Generic 5
+        56,  // Generic 6
+        60,  // Generic 7
+        64,  // Generic 8
+        68,  // Generic 9
+        72,  // Generic 10
+        76,  // Generic 11
+        80,  // Generic 12
+        84,  // Generic 13
+        88,  // Generic 14
+        92,  // Generic 15
+        96,  // Generic 16
+        100, // Generic 17
+        104, // Generic 18
+        108, // Generic 19
+        112, // Generic 20
+        116, // Generic 21
+        120, // Generic 22
+        124, // Generic 23
+        128, // Generic 24
+        132, // Generic 25
+        136, // Generic 26
+        140, // Generic 27
+        144, // Generic 28
+        148, // Generic 29
+        152, // Generic 30
+        156, // Generic 31
+        160, // gl_FrontColor
+        164, // gl_FrontSecondaryColor
+        160, // gl_BackColor
+        164, // gl_BackSecondaryColor
+        192, // gl_TexCoord[0]
+        196, // gl_TexCoord[1]
+        200, // gl_TexCoord[2]
+        204, // gl_TexCoord[3]
+        208, // gl_TexCoord[4]
+        212, // gl_TexCoord[5]
+        216, // gl_TexCoord[6]
+        220, // gl_TexCoord[7]
+    };
+    std::vector<Shader::TransformFeedbackVarying> xfb(256);
+    for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) {
+        const auto& locations = state.varyings[buffer];
+        const auto& layout = state.layouts[buffer];
+        const u32 varying_count = layout.varying_count;
+        u32 highest = 0;
+        for (u32 offset = 0; offset < varying_count; ++offset) {
+            const u32 base_offset = offset;
+            const u8 location = locations[offset];
+
+            Shader::TransformFeedbackVarying varying{
+                .buffer = layout.stream,
+                .stride = layout.stride,
+                .offset = offset * 4,
+                .components = 1,
+            };
+            if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) {
+                UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB");
+
+                const u8 base_index = location / 4;
+                while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) {
+                    ++offset;
+                    ++varying.components;
+                }
+            }
+            xfb[location] = varying;
+            highest = std::max(highest, (base_offset + varying.components) * 4);
+        }
+        UNIMPLEMENTED_IF(highest != layout.stride);
+    }
+    return xfb;
+}
+
+} // namespace VideoCommon
diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h
new file mode 100644
index 0000000000..6832c6db1d
--- /dev/null
+++ b/src/video_core/transform_feedback.h
@@ -0,0 +1,30 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+#include "shader_recompiler/profile.h"
+#include "video_core/engines/maxwell_3d.h"
+
+namespace VideoCommon {
+
+struct TransformFeedbackState {
+    struct Layout {
+        u32 stream;
+        u32 varying_count;
+        u32 stride;
+    };
+    std::array<Layout, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> layouts;
+    std::array<std::array<u8, 128>, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
+        varyings;
+};
+
+std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
+    const TransformFeedbackState& state);
+
+} // namespace VideoCommon