From 0b12e22331951be45043b87a65ae2984d9c35ebf Mon Sep 17 00:00:00 2001
From: Samuliak <samuliak77@gmail.com>
Date: Sat, 5 Oct 2024 19:28:27 +0200
Subject: [PATCH] emit vertex attributes

---
 .../backend/msl/msl_emit_context.cpp          |  26 ++--
 .../backend/msl/msl_emit_context.h            |   2 +-
 .../renderer_metal/mtl_pipeline_cache.cpp     | 125 +++++++++++++++++-
 3 files changed, 137 insertions(+), 16 deletions(-)

diff --git a/src/shader_recompiler/backend/msl/msl_emit_context.cpp b/src/shader_recompiler/backend/msl/msl_emit_context.cpp
index 4f13d732a5..995f6378a6 100644
--- a/src/shader_recompiler/backend/msl/msl_emit_context.cpp
+++ b/src/shader_recompiler/backend/msl/msl_emit_context.cpp
@@ -49,6 +49,7 @@ std::string_view InputArrayDecorator(Stage stage) {
 */
 
 // TODO
+/*
 std::string OutputDecorator(Stage stage, u32 size) {
     switch (stage) {
     case Stage::TessellationControl:
@@ -57,6 +58,7 @@ std::string OutputDecorator(Stage stage, u32 size) {
         return "";
     }
 }
+*/
 
 /*
 // TODO
@@ -240,13 +242,13 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
         stage_name = "vertex";
         break;
     case Stage::TessellationControl:
-        stage_name = "kernel";
+        stage_name = "INVALID";
         break;
     case Stage::TessellationEval:
-        stage_name = "vertex";
+        stage_name = "INVALID";
         break;
     case Stage::Geometry:
-        stage_name = "vertex";
+        stage_name = "INVALID";
         break;
     case Stage::Fragment:
         stage_name = "fragment";
@@ -275,7 +277,11 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
         if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
             continue;
         }
-        DefineStageInOut(index, program.invocations, true);
+        const std::string qualifier{(stage == Stage::VertexA || stage == Stage::VertexB) ? "attribute(" : "user(locn"};
+        // TODO: uncomment
+        header += fmt::format("float4 attr{} [[{}{})]];\n"/*,
+                              InterpDecorator(info.interpolation[index])*/, index/*,
+                              InputArrayDecorator(stage)*/, qualifier, index);
         has_stage_input = true;
     }
     for (size_t index = 0; index < info.uses_patches.size(); ++index) {
@@ -319,7 +325,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
     }
     for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
         if (info.stores.Generic(index)) {
-            DefineStageInOut(index, program.invocations, false);
+            DefineStageOut(index, program.invocations);
         }
     }
     header += "};\n";
@@ -437,14 +443,13 @@ void EmitContext::DefineInputs(Bindings& bindings) {
 }
 
 // TODO
-void EmitContext::DefineStageInOut(size_t index, u32 invocations, bool is_input) {
-    const auto type{fmt::format("float{}", 4)};
+void EmitContext::DefineStageOut(size_t index, u32 invocations) {
     std::string name{fmt::format("attr{}", index)};
-    header += fmt::format("{} {}{} [[user(locn{})]];\n", type, name,
-                          OutputDecorator(stage, invocations), index);
+    header += fmt::format("float4 {} [[user(locn{})]];\n", name/*,
+                          OutputDecorator(stage, invocations)*/, index);
 
     const GenericElementInfo element_info{
-        .name = (is_input ? "__in." : "__out.") + name,
+        .name = "__out." + name,
         .first_element = 0,
         .num_components = 4,
     };
@@ -452,6 +457,7 @@ void EmitContext::DefineStageInOut(size_t index, u32 invocations, bool is_input)
 }
 
 void EmitContext::DefineHelperFunctions() {
+    // TODO: use MSL's extract_bits instead
     header +=
         "uint bitfieldExtract(uint value, int offset, int bits) {\nreturn (value >> offset) & "
         "((1 << bits) - 1);\n}\n";
diff --git a/src/shader_recompiler/backend/msl/msl_emit_context.h b/src/shader_recompiler/backend/msl/msl_emit_context.h
index df00489cc2..20caa2df0b 100644
--- a/src/shader_recompiler/backend/msl/msl_emit_context.h
+++ b/src/shader_recompiler/backend/msl/msl_emit_context.h
@@ -162,7 +162,7 @@ public:
 private:
     // TODO: break down into smaller functions
     void DefineInputs(Bindings& bindings);
-    void DefineStageInOut(size_t index, u32 invocations, bool is_input);
+    void DefineStageOut(size_t index, u32 invocations);
     void DefineHelperFunctions();
     void DefineConstants();
     std::string DefineGlobalMemoryFunctions();
diff --git a/src/video_core/renderer_metal/mtl_pipeline_cache.cpp b/src/video_core/renderer_metal/mtl_pipeline_cache.cpp
index 3ae03289e3..80786e1d1c 100644
--- a/src/video_core/renderer_metal/mtl_pipeline_cache.cpp
+++ b/src/video_core/renderer_metal/mtl_pipeline_cache.cpp
@@ -56,9 +56,124 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
                                     const GraphicsPipelineCacheKey& key,
                                     const Shader::IR::Program& program,
                                     const Shader::IR::Program* previous_program) {
-    Shader::RuntimeInfo info{};
-
-    // TODO: fill in the runtime info
+    Shader::RuntimeInfo info;
+    if (previous_program) {
+        info.previous_stage_stores = previous_program->info.stores;
+        info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping;
+        if (previous_program->is_geometry_passthrough) {
+            info.previous_stage_stores.mask |= previous_program->info.passthrough.mask;
+        }
+    } else {
+        info.previous_stage_stores.mask.set();
+    }
+    // TODO: uncomment
+    /*
+    const Shader::Stage stage{program.stage};
+    const bool has_geometry{key.unique_hashes[4] != 0 && !programs[4].is_geometry_passthrough};
+    const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};
+    const float point_size{Common::BitCast<float>(key.state.point_size)};
+    switch (stage) {
+    case Shader::Stage::VertexB:
+        if (!has_geometry) {
+            if (key.state.topology == Maxwell::PrimitiveTopology::Points) {
+                info.fixed_state_point_size = point_size;
+            }
+            if (key.state.xfb_enabled) {
+                auto [varyings, count] =
+                    VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
+                info.xfb_varyings = varyings;
+                info.xfb_count = count;
+            }
+            info.convert_depth_mode = gl_ndc;
+        }
+        if (key.state.dynamic_vertex_input) {
+            for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
+                info.generic_input_types[index] = AttributeType(key.state, index);
+            }
+        } else {
+            std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
+                                    &CastAttributeType);
+        }
+        break;
+    case Shader::Stage::TessellationEval:
+        info.tess_clockwise = key.state.tessellation_clockwise != 0;
+        info.tess_primitive = [&key] {
+            const u32 raw{key.state.tessellation_primitive.Value()};
+            switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
+            case Maxwell::Tessellation::DomainType::Isolines:
+                return Shader::TessPrimitive::Isolines;
+            case Maxwell::Tessellation::DomainType::Triangles:
+                return Shader::TessPrimitive::Triangles;
+            case Maxwell::Tessellation::DomainType::Quads:
+                return Shader::TessPrimitive::Quads;
+            }
+            ASSERT(false);
+            return Shader::TessPrimitive::Triangles;
+        }();
+        info.tess_spacing = [&] {
+            const u32 raw{key.state.tessellation_spacing};
+            switch (static_cast<Maxwell::Tessellation::Spacing>(raw)) {
+            case Maxwell::Tessellation::Spacing::Integer:
+                return Shader::TessSpacing::Equal;
+            case Maxwell::Tessellation::Spacing::FractionalOdd:
+                return Shader::TessSpacing::FractionalOdd;
+            case Maxwell::Tessellation::Spacing::FractionalEven:
+                return Shader::TessSpacing::FractionalEven;
+            }
+            ASSERT(false);
+            return Shader::TessSpacing::Equal;
+        }();
+        break;
+    case Shader::Stage::Geometry:
+        if (program.output_topology == Shader::OutputTopology::PointList) {
+            info.fixed_state_point_size = point_size;
+        }
+        if (key.state.xfb_enabled != 0) {
+            auto [varyings, count] =
+                VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
+            info.xfb_varyings = varyings;
+            info.xfb_count = count;
+        }
+        info.convert_depth_mode = gl_ndc;
+        break;
+    case Shader::Stage::Fragment:
+        info.alpha_test_func = MaxwellToCompareFunction(
+            key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
+        info.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref);
+        break;
+    default:
+        break;
+    }
+    switch (key.state.topology) {
+    case Maxwell::PrimitiveTopology::Points:
+        info.input_topology = Shader::InputTopology::Points;
+        break;
+    case Maxwell::PrimitiveTopology::Lines:
+    case Maxwell::PrimitiveTopology::LineLoop:
+    case Maxwell::PrimitiveTopology::LineStrip:
+        info.input_topology = Shader::InputTopology::Lines;
+        break;
+    case Maxwell::PrimitiveTopology::Triangles:
+    case Maxwell::PrimitiveTopology::TriangleStrip:
+    case Maxwell::PrimitiveTopology::TriangleFan:
+    case Maxwell::PrimitiveTopology::Quads:
+    case Maxwell::PrimitiveTopology::QuadStrip:
+    case Maxwell::PrimitiveTopology::Polygon:
+    case Maxwell::PrimitiveTopology::Patches:
+        info.input_topology = Shader::InputTopology::Triangles;
+        break;
+    case Maxwell::PrimitiveTopology::LinesAdjacency:
+    case Maxwell::PrimitiveTopology::LineStripAdjacency:
+        info.input_topology = Shader::InputTopology::LinesAdjacency;
+        break;
+    case Maxwell::PrimitiveTopology::TrianglesAdjacency:
+    case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
+        info.input_topology = Shader::InputTopology::TrianglesAdjacency;
+        break;
+    }
+    info.force_early_z = key.state.early_z != 0;
+    info.y_negate = key.state.y_negate != 0;
+    */
 
     return info;
 }
@@ -231,7 +346,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
     auto hash = key.Hash();
     LOG_INFO(Render_Metal, "0x{:016x}", hash);
 
-    // Translate shaders to spirv
+    // Translate shaders
     size_t env_index{0};
     std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
 
@@ -268,7 +383,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
         ConvertLegacyToGeneric(program, runtime_info);
         const std::string code{EmitMSL(profile, runtime_info, program, binding)};
         // HACK
-        std::cout << "SHADER INDEX: " << index - 1 << std::endl;
+        std::cout << "SHADER INDEX: " << stage_index << std::endl;
         std::cout << code << std::endl;
         MTL::CompileOptions* compile_options = MTL::CompileOptions::alloc()->init();
         NS::Error* error = nullptr;