committed by
GitHub
15 changed files with 594 additions and 37 deletions
-
2src/video_core/CMakeLists.txt
-
54src/video_core/command_processor.cpp
-
274src/video_core/geometry_pipeline.cpp
-
49src/video_core/geometry_pipeline.h
-
21src/video_core/pica.cpp
-
11src/video_core/pica_state.h
-
15src/video_core/primitive_assembly.cpp
-
7src/video_core/primitive_assembly.h
-
34src/video_core/regs_pipeline.h
-
7src/video_core/regs_shader.h
-
41src/video_core/shader/shader.cpp
-
49src/video_core/shader/shader.h
-
16src/video_core/shader/shader_interpreter.cpp
-
49src/video_core/shader/shader_jit_x64_compiler.cpp
-
2src/video_core/shader/shader_jit_x64_compiler.h
@ -0,0 +1,274 @@ |
|||
// Copyright 2017 Citra Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "video_core/geometry_pipeline.h"
|
|||
#include "video_core/pica_state.h"
|
|||
#include "video_core/regs.h"
|
|||
#include "video_core/renderer_base.h"
|
|||
#include "video_core/video_core.h"
|
|||
|
|||
namespace Pica { |
|||
|
|||
/// An attribute buffering interface for different pipeline modes
|
|||
class GeometryPipelineBackend { |
|||
public: |
|||
virtual ~GeometryPipelineBackend() = default; |
|||
|
|||
/// Checks if there is no incomplete data transfer
|
|||
virtual bool IsEmpty() const = 0; |
|||
|
|||
/// Checks if the pipeline needs a direct input from index buffer
|
|||
virtual bool NeedIndexInput() const = 0; |
|||
|
|||
/// Submits an index from index buffer
|
|||
virtual void SubmitIndex(unsigned int val) = 0; |
|||
|
|||
/**
|
|||
* Submits vertex attributes |
|||
* @param input attributes of a vertex output from vertex shader |
|||
* @return if the buffer is full and the geometry shader should be invoked |
|||
*/ |
|||
virtual bool SubmitVertex(const Shader::AttributeBuffer& input) = 0; |
|||
}; |
|||
|
|||
// In the Point mode, vertex attributes are sent to the input registers in the geometry shader unit.
|
|||
// The size of vertex shader outputs and geometry shader inputs are constants. Geometry shader is
|
|||
// invoked upon inputs buffer filled up by vertex shader outputs. For example, if we have a geometry
|
|||
// shader that takes 6 inputs, and the vertex shader outputs 2 attributes, it would take 3 vertices
|
|||
// for one geometry shader invocation.
|
|||
// TODO: what happens when the input size is not divisible by the output size?
|
|||
class GeometryPipeline_Point : public GeometryPipelineBackend { |
|||
public: |
|||
GeometryPipeline_Point(const Regs& regs, Shader::GSUnitState& unit) : regs(regs), unit(unit) { |
|||
ASSERT(regs.pipeline.variable_primitive == 0); |
|||
ASSERT(regs.gs.input_to_uniform == 0); |
|||
vs_output_num = regs.pipeline.vs_outmap_total_minus_1_a + 1; |
|||
size_t gs_input_num = regs.gs.max_input_attribute_index + 1; |
|||
ASSERT(gs_input_num % vs_output_num == 0); |
|||
buffer_cur = attribute_buffer.attr; |
|||
buffer_end = attribute_buffer.attr + gs_input_num; |
|||
} |
|||
|
|||
bool IsEmpty() const override { |
|||
return buffer_cur == attribute_buffer.attr; |
|||
} |
|||
|
|||
bool NeedIndexInput() const override { |
|||
return false; |
|||
} |
|||
|
|||
void SubmitIndex(unsigned int val) override { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
bool SubmitVertex(const Shader::AttributeBuffer& input) override { |
|||
buffer_cur = std::copy(input.attr, input.attr + vs_output_num, buffer_cur); |
|||
if (buffer_cur == buffer_end) { |
|||
buffer_cur = attribute_buffer.attr; |
|||
unit.LoadInput(regs.gs, attribute_buffer); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private: |
|||
const Regs& regs; |
|||
Shader::GSUnitState& unit; |
|||
Shader::AttributeBuffer attribute_buffer; |
|||
Math::Vec4<float24>* buffer_cur; |
|||
Math::Vec4<float24>* buffer_end; |
|||
unsigned int vs_output_num; |
|||
}; |
|||
|
|||
// In VariablePrimitive mode, vertex attributes are buffered into the uniform registers in the
|
|||
// geometry shader unit. The number of vertex is variable, which is specified by the first index
|
|||
// value in the batch. This mode is usually used for subdivision.
|
|||
class GeometryPipeline_VariablePrimitive : public GeometryPipelineBackend { |
|||
public: |
|||
GeometryPipeline_VariablePrimitive(const Regs& regs, Shader::ShaderSetup& setup) |
|||
: regs(regs), setup(setup) { |
|||
ASSERT(regs.pipeline.variable_primitive == 1); |
|||
ASSERT(regs.gs.input_to_uniform == 1); |
|||
vs_output_num = regs.pipeline.vs_outmap_total_minus_1_a + 1; |
|||
} |
|||
|
|||
bool IsEmpty() const override { |
|||
return need_index; |
|||
} |
|||
|
|||
bool NeedIndexInput() const override { |
|||
return need_index; |
|||
} |
|||
|
|||
void SubmitIndex(unsigned int val) override { |
|||
DEBUG_ASSERT(need_index); |
|||
|
|||
// The number of vertex input is put to the uniform register
|
|||
float24 vertex_num = float24::FromFloat32(val); |
|||
setup.uniforms.f[0] = Math::MakeVec(vertex_num, vertex_num, vertex_num, vertex_num); |
|||
|
|||
// The second uniform register and so on are used for receiving input vertices
|
|||
buffer_cur = setup.uniforms.f + 1; |
|||
|
|||
main_vertex_num = regs.pipeline.variable_vertex_main_num_minus_1 + 1; |
|||
total_vertex_num = val; |
|||
need_index = false; |
|||
} |
|||
|
|||
bool SubmitVertex(const Shader::AttributeBuffer& input) override { |
|||
DEBUG_ASSERT(!need_index); |
|||
if (main_vertex_num != 0) { |
|||
// For main vertices, receive all attributes
|
|||
buffer_cur = std::copy(input.attr, input.attr + vs_output_num, buffer_cur); |
|||
--main_vertex_num; |
|||
} else { |
|||
// For other vertices, only receive the first attribute (usually the position)
|
|||
*(buffer_cur++) = input.attr[0]; |
|||
} |
|||
--total_vertex_num; |
|||
|
|||
if (total_vertex_num == 0) { |
|||
need_index = true; |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private: |
|||
bool need_index = true; |
|||
const Regs& regs; |
|||
Shader::ShaderSetup& setup; |
|||
unsigned int main_vertex_num; |
|||
unsigned int total_vertex_num; |
|||
Math::Vec4<float24>* buffer_cur; |
|||
unsigned int vs_output_num; |
|||
}; |
|||
|
|||
// In FixedPrimitive mode, vertex attributes are buffered into the uniform registers in the geometry
|
|||
// shader unit. The number of vertex per shader invocation is constant. This is usually used for
|
|||
// particle system.
|
|||
class GeometryPipeline_FixedPrimitive : public GeometryPipelineBackend { |
|||
public: |
|||
GeometryPipeline_FixedPrimitive(const Regs& regs, Shader::ShaderSetup& setup) |
|||
: regs(regs), setup(setup) { |
|||
ASSERT(regs.pipeline.variable_primitive == 0); |
|||
ASSERT(regs.gs.input_to_uniform == 1); |
|||
vs_output_num = regs.pipeline.vs_outmap_total_minus_1_a + 1; |
|||
ASSERT(vs_output_num == regs.pipeline.gs_config.stride_minus_1 + 1); |
|||
size_t vertex_num = regs.pipeline.gs_config.fixed_vertex_num_minus_1 + 1; |
|||
buffer_cur = buffer_begin = setup.uniforms.f + regs.pipeline.gs_config.start_index; |
|||
buffer_end = buffer_begin + vs_output_num * vertex_num; |
|||
} |
|||
|
|||
bool IsEmpty() const override { |
|||
return buffer_cur == buffer_begin; |
|||
} |
|||
|
|||
bool NeedIndexInput() const override { |
|||
return false; |
|||
} |
|||
|
|||
void SubmitIndex(unsigned int val) override { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
bool SubmitVertex(const Shader::AttributeBuffer& input) override { |
|||
buffer_cur = std::copy(input.attr, input.attr + vs_output_num, buffer_cur); |
|||
if (buffer_cur == buffer_end) { |
|||
buffer_cur = buffer_begin; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private: |
|||
const Regs& regs; |
|||
Shader::ShaderSetup& setup; |
|||
Math::Vec4<float24>* buffer_begin; |
|||
Math::Vec4<float24>* buffer_cur; |
|||
Math::Vec4<float24>* buffer_end; |
|||
unsigned int vs_output_num; |
|||
}; |
|||
|
|||
GeometryPipeline::GeometryPipeline(State& state) : state(state) {} |
|||
|
|||
GeometryPipeline::~GeometryPipeline() = default; |
|||
|
|||
void GeometryPipeline::SetVertexHandler(Shader::VertexHandler vertex_handler) { |
|||
this->vertex_handler = vertex_handler; |
|||
} |
|||
|
|||
void GeometryPipeline::Setup(Shader::ShaderEngine* shader_engine) { |
|||
if (!backend) |
|||
return; |
|||
|
|||
this->shader_engine = shader_engine; |
|||
shader_engine->SetupBatch(state.gs, state.regs.gs.main_offset); |
|||
} |
|||
|
|||
void GeometryPipeline::Reconfigure() { |
|||
ASSERT(!backend || backend->IsEmpty()); |
|||
|
|||
if (state.regs.pipeline.use_gs == PipelineRegs::UseGS::No) { |
|||
backend = nullptr; |
|||
return; |
|||
} |
|||
|
|||
ASSERT(state.regs.pipeline.use_gs == PipelineRegs::UseGS::Yes); |
|||
|
|||
// The following assumes that when geometry shader is in use, the shader unit 3 is configured as
|
|||
// a geometry shader unit.
|
|||
// TODO: what happens if this is not true?
|
|||
ASSERT(state.regs.pipeline.gs_unit_exclusive_configuration == 1); |
|||
ASSERT(state.regs.gs.shader_mode == ShaderRegs::ShaderMode::GS); |
|||
|
|||
state.gs_unit.ConfigOutput(state.regs.gs); |
|||
|
|||
ASSERT(state.regs.pipeline.vs_outmap_total_minus_1_a == |
|||
state.regs.pipeline.vs_outmap_total_minus_1_b); |
|||
|
|||
switch (state.regs.pipeline.gs_config.mode) { |
|||
case PipelineRegs::GSMode::Point: |
|||
backend = std::make_unique<GeometryPipeline_Point>(state.regs, state.gs_unit); |
|||
break; |
|||
case PipelineRegs::GSMode::VariablePrimitive: |
|||
backend = std::make_unique<GeometryPipeline_VariablePrimitive>(state.regs, state.gs); |
|||
break; |
|||
case PipelineRegs::GSMode::FixedPrimitive: |
|||
backend = std::make_unique<GeometryPipeline_FixedPrimitive>(state.regs, state.gs); |
|||
break; |
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
bool GeometryPipeline::NeedIndexInput() const { |
|||
if (!backend) |
|||
return false; |
|||
return backend->NeedIndexInput(); |
|||
} |
|||
|
|||
void GeometryPipeline::SubmitIndex(unsigned int val) { |
|||
backend->SubmitIndex(val); |
|||
} |
|||
|
|||
void GeometryPipeline::SubmitVertex(const Shader::AttributeBuffer& input) { |
|||
if (!backend) { |
|||
// No backend means the geometry shader is disabled, so we send the vertex shader output
|
|||
// directly to the primitive assembler.
|
|||
vertex_handler(input); |
|||
} else { |
|||
if (backend->SubmitVertex(input)) { |
|||
shader_engine->Run(state.gs, state.gs_unit); |
|||
|
|||
// The uniform b15 is set to true after every geometry shader invocation. This is useful
|
|||
// for the shader to know if this is the first invocation in a batch, if the program set
|
|||
// b15 to false first.
|
|||
state.gs.uniforms.b[15] = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} // namespace Pica
|
|||
@ -0,0 +1,49 @@ |
|||
// Copyright 2017 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include "video_core/shader/shader.h" |
|||
|
|||
namespace Pica { |
|||
|
|||
struct State; |
|||
|
|||
class GeometryPipelineBackend; |
|||
|
|||
/// A pipeline receiving from vertex shader and sending to geometry shader and primitive assembler |
|||
class GeometryPipeline { |
|||
public: |
|||
explicit GeometryPipeline(State& state); |
|||
~GeometryPipeline(); |
|||
|
|||
/// Sets the handler for receiving vertex outputs from vertex shader |
|||
void SetVertexHandler(Shader::VertexHandler vertex_handler); |
|||
|
|||
/** |
|||
* Setup the geometry shader unit if it is in use |
|||
* @param shader_engine the shader engine for the geometry shader to run |
|||
*/ |
|||
void Setup(Shader::ShaderEngine* shader_engine); |
|||
|
|||
/// Reconfigures the pipeline according to current register settings |
|||
void Reconfigure(); |
|||
|
|||
/// Checks if the pipeline needs a direct input from index buffer |
|||
bool NeedIndexInput() const; |
|||
|
|||
/// Submits an index from index buffer. Call this only when NeedIndexInput returns true |
|||
void SubmitIndex(unsigned int val); |
|||
|
|||
/// Submits vertex attributes output from vertex shader |
|||
void SubmitVertex(const Shader::AttributeBuffer& input); |
|||
|
|||
private: |
|||
Shader::VertexHandler vertex_handler; |
|||
Shader::ShaderEngine* shader_engine; |
|||
std::unique_ptr<GeometryPipelineBackend> backend; |
|||
State& state; |
|||
}; |
|||
} // namespace Pica |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue