summaryrefslogtreecommitdiff
path: root/src/video_core/geometry_pipeline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/geometry_pipeline.cpp')
-rw-r--r--src/video_core/geometry_pipeline.cpp274
1 files changed, 0 insertions, 274 deletions
diff --git a/src/video_core/geometry_pipeline.cpp b/src/video_core/geometry_pipeline.cpp
deleted file mode 100644
index 98ff2ccd3..000000000
--- a/src/video_core/geometry_pipeline.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
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(static_cast<float>(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