Pica: Improve accuracy of immediate-mode support
This partially fixes Etrian Odyssey IV.
This commit is contained in:
parent
0c447e0a06
commit
81004211dd
@ -75,12 +75,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
|
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E):
|
||||||
|
g_state.primitive_assembler.Reconfigure(regs.triangle_topology);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PICA_REG_INDEX_WORKAROUND(restart_primitive, 0x25F):
|
||||||
|
g_state.primitive_assembler.Reset();
|
||||||
|
break;
|
||||||
|
|
||||||
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
|
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
|
||||||
if (regs.vs_default_attributes_setup.index == 15) {
|
g_state.immediate.current_attribute = 0;
|
||||||
// Reset immediate primitive state
|
default_attr_counter = 0;
|
||||||
g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
|
|
||||||
g_state.immediate.attribute_id = 0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Load default vertex input attributes
|
// Load default vertex input attributes
|
||||||
@ -105,7 +110,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
|
Math::Vec4<float24> attribute;
|
||||||
|
|
||||||
// NOTE: The destination component order indeed is "backwards"
|
// NOTE: The destination component order indeed is "backwards"
|
||||||
attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
|
attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
|
||||||
@ -119,26 +124,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
|
|
||||||
// TODO: Verify that this actually modifies the register!
|
// TODO: Verify that this actually modifies the register!
|
||||||
if (setup.index < 15) {
|
if (setup.index < 15) {
|
||||||
|
g_state.vs.default_attributes[setup.index] = attribute;
|
||||||
setup.index++;
|
setup.index++;
|
||||||
} else {
|
} else {
|
||||||
// Put each attribute into an immediate input buffer.
|
// Put each attribute into an immediate input buffer.
|
||||||
// When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
|
// When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
|
||||||
// sent to the primitive assembler.
|
// sent to the primitive assembler.
|
||||||
|
|
||||||
auto& immediate_input = g_state.immediate.input;
|
auto& immediate_input = g_state.immediate.input_vertex;
|
||||||
auto& immediate_attribute_id = g_state.immediate.attribute_id;
|
auto& immediate_attribute_id = g_state.immediate.current_attribute;
|
||||||
const auto& attribute_config = regs.vertex_attributes;
|
|
||||||
|
|
||||||
immediate_input.attr[immediate_attribute_id++] = attribute;
|
immediate_input.attr[immediate_attribute_id++] = attribute;
|
||||||
|
|
||||||
if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) {
|
if (immediate_attribute_id >= regs.vs.num_input_attributes+1) {
|
||||||
immediate_attribute_id = 0;
|
immediate_attribute_id = 0;
|
||||||
|
|
||||||
Shader::UnitState<false> shader_unit;
|
Shader::UnitState<false> shader_unit;
|
||||||
Shader::Setup(shader_unit);
|
Shader::Setup(shader_unit);
|
||||||
|
|
||||||
|
if (g_debug_context)
|
||||||
|
g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
|
||||||
|
|
||||||
// Send to vertex shader
|
// Send to vertex shader
|
||||||
Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes());
|
Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
|
||||||
|
|
||||||
// Send to renderer
|
// Send to renderer
|
||||||
using Pica::Shader::OutputVertex;
|
using Pica::Shader::OutputVertex;
|
||||||
@ -146,7 +154,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
|
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
|
||||||
};
|
};
|
||||||
|
|
||||||
g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle);
|
g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,9 +162,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case PICA_REG_INDEX(gpu_mode):
|
case PICA_REG_INDEX(gpu_mode):
|
||||||
if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) {
|
if (regs.gpu_mode == Regs::GPUMode::Configuring) {
|
||||||
// Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
|
// Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
|
||||||
VideoCore::g_renderer->Rasterizer()->DrawTriangles();
|
VideoCore::g_renderer->Rasterizer()->DrawTriangles();
|
||||||
|
|
||||||
|
if (g_debug_context) {
|
||||||
|
g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -241,7 +253,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
DebugUtils::GeometryDumper geometry_dumper;
|
DebugUtils::GeometryDumper geometry_dumper;
|
||||||
PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
|
PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
|
||||||
#endif
|
#endif
|
||||||
PrimitiveAssembler<Shader::OutputVertex> primitive_assembler(regs.triangle_topology.Value());
|
PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
|
||||||
|
|
||||||
if (g_debug_context) {
|
if (g_debug_context) {
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
@ -412,16 +424,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
range.second, range.first);
|
range.second, range.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCore::g_renderer->Rasterizer()->DrawTriangles();
|
|
||||||
|
|
||||||
#if PICA_DUMP_GEOMETRY
|
#if PICA_DUMP_GEOMETRY
|
||||||
geometry_dumper.Dump();
|
geometry_dumper.Dump();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (g_debug_context) {
|
|
||||||
g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,12 +493,25 @@ std::string Regs::GetCommandName(int index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Init() {
|
void Init() {
|
||||||
|
g_state.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
Shader::Shutdown();
|
Shader::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
memset(&g_state, 0, sizeof(State));
|
template <typename T>
|
||||||
|
void Zero(T& o) {
|
||||||
|
memset(&o, 0, sizeof(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::Reset() {
|
||||||
|
Zero(regs);
|
||||||
|
Zero(vs);
|
||||||
|
Zero(gs);
|
||||||
|
Zero(cmd_list);
|
||||||
|
Zero(immediate);
|
||||||
|
primitive_assembler.Reconfigure(Regs::TriangleTopology::List);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1123,7 +1123,12 @@ struct Regs {
|
|||||||
BitField<24, 8, u32> w;
|
BitField<24, 8, u32> w;
|
||||||
} int_uniforms[4];
|
} int_uniforms[4];
|
||||||
|
|
||||||
INSERT_PADDING_WORDS(0x5);
|
INSERT_PADDING_WORDS(0x4);
|
||||||
|
|
||||||
|
union {
|
||||||
|
// Number of input attributes to shader unit - 1
|
||||||
|
BitField<0, 4, u32> num_input_attributes;
|
||||||
|
};
|
||||||
|
|
||||||
// Offset to shader program entry point (in words)
|
// Offset to shader program entry point (in words)
|
||||||
BitField<0, 16, u32> main_offset;
|
BitField<0, 16, u32> main_offset;
|
||||||
|
@ -12,6 +12,8 @@ namespace Pica {
|
|||||||
|
|
||||||
/// Struct used to describe current Pica state
|
/// Struct used to describe current Pica state
|
||||||
struct State {
|
struct State {
|
||||||
|
void Reset();
|
||||||
|
|
||||||
/// Pica registers
|
/// Pica registers
|
||||||
Regs regs;
|
Regs regs;
|
||||||
|
|
||||||
@ -46,13 +48,14 @@ struct State {
|
|||||||
|
|
||||||
/// Struct used to describe immediate mode rendering state
|
/// Struct used to describe immediate mode rendering state
|
||||||
struct ImmediateModeState {
|
struct ImmediateModeState {
|
||||||
Shader::InputVertex input;
|
// Used to buffer partial vertices for immediate-mode rendering.
|
||||||
|
Shader::InputVertex input_vertex;
|
||||||
|
// Index of the next attribute to be loaded into `input_vertex`.
|
||||||
|
int current_attribute = 0;
|
||||||
|
} immediate;
|
||||||
|
|
||||||
// This is constructed with a dummy triangle topology
|
// This is constructed with a dummy triangle topology
|
||||||
PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
|
PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
|
||||||
int attribute_id = 0;
|
|
||||||
|
|
||||||
ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
|
|
||||||
} immediate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern State g_state; ///< Current Pica state
|
extern State g_state; ///< Current Pica state
|
||||||
|
@ -20,7 +20,7 @@ struct PrimitiveAssembler {
|
|||||||
VertexType& v1,
|
VertexType& v1,
|
||||||
VertexType& v2)>;
|
VertexType& v2)>;
|
||||||
|
|
||||||
PrimitiveAssembler(Regs::TriangleTopology topology);
|
PrimitiveAssembler(Regs::TriangleTopology topology = Regs::TriangleTopology::List);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Queues a vertex, builds primitives from the vertex queue according to the given
|
* Queues a vertex, builds primitives from the vertex queue according to the given
|
||||||
|
Loading…
Reference in New Issue
Block a user