mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 21:40:14 +00:00
Implement 4 shader units and geometry shaders
This commit is contained in:
parent
f02650b0d8
commit
9da3f2d6c2
@ -45,6 +45,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
|
|||||||
{ Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") },
|
{ Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") },
|
||||||
{ Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") },
|
{ Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") },
|
||||||
{ Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") },
|
{ Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") },
|
||||||
|
{ Pica::DebugContext::Event::GeometryShaderInvocation, tr("Geometry shader invocation") },
|
||||||
{ Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") },
|
{ Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") },
|
||||||
{ Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") },
|
{ Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") },
|
||||||
{ Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") }
|
{ Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") }
|
||||||
|
@ -139,7 +139,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
|
if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
|
||||||
immediate_attribute_id = 0;
|
immediate_attribute_id = 0;
|
||||||
|
|
||||||
Shader::UnitState<false> shader_unit;
|
auto& shader_unit = Shader::GetShaderUnit(false);
|
||||||
g_state.vs.Setup();
|
g_state.vs.Setup();
|
||||||
|
|
||||||
// Send to vertex shader
|
// Send to vertex shader
|
||||||
@ -232,9 +232,12 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
unsigned int vertex_cache_pos = 0;
|
unsigned int vertex_cache_pos = 0;
|
||||||
vertex_cache_ids.fill(-1);
|
vertex_cache_ids.fill(-1);
|
||||||
|
|
||||||
Shader::UnitState<false> shader_unit;
|
auto& vs_shader_unit = Shader::GetShaderUnit(false);
|
||||||
g_state.vs.Setup();
|
g_state.vs.Setup();
|
||||||
|
|
||||||
|
auto& gs_unit_state = Shader::GetShaderUnit(true);
|
||||||
|
g_state.gs.Setup();
|
||||||
|
|
||||||
for (unsigned int index = 0; index < regs.num_vertices; ++index)
|
for (unsigned int index = 0; index < regs.num_vertices; ++index)
|
||||||
{
|
{
|
||||||
// Indexed rendering doesn't use the start offset
|
// Indexed rendering doesn't use the start offset
|
||||||
@ -270,8 +273,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
// Send to vertex shader
|
// Send to vertex shader
|
||||||
if (g_debug_context)
|
if (g_debug_context)
|
||||||
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input);
|
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input);
|
||||||
g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes(), regs.vs);
|
g_state.vs.Run(vs_shader_unit, input, loader.GetNumTotalAttributes(), regs.vs);
|
||||||
output_registers = shader_unit.output_registers;
|
output_registers = vs_shader_unit.output_registers;
|
||||||
|
|
||||||
if (is_indexed) {
|
if (is_indexed) {
|
||||||
vertex_cache[vertex_cache_pos] = output_registers;
|
vertex_cache[vertex_cache_pos] = output_registers;
|
||||||
@ -280,17 +283,56 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retreive vertex from register data
|
// Helper to send triangle to renderer
|
||||||
Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs);
|
|
||||||
|
|
||||||
// Send to renderer
|
|
||||||
using Pica::Shader::OutputVertex;
|
using Pica::Shader::OutputVertex;
|
||||||
auto AddTriangle = [](
|
auto AddTriangle = [](
|
||||||
const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
|
const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
|
||||||
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
|
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
|
||||||
};
|
};
|
||||||
|
|
||||||
primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
|
if (Shader::UseGS()) {
|
||||||
|
|
||||||
|
auto& regs = g_state.regs;
|
||||||
|
auto& gs_regs = g_state.regs.gs;
|
||||||
|
auto& gs_buf = g_state.gs_input_buffer;
|
||||||
|
|
||||||
|
// Vertex Shader Outputs are converted into Geometry Shader inputs by filling up a buffer
|
||||||
|
// For example, if we have a geoshader that takes 6 inputs, and the vertex shader outputs 2 attributes
|
||||||
|
// It would take 3 vertices to fill up the Geometry Shader buffer
|
||||||
|
unsigned int gs_input_count = gs_regs.num_input_attributes + 1;
|
||||||
|
unsigned int vs_output_count = regs.vs_outmap_total2 + 1;
|
||||||
|
ASSERT_MSG(regs.vs_outmap_total1 == regs.vs_outmap_total2, "VS_OUTMAP_TOTAL1 and VS_OUTMAP_TOTAL2 don't match!");
|
||||||
|
// copy into the geoshader buffer
|
||||||
|
for (unsigned int i = 0; i < vs_output_count; i++) {
|
||||||
|
if (gs_buf.index >= gs_input_count) {
|
||||||
|
// TODO(ds84182): LOG_ERROR()
|
||||||
|
ASSERT_MSG(false, "Number of GS inputs (%d) is not divisible by number of VS outputs (%d)",
|
||||||
|
gs_input_count, vs_output_count);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gs_buf.buffer.attr[gs_buf.index++] = output_registers.value[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gs_buf.index >= gs_input_count) {
|
||||||
|
|
||||||
|
// b15 will be false when a new primitive starts and then switch to true at some point
|
||||||
|
//TODO: Test how this works exactly on hardware
|
||||||
|
g_state.gs.uniforms.b[15] |= (index > 0);
|
||||||
|
|
||||||
|
// Process Geometry Shader
|
||||||
|
if (g_debug_context)
|
||||||
|
g_debug_context->OnEvent(DebugContext::Event::GeometryShaderInvocation, static_cast<void*>(&gs_buf.buffer));
|
||||||
|
gs_unit_state.emit_triangle_callback = AddTriangle;
|
||||||
|
g_state.gs.Run(gs_unit_state, gs_buf.buffer, gs_input_count, regs.gs);
|
||||||
|
gs_unit_state.emit_triangle_callback = nullptr;
|
||||||
|
|
||||||
|
gs_buf.index = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs);
|
||||||
|
primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& range : memory_accesses.ranges) {
|
for (auto& range : memory_accesses.ranges) {
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
IncomingPrimitiveBatch,
|
IncomingPrimitiveBatch,
|
||||||
FinishedPrimitiveBatch,
|
FinishedPrimitiveBatch,
|
||||||
VertexShaderInvocation,
|
VertexShaderInvocation,
|
||||||
|
GeometryShaderInvocation,
|
||||||
IncomingDisplayTransfer,
|
IncomingDisplayTransfer,
|
||||||
GSPCommandProcessed,
|
GSPCommandProcessed,
|
||||||
BufferSwapped,
|
BufferSwapped,
|
||||||
|
@ -22,6 +22,8 @@ struct State {
|
|||||||
/// Pica registers
|
/// Pica registers
|
||||||
Regs regs;
|
Regs regs;
|
||||||
|
|
||||||
|
Shader::UnitState<false> shader_units[4];
|
||||||
|
|
||||||
Shader::ShaderSetup vs;
|
Shader::ShaderSetup vs;
|
||||||
Shader::ShaderSetup gs;
|
Shader::ShaderSetup gs;
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ template<typename VertexType>
|
|||||||
void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler)
|
void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler)
|
||||||
{
|
{
|
||||||
switch (topology) {
|
switch (topology) {
|
||||||
// TODO: Figure out what's different with TriangleTopology::Shader.
|
|
||||||
case Regs::TriangleTopology::List:
|
case Regs::TriangleTopology::List:
|
||||||
case Regs::TriangleTopology::Shader:
|
case Regs::TriangleTopology::Shader:
|
||||||
if (buffer_index < 2) {
|
if (buffer_index < 2) {
|
||||||
|
@ -167,6 +167,28 @@ bool SharedGS() {
|
|||||||
return g_state.regs.vs_com_mode == Pica::Regs::VSComMode::Shared;
|
return g_state.regs.vs_com_mode == Pica::Regs::VSComMode::Shared;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UseGS() {
|
||||||
|
// TODO(ds84182): This would be more accurate if it looked at induvidual shader units for the geoshader bit
|
||||||
|
// gs_regs.input_buffer_config.use_geometry_shader == 0x08
|
||||||
|
ASSERT((g_state.regs.using_geometry_shader == 0) || (g_state.regs.using_geometry_shader == 2));
|
||||||
|
return g_state.regs.using_geometry_shader == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnitState<false>& GetShaderUnit(bool gs) {
|
||||||
|
|
||||||
|
// GS are always run on shader unit 3
|
||||||
|
if (gs) {
|
||||||
|
return g_state.shader_units[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The worst scheduler you'll ever see!
|
||||||
|
//TODO: How does PICA shader scheduling work?
|
||||||
|
static unsigned shader_unit_scheduler = 0;
|
||||||
|
shader_unit_scheduler++;
|
||||||
|
shader_unit_scheduler %= 3; // TODO: When does it also allow use of unit 3?!
|
||||||
|
return g_state.shader_units[shader_unit_scheduler];
|
||||||
|
}
|
||||||
|
|
||||||
void WriteUniformBoolReg(bool gs, u32 value) {
|
void WriteUniformBoolReg(bool gs, u32 value) {
|
||||||
auto& setup = gs ? g_state.gs : g_state.vs;
|
auto& setup = gs ? g_state.gs : g_state.vs;
|
||||||
|
|
||||||
|
@ -408,6 +408,8 @@ struct ShaderSetup {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool SharedGS();
|
bool SharedGS();
|
||||||
|
bool UseGS();
|
||||||
|
UnitState<false>& GetShaderUnit(bool gs);
|
||||||
void WriteUniformBoolReg(bool gs, u32 value);
|
void WriteUniformBoolReg(bool gs, u32 value);
|
||||||
void WriteUniformIntReg(bool gs, unsigned index, const Math::Vec4<u8>& values);
|
void WriteUniformIntReg(bool gs, unsigned index, const Math::Vec4<u8>& values);
|
||||||
void WriteUniformFloatSetupReg(bool gs, u32 value);
|
void WriteUniformFloatSetupReg(bool gs, u32 value);
|
||||||
|
Loading…
Reference in New Issue
Block a user