From 0c447e0a067c0445f45f333ba35597dbc6a5b656 Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Sat, 5 Mar 2016 18:09:55 -0800
Subject: [PATCH 1/2] OpenGL: Don't attempt to draw empty triangle batches

Our code did not handle this well, causing random crashes in some
situations.
---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b3dc6aa19..1fadcf5ae 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -190,6 +190,9 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
 }
 
 void RasterizerOpenGL::DrawTriangles() {
+    if (vertex_batch.empty())
+        return;
+
     SyncFramebuffer();
     SyncDrawState();
 

From 81004211dda74390c02973c37e89215f5ff8829b Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Sat, 5 Mar 2016 14:49:23 -0800
Subject: [PATCH 2/2] Pica: Improve accuracy of immediate-mode support

This partially fixes Etrian Odyssey IV.
---
 src/video_core/command_processor.cpp | 46 ++++++++++++++++------------
 src/video_core/pica.cpp              | 15 ++++++++-
 src/video_core/pica.h                |  7 ++++-
 src/video_core/pica_state.h          | 15 +++++----
 src/video_core/primitive_assembly.h  |  2 +-
 5 files changed, 56 insertions(+), 29 deletions(-)

diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 4b59984ad..028b59348 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -75,12 +75,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
             GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
             break;
 
+        case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E):
+            g_state.primitive_assembler.Reconfigure(regs.triangle_topology);
+            break;
+
+        case PICA_REG_INDEX_WORKAROUND(restart_primitive, 0x25F):
+            g_state.primitive_assembler.Reset();
+            break;
+
         case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
-            if (regs.vs_default_attributes_setup.index == 15) {
-                // Reset immediate primitive state
-                g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
-                g_state.immediate.attribute_id = 0;
-            }
+            g_state.immediate.current_attribute = 0;
+            default_attr_counter = 0;
             break;
 
         // Load default vertex input attributes
@@ -105,7 +110,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
                     break;
                 }
 
-                Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
+                Math::Vec4<float24> attribute;
 
                 // NOTE: The destination component order indeed is "backwards"
                 attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
@@ -119,26 +124,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
 
                 // TODO: Verify that this actually modifies the register!
                 if (setup.index < 15) {
+                    g_state.vs.default_attributes[setup.index] = attribute;
                     setup.index++;
                 } else {
                     // Put each attribute into an immediate input buffer.
                     // When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
                     // sent to the primitive assembler.
 
-                    auto& immediate_input = g_state.immediate.input;
-                    auto& immediate_attribute_id = g_state.immediate.attribute_id;
-                    const auto& attribute_config = regs.vertex_attributes;
+                    auto& immediate_input = g_state.immediate.input_vertex;
+                    auto& immediate_attribute_id = g_state.immediate.current_attribute;
 
                     immediate_input.attr[immediate_attribute_id++] = attribute;
 
-                    if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) {
+                    if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
                         immediate_attribute_id = 0;
 
                         Shader::UnitState<false> shader_unit;
                         Shader::Setup(shader_unit);
 
+                        if (g_debug_context)
+                            g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
+
                         // Send to vertex shader
-                        Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes());
+                        Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
 
                         // Send to renderer
                         using Pica::Shader::OutputVertex;
@@ -146,7 +154,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
                             VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
                         };
 
-                        g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle);
+                        g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
                     }
                 }
             }
@@ -154,9 +162,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
         }
 
         case PICA_REG_INDEX(gpu_mode):
-            if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) {
+            if (regs.gpu_mode == Regs::GPUMode::Configuring) {
                 // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
                 VideoCore::g_renderer->Rasterizer()->DrawTriangles();
+
+                if (g_debug_context) {
+                    g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
+                }
             }
             break;
 
@@ -241,7 +253,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
             DebugUtils::GeometryDumper geometry_dumper;
             PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
 #endif
-            PrimitiveAssembler<Shader::OutputVertex> primitive_assembler(regs.triangle_topology.Value());
+            PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
 
             if (g_debug_context) {
                 for (int i = 0; i < 3; ++i) {
@@ -412,16 +424,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
                                                           range.second, range.first);
             }
 
-            VideoCore::g_renderer->Rasterizer()->DrawTriangles();
-
 #if PICA_DUMP_GEOMETRY
             geometry_dumper.Dump();
 #endif
 
-            if (g_debug_context) {
-                g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
-            }
-
             break;
         }
 
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index 32ad72674..ccbaf071b 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -493,12 +493,25 @@ std::string Regs::GetCommandName(int index) {
 }
 
 void Init() {
+    g_state.Reset();
 }
 
 void Shutdown() {
     Shader::Shutdown();
+}
 
-    memset(&g_state, 0, sizeof(State));
+template <typename T>
+void Zero(T& o) {
+    memset(&o, 0, sizeof(o));
+}
+
+void State::Reset() {
+    Zero(regs);
+    Zero(vs);
+    Zero(gs);
+    Zero(cmd_list);
+    Zero(immediate);
+    primitive_assembler.Reconfigure(Regs::TriangleTopology::List);
 }
 
 }
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 4b783ac6b..16f9e4006 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -1123,7 +1123,12 @@ struct Regs {
             BitField<24, 8, u32> w;
         } int_uniforms[4];
 
-        INSERT_PADDING_WORDS(0x5);
+        INSERT_PADDING_WORDS(0x4);
+
+        union {
+            // Number of input attributes to shader unit - 1
+            BitField<0, 4, u32> num_input_attributes;
+        };
 
         // Offset to shader program entry point (in words)
         BitField<0, 16, u32> main_offset;
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index c7616bc55..323290054 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -12,6 +12,8 @@ namespace Pica {
 
 /// Struct used to describe current Pica state
 struct State {
+    void Reset();
+
     /// Pica registers
     Regs regs;
 
@@ -46,13 +48,14 @@ struct State {
 
     /// Struct used to describe immediate mode rendering state
     struct ImmediateModeState {
-        Shader::InputVertex input;
-        // This is constructed with a dummy triangle topology
-        PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
-        int attribute_id = 0;
-
-        ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
+        // Used to buffer partial vertices for immediate-mode rendering.
+        Shader::InputVertex input_vertex;
+        // Index of the next attribute to be loaded into `input_vertex`.
+        int current_attribute = 0;
     } immediate;
+
+    // This is constructed with a dummy triangle topology
+    PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
 };
 
 extern State g_state; ///< Current Pica state
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index cc6e5fde5..9396b4c85 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -20,7 +20,7 @@ struct PrimitiveAssembler {
                                                VertexType& v1,
                                                VertexType& v2)>;
 
-    PrimitiveAssembler(Regs::TriangleTopology topology);
+    PrimitiveAssembler(Regs::TriangleTopology topology = Regs::TriangleTopology::List);
 
     /*
      * Queues a vertex, builds primitives from the vertex queue according to the given