Implement 4 shader units and geometry shaders

This commit is contained in:
Jannik Vogel 2016-04-06 02:17:12 +02:00
parent f02650b0d8
commit 9da3f2d6c2
7 changed files with 79 additions and 10 deletions

View File

@ -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") }

View File

@ -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,19 +283,58 @@ 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);
}; };
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); primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
} }
}
for (auto& range : memory_accesses.ranges) { for (auto& range : memory_accesses.ranges) {
g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first), g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first),
range.second, range.first); range.second, range.first);

View File

@ -41,6 +41,7 @@ public:
IncomingPrimitiveBatch, IncomingPrimitiveBatch,
FinishedPrimitiveBatch, FinishedPrimitiveBatch,
VertexShaderInvocation, VertexShaderInvocation,
GeometryShaderInvocation,
IncomingDisplayTransfer, IncomingDisplayTransfer,
GSPCommandProcessed, GSPCommandProcessed,
BufferSwapped, BufferSwapped,

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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);