From 2f1c129f6407fe2d5c8c3e57c6717d5668570de5 Mon Sep 17 00:00:00 2001
From: Tony Wasserka <NeoBrainX@gmail.com>
Date: Sun, 17 Aug 2014 17:44:55 +0200
Subject: [PATCH] Pica: Consolidate the primitive assembly code in
 PrimitiveAssembly and GeometryDumper.

---
 src/video_core/command_processor.cpp       | 18 ++++++++--
 src/video_core/debug_utils/debug_utils.cpp | 22 ++++---------
 src/video_core/debug_utils/debug_utils.h   | 12 +++----
 src/video_core/primitive_assembly.cpp      | 28 +++++++++-------
 src/video_core/primitive_assembly.h        | 38 +++++++++++++++++-----
 5 files changed, 73 insertions(+), 45 deletions(-)

diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 8da030601..9567a9849 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -2,6 +2,7 @@
 // Licensed under GPLv2
 // Refer to the license.txt file included.
 
+#include "clipper.h"
 #include "command_processor.h"
 #include "math.h"
 #include "pica.h"
@@ -79,6 +80,8 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
             bool index_u16 = (bool)index_info.format;
 
             DebugUtils::GeometryDumper geometry_dumper;
+            PrimitiveAssembler<VertexShader::OutputVertex> clipper_primitive_assembler(registers.triangle_topology.Value());
+            PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(registers.triangle_topology.Value());
 
             for (int index = 0; index < registers.num_vertices; ++index)
             {
@@ -108,16 +111,25 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
                     }
                 }
 
-                // NOTE: For now, we simply assume that the first input attribute corresponds to the position.
-                geometry_dumper.AddVertex({input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()}, registers.triangle_topology);
+                // NOTE: When dumping geometry, we simply assume that the first input attribute
+                //       corresponds to the position for now.
+                DebugUtils::GeometryDumper::Vertex dumped_vertex = {
+                    input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()
+                };
+                using namespace std::placeholders;
+                dumping_primitive_assembler.SubmitVertex(dumped_vertex,
+                                                         std::bind(&DebugUtils::GeometryDumper::AddTriangle,
+                                                                   &geometry_dumper, _1, _2, _3));
 
+                // Send to vertex shader
                 VertexShader::OutputVertex output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes());
 
                 if (is_indexed) {
                     // TODO: Add processed vertex to vertex cache!
                 }
 
-                PrimitiveAssembly::SubmitVertex(output);
+                // Send to triangle clipper
+                clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
             }
             geometry_dumper.Dump();
             break;
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index f7d9455be..48e6dd182 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -22,27 +22,17 @@ namespace Pica {
 
 namespace DebugUtils {
 
-void GeometryDumper::AddVertex(std::array<float,3> pos, TriangleTopology topology) {
-    vertices.push_back({pos[0], pos[1], pos[2]});
+void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) {
+    vertices.push_back(v0);
+    vertices.push_back(v1);
+    vertices.push_back(v2);
 
     int num_vertices = vertices.size();
-
-    switch (topology) {
-    case TriangleTopology::List:
-    case TriangleTopology::ListIndexed:
-        if (0 == (num_vertices % 3))
-            faces.push_back({ num_vertices-3, num_vertices-2, num_vertices-1 });
-        break;
-
-    default:
-        ERROR_LOG(GPU, "Unknown triangle topology %x", (int)topology);
-        exit(0);
-        break;
-    }
+    faces.push_back({ num_vertices-3, num_vertices-2, num_vertices-1 });
 }
 
 void GeometryDumper::Dump() {
-    // NOTE: Permanently enabling this just trashes hard disks for no reason.
+    // NOTE: Permanently enabling this just trashes the hard disk for no reason.
     //       Hence, this is currently disabled.
     return;
 
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 53c33c96e..8b1499bf2 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -14,20 +14,18 @@ namespace Pica {
 
 namespace DebugUtils {
 
-using TriangleTopology = Regs::TriangleTopology;
-
 // Simple utility class for dumping geometry data to an OBJ file
 class GeometryDumper {
 public:
-    void AddVertex(std::array<float,3> pos, TriangleTopology topology);
-
-    void Dump();
-
-private:
     struct Vertex {
         std::array<float,3> pos;
     };
 
+    void AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2);
+
+    void Dump();
+
+private:
     struct Face {
         int index[3];
     };
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index 2354ffb99..dabf2d1a3 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -2,21 +2,23 @@
 // Licensed under GPLv2
 // Refer to the license.txt file included.
 
-#include "clipper.h"
 #include "pica.h"
 #include "primitive_assembly.h"
 #include "vertex_shader.h"
 
+#include "video_core/debug_utils/debug_utils.h"
+
 namespace Pica {
 
-namespace PrimitiveAssembly {
+template<typename VertexType>
+PrimitiveAssembler<VertexType>::PrimitiveAssembler(Regs::TriangleTopology topology)
+    : topology(topology), buffer_index(0) {
+}
 
-static OutputVertex buffer[2];
-static int buffer_index = 0; // TODO: reset this on emulation restart
-
-void SubmitVertex(OutputVertex& vtx)
+template<typename VertexType>
+void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler)
 {
-    switch (registers.triangle_topology) {
+    switch (topology) {
         case Regs::TriangleTopology::List:
         case Regs::TriangleTopology::ListIndexed:
             if (buffer_index < 2) {
@@ -24,7 +26,7 @@ void SubmitVertex(OutputVertex& vtx)
             } else {
                 buffer_index = 0;
 
-                Clipper::ProcessTriangle(buffer[0], buffer[1], vtx);
+                triangle_handler(buffer[0], buffer[1], vtx);
             }
             break;
 
@@ -32,7 +34,7 @@ void SubmitVertex(OutputVertex& vtx)
             if (buffer_index == 2) {
                 buffer_index = 0;
 
-                Clipper::ProcessTriangle(buffer[0], buffer[1], vtx);
+                triangle_handler(buffer[0], buffer[1], vtx);
 
                 buffer[1] = vtx;
             } else {
@@ -41,11 +43,15 @@ void SubmitVertex(OutputVertex& vtx)
             break;
 
         default:
-            ERROR_LOG(GPU, "Unknown triangle mode %x:", (int)registers.triangle_topology.Value());
+            ERROR_LOG(GPU, "Unknown triangle topology %x:", (int)topology);
             break;
     }
 }
 
-} // namespace
+// explicitly instantiate use cases
+template
+struct PrimitiveAssembler<VertexShader::OutputVertex>;
+template
+struct PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex>;
 
 } // namespace
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index 2a2b0c170..ea2e2f61e 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -4,18 +4,40 @@
 
 #pragma once
 
+#include <functional>
+
+#include "video_core/pica.h"
+
+#include "video_core/vertex_shader.h"
+
 namespace Pica {
 
-namespace VertexShader {
-    struct OutputVertex;
-}
+/*
+ * Utility class to build triangles from a series of vertices,
+ * according to a given triangle topology.
+ */
+template<typename VertexType>
+struct PrimitiveAssembler {
+    using TriangleHandler = std::function<void(VertexType& v0,
+                                               VertexType& v1,
+                                               VertexType& v2)>;
 
-namespace PrimitiveAssembly {
+    PrimitiveAssembler(Regs::TriangleTopology topology);
 
-using VertexShader::OutputVertex;
+    /*
+     * Queues a vertex, builds primitives from the vertex queue according to the given
+     * triangle topology, and calls triangle_handler for each generated primitive.
+     * NOTE: We could specify the triangle handler in the constructor, but this way we can
+     * keep event and handler code next to each other.
+     */
+    void SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler);
+
+private:
+    Regs::TriangleTopology topology;
+
+    int buffer_index;
+    VertexType buffer[2];
+};
 
-void SubmitVertex(OutputVertex& vtx);
-
-} // namespace
 
 } // namespace