summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar wwylele2017-08-04 17:03:17 +0300
committerGravatar wwylele2017-08-19 10:13:20 +0300
commit0f35755572fe63534813528de9a0710193f2e335 (patch)
treeb0eeedaff959cde5195e1d5cf1a3c784954f5273
parentpica/shader/jit: implement SETEMIT and EMIT (diff)
downloadyuzu-0f35755572fe63534813528de9a0710193f2e335.tar.gz
yuzu-0f35755572fe63534813528de9a0710193f2e335.tar.xz
yuzu-0f35755572fe63534813528de9a0710193f2e335.zip
pica/command_processor: build geometry pipeline and run geometry shader
The geometry pipeline manages data transfer between VS, GS and primitive assembler. It has known four modes: - no GS mode: sends VS output directly to the primitive assembler (what citra currently does) - GS mode 0: sends VS output to GS input registers, and sends GS output to primitive assembler - GS mode 1: sends VS output to GS uniform registers, and sends GS output to primitive assembler. It also takes an index from the index buffer at the beginning of each primitive for determine the primitive size. - GS mode 2: similar to mode 1, but doesn't take the index and uses a fixed primitive size. hwtest shows that immediate mode also supports GS (at least for mode 0), so the geometry pipeline gets refactored into its own class for supporting both drawing mode. In the immediate mode, some games don't set the pipeline registers to a valid value until the first attribute input, so a geometry pipeline reset flag is set in `pipeline.vs_default_attributes_setup.index` trigger, and the actual pipeline reconfigure is triggered in the first attribute input. In the normal drawing mode with index buffer, the vertex cache is a little bit modified to support the geometry pipeline. Instead of OutputVertex, it now holds AttributeBuffer, which is the input to the geometry pipeline. The AttributeBuffer->OutputVertex conversion is done inside the pipeline vertex handler. The actual hardware vertex cache is believed to be implemented in a similar way (because this is the only way that makes sense). Both geometry pipeline and GS unit rely on states preservation across drawing call, so they are put into the global state. In the future, the other three vertex shader units should be also placed in the global state, and a scheduler should be implemented on top of the four units. Note that the current gs_unit already allows running VS on it in the future.
Diffstat (limited to '')
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/command_processor.cpp54
-rw-r--r--src/video_core/geometry_pipeline.cpp274
-rw-r--r--src/video_core/geometry_pipeline.h49
-rw-r--r--src/video_core/pica.cpp21
-rw-r--r--src/video_core/pica_state.h11
6 files changed, 383 insertions, 28 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index cffa4c952..82f47d8a9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,6 +1,7 @@
1set(SRCS 1set(SRCS
2 command_processor.cpp 2 command_processor.cpp
3 debug_utils/debug_utils.cpp 3 debug_utils/debug_utils.cpp
4 geometry_pipeline.cpp
4 pica.cpp 5 pica.cpp
5 primitive_assembly.cpp 6 primitive_assembly.cpp
6 regs.cpp 7 regs.cpp
@@ -29,6 +30,7 @@ set(SRCS
29set(HEADERS 30set(HEADERS
30 command_processor.h 31 command_processor.h
31 debug_utils/debug_utils.h 32 debug_utils/debug_utils.h
33 geometry_pipeline.h
32 gpu_debugger.h 34 gpu_debugger.h
33 pica.h 35 pica.h
34 pica_state.h 36 pica_state.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index f98ca3302..fb65a3a0a 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -161,6 +161,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
161 161
162 case PICA_REG_INDEX(pipeline.vs_default_attributes_setup.index): 162 case PICA_REG_INDEX(pipeline.vs_default_attributes_setup.index):
163 g_state.immediate.current_attribute = 0; 163 g_state.immediate.current_attribute = 0;
164 g_state.immediate.reset_geometry_pipeline = true;
164 default_attr_counter = 0; 165 default_attr_counter = 0;
165 break; 166 break;
166 167
@@ -234,16 +235,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
234 shader_engine->Run(g_state.vs, shader_unit); 235 shader_engine->Run(g_state.vs, shader_unit);
235 shader_unit.WriteOutput(regs.vs, output); 236 shader_unit.WriteOutput(regs.vs, output);
236 237
237 // Send to renderer 238 // Send to geometry pipeline
238 using Pica::Shader::OutputVertex; 239 if (g_state.immediate.reset_geometry_pipeline) {
239 auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1, 240 g_state.geometry_pipeline.Reconfigure();
240 const OutputVertex& v2) { 241 g_state.immediate.reset_geometry_pipeline = false;
241 VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2); 242 }
242 }; 243 ASSERT(!g_state.geometry_pipeline.NeedIndexInput());
243 244 g_state.geometry_pipeline.Setup(shader_engine);
244 g_state.primitive_assembler.SubmitVertex( 245 g_state.geometry_pipeline.SubmitVertex(output);
245 Shader::OutputVertex::FromAttributeBuffer(regs.rasterizer, output),
246 AddTriangle);
247 } 246 }
248 } 247 }
249 } 248 }
@@ -321,8 +320,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
321 // The size has been tuned for optimal balance between hit-rate and the cost of lookup 320 // The size has been tuned for optimal balance between hit-rate and the cost of lookup
322 const size_t VERTEX_CACHE_SIZE = 32; 321 const size_t VERTEX_CACHE_SIZE = 32;
323 std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids; 322 std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
324 std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache; 323 std::array<Shader::AttributeBuffer, VERTEX_CACHE_SIZE> vertex_cache;
325 Shader::OutputVertex output_vertex; 324 Shader::AttributeBuffer vs_output;
326 325
327 unsigned int vertex_cache_pos = 0; 326 unsigned int vertex_cache_pos = 0;
328 vertex_cache_ids.fill(-1); 327 vertex_cache_ids.fill(-1);
@@ -332,6 +331,11 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
332 331
333 shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset); 332 shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
334 333
334 g_state.geometry_pipeline.Reconfigure();
335 g_state.geometry_pipeline.Setup(shader_engine);
336 if (g_state.geometry_pipeline.NeedIndexInput())
337 ASSERT(is_indexed);
338
335 for (unsigned int index = 0; index < regs.pipeline.num_vertices; ++index) { 339 for (unsigned int index = 0; index < regs.pipeline.num_vertices; ++index) {
336 // Indexed rendering doesn't use the start offset 340 // Indexed rendering doesn't use the start offset
337 unsigned int vertex = 341 unsigned int vertex =
@@ -345,6 +349,11 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
345 bool vertex_cache_hit = false; 349 bool vertex_cache_hit = false;
346 350
347 if (is_indexed) { 351 if (is_indexed) {
352 if (g_state.geometry_pipeline.NeedIndexInput()) {
353 g_state.geometry_pipeline.SubmitIndex(vertex);
354 continue;
355 }
356
348 if (g_debug_context && Pica::g_debug_context->recorder) { 357 if (g_debug_context && Pica::g_debug_context->recorder) {
349 int size = index_u16 ? 2 : 1; 358 int size = index_u16 ? 2 : 1;
350 memory_accesses.AddAccess(base_address + index_info.offset + size * index, 359 memory_accesses.AddAccess(base_address + index_info.offset + size * index,
@@ -353,7 +362,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
353 362
354 for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) { 363 for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
355 if (vertex == vertex_cache_ids[i]) { 364 if (vertex == vertex_cache_ids[i]) {
356 output_vertex = vertex_cache[i]; 365 vs_output = vertex_cache[i];
357 vertex_cache_hit = true; 366 vertex_cache_hit = true;
358 break; 367 break;
359 } 368 }
@@ -362,7 +371,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
362 371
363 if (!vertex_cache_hit) { 372 if (!vertex_cache_hit) {
364 // Initialize data for the current vertex 373 // Initialize data for the current vertex
365 Shader::AttributeBuffer input, output{}; 374 Shader::AttributeBuffer input;
366 loader.LoadVertex(base_address, index, vertex, input, memory_accesses); 375 loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
367 376
368 // Send to vertex shader 377 // Send to vertex shader
@@ -371,26 +380,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
371 (void*)&input); 380 (void*)&input);
372 shader_unit.LoadInput(regs.vs, input); 381 shader_unit.LoadInput(regs.vs, input);
373 shader_engine->Run(g_state.vs, shader_unit); 382 shader_engine->Run(g_state.vs, shader_unit);
374 shader_unit.WriteOutput(regs.vs, output); 383 shader_unit.WriteOutput(regs.vs, vs_output);
375
376 // Retrieve vertex from register data
377 output_vertex = Shader::OutputVertex::FromAttributeBuffer(regs.rasterizer, output);
378 384
379 if (is_indexed) { 385 if (is_indexed) {
380 vertex_cache[vertex_cache_pos] = output_vertex; 386 vertex_cache[vertex_cache_pos] = vs_output;
381 vertex_cache_ids[vertex_cache_pos] = vertex; 387 vertex_cache_ids[vertex_cache_pos] = vertex;
382 vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE; 388 vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
383 } 389 }
384 } 390 }
385 391
386 // Send to renderer 392 // Send to geometry pipeline
387 using Pica::Shader::OutputVertex; 393 g_state.geometry_pipeline.SubmitVertex(vs_output);
388 auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1,
389 const OutputVertex& v2) {
390 VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
391 };
392
393 primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
394 } 394 }
395 395
396 for (auto& range : memory_accesses.ranges) { 396 for (auto& range : memory_accesses.ranges) {
diff --git a/src/video_core/geometry_pipeline.cpp b/src/video_core/geometry_pipeline.cpp
new file mode 100644
index 000000000..b146e2ecb
--- /dev/null
+++ b/src/video_core/geometry_pipeline.cpp
@@ -0,0 +1,274 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/geometry_pipeline.h"
6#include "video_core/pica_state.h"
7#include "video_core/regs.h"
8#include "video_core/renderer_base.h"
9#include "video_core/video_core.h"
10
11namespace Pica {
12
13/// An attribute buffering interface for different pipeline modes
14class GeometryPipelineBackend {
15public:
16 virtual ~GeometryPipelineBackend() = default;
17
18 /// Checks if there is no incomplete data transfer
19 virtual bool IsEmpty() const = 0;
20
21 /// Checks if the pipeline needs a direct input from index buffer
22 virtual bool NeedIndexInput() const = 0;
23
24 /// Submits an index from index buffer
25 virtual void SubmitIndex(unsigned int val) = 0;
26
27 /**
28 * Submits vertex attributes
29 * @param input attributes of a vertex output from vertex shader
30 * @return if the buffer is full and the geometry shader should be invoked
31 */
32 virtual bool SubmitVertex(const Shader::AttributeBuffer& input) = 0;
33};
34
35// In the Point mode, vertex attributes are sent to the input registers in the geometry shader unit.
36// The size of vertex shader outputs and geometry shader inputs are constants. Geometry shader is
37// invoked upon inputs buffer filled up by vertex shader outputs. For example, if we have a geometry
38// shader that takes 6 inputs, and the vertex shader outputs 2 attributes, it would take 3 vertices
39// for one geometry shader invocation.
40// TODO: what happens when the input size is not divisible by the output size?
41class GeometryPipeline_Point : public GeometryPipelineBackend {
42public:
43 GeometryPipeline_Point(const Regs& regs, Shader::GSUnitState& unit) : regs(regs), unit(unit) {
44 ASSERT(regs.pipeline.variable_primitive == 0);
45 ASSERT(regs.gs.input_to_uniform == 0);
46 vs_output_num = regs.pipeline.vs_outmap_total_minus_1_a + 1;
47 size_t gs_input_num = regs.gs.max_input_attribute_index + 1;
48 ASSERT(gs_input_num % vs_output_num == 0);
49 buffer_cur = attribute_buffer.attr;
50 buffer_end = attribute_buffer.attr + gs_input_num;
51 }
52
53 bool IsEmpty() const override {
54 return buffer_cur == attribute_buffer.attr;
55 }
56
57 bool NeedIndexInput() const override {
58 return false;
59 }
60
61 void SubmitIndex(unsigned int val) override {
62 UNREACHABLE();
63 }
64
65 bool SubmitVertex(const Shader::AttributeBuffer& input) override {
66 buffer_cur = std::copy(input.attr, input.attr + vs_output_num, buffer_cur);
67 if (buffer_cur == buffer_end) {
68 buffer_cur = attribute_buffer.attr;
69 unit.LoadInput(regs.gs, attribute_buffer);
70 return true;
71 }
72 return false;
73 }
74
75private:
76 const Regs& regs;
77 Shader::GSUnitState& unit;
78 Shader::AttributeBuffer attribute_buffer;
79 Math::Vec4<float24>* buffer_cur;
80 Math::Vec4<float24>* buffer_end;
81 unsigned int vs_output_num;
82};
83
84// In VariablePrimitive mode, vertex attributes are buffered into the uniform registers in the
85// geometry shader unit. The number of vertex is variable, which is specified by the first index
86// value in the batch. This mode is usually used for subdivision.
87class GeometryPipeline_VariablePrimitive : public GeometryPipelineBackend {
88public:
89 GeometryPipeline_VariablePrimitive(const Regs& regs, Shader::ShaderSetup& setup)
90 : regs(regs), setup(setup) {
91 ASSERT(regs.pipeline.variable_primitive == 1);
92 ASSERT(regs.gs.input_to_uniform == 1);
93 vs_output_num = regs.pipeline.vs_outmap_total_minus_1_a + 1;
94 }
95
96 bool IsEmpty() const override {
97 return need_index;
98 }
99
100 bool NeedIndexInput() const override {
101 return need_index;
102 }
103
104 void SubmitIndex(unsigned int val) override {
105 DEBUG_ASSERT(need_index);
106
107 // The number of vertex input is put to the uniform register
108 float24 vertex_num = float24::FromFloat32(val);
109 setup.uniforms.f[0] = Math::MakeVec(vertex_num, vertex_num, vertex_num, vertex_num);
110
111 // The second uniform register and so on are used for receiving input vertices
112 buffer_cur = setup.uniforms.f + 1;
113
114 main_vertex_num = regs.pipeline.variable_vertex_main_num_minus_1 + 1;
115 total_vertex_num = val;
116 need_index = false;
117 }
118
119 bool SubmitVertex(const Shader::AttributeBuffer& input) override {
120 DEBUG_ASSERT(!need_index);
121 if (main_vertex_num != 0) {
122 // For main vertices, receive all attributes
123 buffer_cur = std::copy(input.attr, input.attr + vs_output_num, buffer_cur);
124 --main_vertex_num;
125 } else {
126 // For other vertices, only receive the first attribute (usually the position)
127 *(buffer_cur++) = input.attr[0];
128 }
129 --total_vertex_num;
130
131 if (total_vertex_num == 0) {
132 need_index = true;
133 return true;
134 }
135
136 return false;
137 }
138
139private:
140 bool need_index = true;
141 const Regs& regs;
142 Shader::ShaderSetup& setup;
143 unsigned int main_vertex_num;
144 unsigned int total_vertex_num;
145 Math::Vec4<float24>* buffer_cur;
146 unsigned int vs_output_num;
147};
148
149// In FixedPrimitive mode, vertex attributes are buffered into the uniform registers in the geometry
150// shader unit. The number of vertex per shader invocation is constant. This is usually used for
151// particle system.
152class GeometryPipeline_FixedPrimitive : public GeometryPipelineBackend {
153public:
154 GeometryPipeline_FixedPrimitive(const Regs& regs, Shader::ShaderSetup& setup)
155 : regs(regs), setup(setup) {
156 ASSERT(regs.pipeline.variable_primitive == 0);
157 ASSERT(regs.gs.input_to_uniform == 1);
158 vs_output_num = regs.pipeline.vs_outmap_total_minus_1_a + 1;
159 ASSERT(vs_output_num == regs.pipeline.gs_config.stride_minus_1 + 1);
160 size_t vertex_num = regs.pipeline.gs_config.fixed_vertex_num_minus_1 + 1;
161 buffer_cur = buffer_begin = setup.uniforms.f + regs.pipeline.gs_config.start_index;
162 buffer_end = buffer_begin + vs_output_num * vertex_num;
163 }
164
165 bool IsEmpty() const override {
166 return buffer_cur == buffer_begin;
167 }
168
169 bool NeedIndexInput() const override {
170 return false;
171 }
172
173 void SubmitIndex(unsigned int val) override {
174 UNREACHABLE();
175 }
176
177 bool SubmitVertex(const Shader::AttributeBuffer& input) override {
178 buffer_cur = std::copy(input.attr, input.attr + vs_output_num, buffer_cur);
179 if (buffer_cur == buffer_end) {
180 buffer_cur = buffer_begin;
181 return true;
182 }
183 return false;
184 }
185
186private:
187 const Regs& regs;
188 Shader::ShaderSetup& setup;
189 Math::Vec4<float24>* buffer_begin;
190 Math::Vec4<float24>* buffer_cur;
191 Math::Vec4<float24>* buffer_end;
192 unsigned int vs_output_num;
193};
194
195GeometryPipeline::GeometryPipeline(State& state) : state(state) {}
196
197GeometryPipeline::~GeometryPipeline() = default;
198
199void GeometryPipeline::SetVertexHandler(Shader::VertexHandler vertex_handler) {
200 this->vertex_handler = vertex_handler;
201}
202
203void GeometryPipeline::Setup(Shader::ShaderEngine* shader_engine) {
204 if (!backend)
205 return;
206
207 this->shader_engine = shader_engine;
208 shader_engine->SetupBatch(state.gs, state.regs.gs.main_offset);
209}
210
211void GeometryPipeline::Reconfigure() {
212 ASSERT(!backend || backend->IsEmpty());
213
214 if (state.regs.pipeline.use_gs == PipelineRegs::UseGS::No) {
215 backend = nullptr;
216 return;
217 }
218
219 ASSERT(state.regs.pipeline.use_gs == PipelineRegs::UseGS::Yes);
220
221 // The following assumes that when geometry shader is in use, the shader unit 3 is configured as
222 // a geometry shader unit.
223 // TODO: what happens if this is not true?
224 ASSERT(state.regs.pipeline.gs_unit_exclusive_configuration == 1);
225 ASSERT(state.regs.gs.shader_mode == ShaderRegs::ShaderMode::GS);
226
227 state.gs_unit.ConfigOutput(state.regs.gs);
228
229 ASSERT(state.regs.pipeline.vs_outmap_total_minus_1_a ==
230 state.regs.pipeline.vs_outmap_total_minus_1_b);
231
232 switch (state.regs.pipeline.gs_config.mode) {
233 case PipelineRegs::GSMode::Point:
234 backend = std::make_unique<GeometryPipeline_Point>(state.regs, state.gs_unit);
235 break;
236 case PipelineRegs::GSMode::VariablePrimitive:
237 backend = std::make_unique<GeometryPipeline_VariablePrimitive>(state.regs, state.gs);
238 break;
239 case PipelineRegs::GSMode::FixedPrimitive:
240 backend = std::make_unique<GeometryPipeline_FixedPrimitive>(state.regs, state.gs);
241 break;
242 default:
243 UNREACHABLE();
244 }
245}
246
247bool GeometryPipeline::NeedIndexInput() const {
248 if (!backend)
249 return false;
250 return backend->NeedIndexInput();
251}
252
253void GeometryPipeline::SubmitIndex(unsigned int val) {
254 backend->SubmitIndex(val);
255}
256
257void GeometryPipeline::SubmitVertex(const Shader::AttributeBuffer& input) {
258 if (!backend) {
259 // No backend means the geometry shader is disabled, so we send the vertex shader output
260 // directly to the primitive assembler.
261 vertex_handler(input);
262 } else {
263 if (backend->SubmitVertex(input)) {
264 shader_engine->Run(state.gs, state.gs_unit);
265
266 // The uniform b15 is set to true after every geometry shader invocation. This is useful
267 // for the shader to know if this is the first invocation in a batch, if the program set
268 // b15 to false first.
269 state.gs.uniforms.b[15] = true;
270 }
271 }
272}
273
274} // namespace Pica
diff --git a/src/video_core/geometry_pipeline.h b/src/video_core/geometry_pipeline.h
new file mode 100644
index 000000000..91fdd3192
--- /dev/null
+++ b/src/video_core/geometry_pipeline.h
@@ -0,0 +1,49 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "video_core/shader/shader.h"
9
10namespace Pica {
11
12struct State;
13
14class GeometryPipelineBackend;
15
16/// A pipeline receiving from vertex shader and sending to geometry shader and primitive assembler
17class GeometryPipeline {
18public:
19 explicit GeometryPipeline(State& state);
20 ~GeometryPipeline();
21
22 /// Sets the handler for receiving vertex outputs from vertex shader
23 void SetVertexHandler(Shader::VertexHandler vertex_handler);
24
25 /**
26 * Setup the geometry shader unit if it is in use
27 * @param shader_engine the shader engine for the geometry shader to run
28 */
29 void Setup(Shader::ShaderEngine* shader_engine);
30
31 /// Reconfigures the pipeline according to current register settings
32 void Reconfigure();
33
34 /// Checks if the pipeline needs a direct input from index buffer
35 bool NeedIndexInput() const;
36
37 /// Submits an index from index buffer. Call this only when NeedIndexInput returns true
38 void SubmitIndex(unsigned int val);
39
40 /// Submits vertex attributes output from vertex shader
41 void SubmitVertex(const Shader::AttributeBuffer& input);
42
43private:
44 Shader::VertexHandler vertex_handler;
45 Shader::ShaderEngine* shader_engine;
46 std::unique_ptr<GeometryPipelineBackend> backend;
47 State& state;
48};
49} // namespace Pica
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index b95148a6a..218e06883 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include "video_core/geometry_pipeline.h"
6#include "video_core/pica.h" 7#include "video_core/pica.h"
7#include "video_core/pica_state.h" 8#include "video_core/pica_state.h"
8#include "video_core/regs_pipeline.h" 9#include "video_core/renderer_base.h"
10#include "video_core/video_core.h"
9 11
10namespace Pica { 12namespace Pica {
11 13
@@ -24,6 +26,23 @@ void Zero(T& o) {
24 memset(&o, 0, sizeof(o)); 26 memset(&o, 0, sizeof(o));
25} 27}
26 28
29State::State() : geometry_pipeline(*this) {
30 auto SubmitVertex = [this](const Shader::AttributeBuffer& vertex) {
31 using Pica::Shader::OutputVertex;
32 auto AddTriangle = [this](const OutputVertex& v0, const OutputVertex& v1,
33 const OutputVertex& v2) {
34 VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
35 };
36 primitive_assembler.SubmitVertex(
37 Shader::OutputVertex::FromAttributeBuffer(regs.rasterizer, vertex), AddTriangle);
38 };
39
40 auto SetWinding = [this]() { primitive_assembler.SetWinding(); };
41
42 g_state.gs_unit.SetVertexHandler(SubmitVertex, SetWinding);
43 g_state.geometry_pipeline.SetVertexHandler(SubmitVertex);
44}
45
27void State::Reset() { 46void State::Reset() {
28 Zero(regs); 47 Zero(regs);
29 Zero(vs); 48 Zero(vs);
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index 864a2c9e6..c6634a0bc 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -8,6 +8,7 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/vector_math.h" 10#include "common/vector_math.h"
11#include "video_core/geometry_pipeline.h"
11#include "video_core/primitive_assembly.h" 12#include "video_core/primitive_assembly.h"
12#include "video_core/regs.h" 13#include "video_core/regs.h"
13#include "video_core/shader/shader.h" 14#include "video_core/shader/shader.h"
@@ -16,6 +17,7 @@ namespace Pica {
16 17
17/// Struct used to describe current Pica state 18/// Struct used to describe current Pica state
18struct State { 19struct State {
20 State();
19 void Reset(); 21 void Reset();
20 22
21 /// Pica registers 23 /// Pica registers
@@ -137,8 +139,17 @@ struct State {
137 Shader::AttributeBuffer input_vertex; 139 Shader::AttributeBuffer input_vertex;
138 // Index of the next attribute to be loaded into `input_vertex`. 140 // Index of the next attribute to be loaded into `input_vertex`.
139 u32 current_attribute = 0; 141 u32 current_attribute = 0;
142 // Indicates the immediate mode just started and the geometry pipeline needs to reconfigure
143 bool reset_geometry_pipeline = true;
140 } immediate; 144 } immediate;
141 145
146 // the geometry shader needs to be kept in the global state because some shaders relie on
147 // preserved register value across shader invocation.
148 // TODO: also bring the three vertex shader units here and implement the shader scheduler.
149 Shader::GSUnitState gs_unit;
150
151 GeometryPipeline geometry_pipeline;
152
142 // This is constructed with a dummy triangle topology 153 // This is constructed with a dummy triangle topology
143 PrimitiveAssembler<Shader::OutputVertex> primitive_assembler; 154 PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
144}; 155};