Merge pull request #59 from skylersaleh/vertex_refactor

Refactored Vertex Pipeline to always use Pica Formatted Vertex
This commit is contained in:
wheremyfoodat 2023-07-01 22:56:45 +03:00 committed by GitHub
commit 14d287dbd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 75 additions and 82 deletions

View file

@ -132,7 +132,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc
include/PICA/dynapica/shader_rec_emitter_x64.hpp include/PICA/pica_hash.hpp include/result/result.hpp include/PICA/dynapica/shader_rec_emitter_x64.hpp include/PICA/pica_hash.hpp include/result/result.hpp
include/result/result_common.hpp include/result/result_fs.hpp include/result/result_fnd.hpp include/result/result_common.hpp include/result/result_fs.hpp include/result/result_fnd.hpp
include/result/result_gsp.hpp include/result/result_kernel.hpp include/result/result_os.hpp include/result/result_gsp.hpp include/result/result_kernel.hpp include/result/result_os.hpp
include/crypto/aes_engine.hpp include/metaprogramming.hpp include/crypto/aes_engine.hpp include/metaprogramming.hpp include/PICA/pica_vertex.hpp
) )
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp

View file

@ -8,6 +8,7 @@
#include "PICA/shader_unit.hpp" #include "PICA/shader_unit.hpp"
#include "PICA/dynapica/shader_rec.hpp" #include "PICA/dynapica/shader_rec.hpp"
#include "renderer_gl/renderer_gl.hpp" #include "renderer_gl/renderer_gl.hpp"
#include "PICA/pica_vertex.hpp"
class GPU { class GPU {
static constexpr u32 regNum = 0x300; static constexpr u32 regNum = 0x300;
@ -27,7 +28,7 @@ class GPU {
std::array<vec4f, 16> currentAttributes; // Vertex attributes before being passed to the shader std::array<vec4f, 16> currentAttributes; // Vertex attributes before being passed to the shader
std::array<vec4f, 16> immediateModeAttributes; // Vertex attributes uploaded via immediate mode submission std::array<vec4f, 16> immediateModeAttributes; // Vertex attributes uploaded via immediate mode submission
std::array<Vertex, 3> immediateModeVertices; std::array<PicaVertex, 3> immediateModeVertices;
uint immediateModeVertIndex; uint immediateModeVertIndex;
uint immediateModeAttrIndex; // Index of the immediate mode attribute we're uploading uint immediateModeAttrIndex; // Index of the immediate mode attribute we're uploading
@ -67,7 +68,7 @@ class GPU {
u32* cmdBuffCurr = nullptr; u32* cmdBuffCurr = nullptr;
Renderer renderer; Renderer renderer;
Vertex getImmediateModeVertex(); PicaVertex getImmediateModeVertex();
public: public:
GPU(Memory& mem); GPU(Memory& mem);
void initGraphicsContext() { renderer.initGraphicsContext(); } void initGraphicsContext() { renderer.initGraphicsContext(); }

View file

@ -0,0 +1,43 @@
#pragma once
#include "PICA/float_types.hpp"
#include <array>
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
struct PicaVertex {
using vec2f = std::array<Floats::f24, 2>;
using vec3f = std::array<Floats::f24, 3>;
using vec4f = std::array<Floats::f24, 4>;
union {
struct {
vec4f positions; // Vertex position
vec4f quaternion; // Quaternion specifying the normal/tangent frame (for fragment lighting)
vec4f colour; // Vertex color
vec2f texcoord0; // Texcoords for texture unit 0 (Only U and V, W is stored separately for 3D textures!)
vec2f texcoord1; // Texcoords for TU 1
Floats::f24 texcoord0_w; // W component for texcoord 0 if using a 3D texture
u32 padding; // Unused
vec3f view; // View vector (for fragment lighting)
u32 padding2; // Unused
vec2f texcoord2; // Texcoords for TU 2
} s;
// The software, non-accelerated vertex loader writes here and then reads specific components from the above struct
Floats::f24 raw[0x20];
};
PicaVertex() {}
};
// Float is used here instead of Floats::f24 to ensure that Floats::f24 is properly sized for direct interpretations as a float by the render backend
#define ASSERT_POS(member, pos) static_assert(offsetof(PicaVertex, s.member) == pos * sizeof(float), "PicaVertex struct is broken!");
ASSERT_POS(positions, 0)
ASSERT_POS(quaternion, 4)
ASSERT_POS(colour, 8)
ASSERT_POS(texcoord0, 12)
ASSERT_POS(texcoord1, 14)
ASSERT_POS(texcoord0_w, 16)
ASSERT_POS(view, 18)
ASSERT_POS(texcoord2, 22)
#undef ASSERT_POS

View file

@ -9,20 +9,11 @@
#include "surface_cache.hpp" #include "surface_cache.hpp"
#include "textures.hpp" #include "textures.hpp"
#include "PICA/regs.hpp" #include "PICA/regs.hpp"
#include "PICA/pica_vertex.hpp"
// More circular dependencies! // More circular dependencies!
class GPU; class GPU;
struct Vertex {
OpenGL::vec4 position;
OpenGL::vec4 colour;
OpenGL::vec2 texcoord0;
OpenGL::vec2 texcoord1;
Floats::f24 texcoord0_w;
u32 padding; // pad so that texcoord2 is 64-bit aligned
OpenGL::vec2 texcoord2;
};
class Renderer { class Renderer {
GPU& gpu; GPU& gpu;
OpenGL::Program triangleProgram; OpenGL::Program triangleProgram;
@ -95,7 +86,7 @@ class Renderer {
void getGraphicsContext(); // Set up graphics context for rendering void getGraphicsContext(); // Set up graphics context for rendering
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
void drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices); // Draw the given vertices void drawVertices(PICA::PrimType primType, std::span<const PicaVertex> vertices); // Draw the given vertices
void setFBSize(u32 width, u32 height) { void setFBSize(u32 width, u32 height) {
fbSize.x() = width; fbSize.x() = width;

View file

@ -10,42 +10,6 @@
using namespace Floats; using namespace Floats;
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
struct OutputVertex {
using vec2f = OpenGL::Vector<f24, 2>;
using vec3f = OpenGL::Vector<f24, 3>;
using vec4f = OpenGL::Vector<f24, 4>;
union {
struct {
vec4f positions; // Vertex position
vec4f quaternion; // Quaternion specifying the normal/tangent frame (for fragment lighting)
vec4f colour; // Vertex color
vec2f texcoord0; // Texcoords for texture unit 0 (Only U and V, W is stored separately for 3D textures!)
vec2f texcoord1; // Texcoords for TU 1
f24 texcoord0_w; // W component for texcoord 0 if using a 3D texture
u32 padding; // Unused
vec3f view; // View vector (for fragment lighting)
u32 padding2; // Unused
vec2f texcoord2; // Texcoords for TU 2
} s;
// The software, non-accelerated vertex loader writes here and then reads specific components from the above struct
f24 raw[0x20];
};
OutputVertex() {}
};
#define ASSERT_POS(member, pos) static_assert(offsetof(OutputVertex, s.member) == pos * sizeof(f24), "OutputVertex struct is broken!");
ASSERT_POS(positions, 0)
ASSERT_POS(quaternion, 4)
ASSERT_POS(colour, 8)
ASSERT_POS(texcoord0, 12)
ASSERT_POS(texcoord1, 14)
ASSERT_POS(texcoord0_w, 16)
ASSERT_POS(view, 18)
ASSERT_POS(texcoord2, 22)
GPU::GPU(Memory& mem) : mem(mem), renderer(*this, regs) { GPU::GPU(Memory& mem) : mem(mem), renderer(*this, regs) {
vram = new u8[vramSize]; vram = new u8[vramSize];
@ -95,7 +59,7 @@ void GPU::drawArrays(bool indexed) {
} }
} }
static std::array<Vertex, Renderer::vertexBufferSize> vertices; static std::array<PicaVertex, Renderer::vertexBufferSize> vertices;
template <bool indexed, bool useShaderJIT> template <bool indexed, bool useShaderJIT>
void GPU::drawArrays() { void GPU::drawArrays() {
@ -283,7 +247,7 @@ void GPU::drawArrays() {
shaderUnit.vs.run(); shaderUnit.vs.run();
} }
OutputVertex out; PicaVertex& out = vertices[i];
// Map shader outputs to fixed function properties // Map shader outputs to fixed function properties
const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7; const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
for (int i = 0; i < totalShaderOutputs; i++) { for (int i = 0; i < totalShaderOutputs; i++) {
@ -294,24 +258,13 @@ void GPU::drawArrays() {
out.raw[mapping] = shaderUnit.vs.outputs[i][j]; out.raw[mapping] = shaderUnit.vs.outputs[i][j];
} }
} }
std::memcpy(&vertices[i].position, &out.s.positions, sizeof(vec4f));
std::memcpy(&vertices[i].colour, &out.s.colour, sizeof(vec4f));
std::memcpy(&vertices[i].texcoord0, &out.s.texcoord0, 2 * sizeof(f24));
std::memcpy(&vertices[i].texcoord1, &out.s.texcoord1, 2 * sizeof(f24));
std::memcpy(&vertices[i].texcoord0_w, &out.s.texcoord0_w, sizeof(f24));
std::memcpy(&vertices[i].texcoord2, &out.s.texcoord2, 2 * sizeof(f24));
//printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)vertices[i].position.x(), (double)vertices[i].position.y(), (double)vertices[i].position.z(), (double)vertices[i].position.w());
//printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)vertices[i].colour.r(), (double)vertices[i].colour.g(), (double)vertices[i].colour.b(), (double)vertices[i].colour.a());
//printf("(u, v ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
} }
renderer.drawVertices(primType, std::span(vertices).first(vertexCount)); renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
} }
Vertex GPU::getImmediateModeVertex() { PicaVertex GPU::getImmediateModeVertex() {
Vertex v; PicaVertex v;
const int totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1; const int totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
// Copy immediate mode attributes to vertex shader unit // Copy immediate mode attributes to vertex shader unit
@ -321,13 +274,13 @@ Vertex GPU::getImmediateModeVertex() {
// Run VS and return vertex data. TODO: Don't hardcode offsets for each attribute // Run VS and return vertex data. TODO: Don't hardcode offsets for each attribute
shaderUnit.vs.run(); shaderUnit.vs.run();
std::memcpy(&v.position, &shaderUnit.vs.outputs[0], sizeof(vec4f)); std::memcpy(&v.s.positions, &shaderUnit.vs.outputs[0], sizeof(vec4f));
std::memcpy(&v.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f)); std::memcpy(&v.s.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
std::memcpy(&v.texcoord0, &shaderUnit.vs.outputs[2], 2 * sizeof(f24)); std::memcpy(&v.s.texcoord0, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)v.position.x(), (double)v.position.y(), (double)v.position.z(), (double)v.position.w()); printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)v.s.positions[0], (double)v.s.positions[1], (double)v.s.positions[2], (double)v.s.positions[3]);
printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)v.colour.r(), (double)v.colour.g(), (double)v.colour.b(), (double)v.colour.a()); printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)v.s.colour[0], (double)v.s.colour[1], (double)v.s.colour[2], (double)v.s.colour[3]);
printf("(u, v ) = (%f, %f)\n", v.texcoord0.u(), v.texcoord0.v()); printf("(u, v ) = (%f, %f)\n", (double)v.s.texcoord0[0], (double)v.s.texcoord0[1]);
return v; return v;
} }

View file

@ -146,7 +146,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
immediateModeAttributes[immediateModeAttrIndex++] = attr; immediateModeAttributes[immediateModeAttrIndex++] = attr;
if (immediateModeAttrIndex == totalAttrCount) { if (immediateModeAttrIndex == totalAttrCount) {
Vertex v = getImmediateModeVertex(); PicaVertex v = getImmediateModeVertex();
immediateModeAttrIndex = 0; immediateModeAttrIndex = 0;
immediateModeVertices[immediateModeVertIndex++] = v; immediateModeVertices[immediateModeVertIndex++] = v;

View file

@ -190,14 +190,19 @@ const char* fragmentShader = R"(
return result; return result;
} }
void calcLighting(out vec4 primary_color, out vec4 secondary_color){
primary_color = vec4(vec3(0.5) ,1.0);
secondary_color = vec4(vec3(0.5) ,1.0);
}
void main() { void main() {
vec2 tex2UV = (u_textureConfig & (1u << 13)) != 0u ? v_texcoord1 : v_texcoord2; vec2 tex2UV = (u_textureConfig & (1u << 13)) != 0u ? v_texcoord1 : v_texcoord2;
// TODO: what do invalid sources and disabled textures read as? // TODO: what do invalid sources and disabled textures read as?
// And what does the "previous combiner" source read initially? // And what does the "previous combiner" source read initially?
tevSources[0] = v_colour; // Primary/vertex color tevSources[0] = v_colour; // Primary/vertex color
tevSources[1] = vec4(vec3(0.5), 1.0); // Fragment primary color calcLighting(tevSources[1],tevSources[2]);
tevSources[2] = vec4(vec3(0.5), 1.0); // Fragment secondary color
if ((u_textureConfig & 1u) != 0u) tevSources[3] = texture(u_tex0, v_texcoord0.xy); if ((u_textureConfig & 1u) != 0u) tevSources[3] = texture(u_tex0, v_texcoord0.xy);
if ((u_textureConfig & 2u) != 0u) tevSources[4] = texture(u_tex1, v_texcoord1); if ((u_textureConfig & 2u) != 0u) tevSources[4] = texture(u_tex1, v_texcoord1);
if ((u_textureConfig & 4u) != 0u) tevSources[5] = texture(u_tex2, tex2UV); if ((u_textureConfig & 4u) != 0u) tevSources[5] = texture(u_tex2, tex2UV);
@ -379,28 +384,28 @@ void Renderer::initGraphicsContext() {
displayProgram.use(); displayProgram.use();
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW); vbo.createFixedSize(sizeof(PicaVertex) * vertexBufferSize, GL_STREAM_DRAW);
vbo.bind(); vbo.bind();
vao.create(); vao.create();
vao.bind(); vao.bind();
// Position (x, y, z, w) attributes // Position (x, y, z, w) attributes
vao.setAttributeFloat<float>(0, 4, sizeof(Vertex), offsetof(Vertex, position)); vao.setAttributeFloat<float>(0, 4, sizeof(PicaVertex), offsetof(PicaVertex, s.positions));
vao.enableAttribute(0); vao.enableAttribute(0);
// Colour attribute // Colour attribute
vao.setAttributeFloat<float>(1, 4, sizeof(Vertex), offsetof(Vertex, colour)); vao.setAttributeFloat<float>(1, 4, sizeof(PicaVertex), offsetof(PicaVertex, s.colour));
vao.enableAttribute(1); vao.enableAttribute(1);
// UV 0 attribute // UV 0 attribute
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, texcoord0)); vao.setAttributeFloat<float>(2, 2, sizeof(PicaVertex), offsetof(PicaVertex, s.texcoord0));
vao.enableAttribute(2); vao.enableAttribute(2);
// UV 1 attribute // UV 1 attribute
vao.setAttributeFloat<float>(3, 2, sizeof(Vertex), offsetof(Vertex, texcoord1)); vao.setAttributeFloat<float>(3, 2, sizeof(PicaVertex), offsetof(PicaVertex, s.texcoord1));
vao.enableAttribute(3); vao.enableAttribute(3);
// UV 0 W-component attribute // UV 0 W-component attribute
vao.setAttributeFloat<float>(4, 1, sizeof(Vertex), offsetof(Vertex, texcoord0_w)); vao.setAttributeFloat<float>(4, 1, sizeof(PicaVertex), offsetof(PicaVertex, s.texcoord0_w));
vao.enableAttribute(4); vao.enableAttribute(4);
// UV 2 attribute // UV 2 attribute
vao.setAttributeFloat<float>(5, 2, sizeof(Vertex), offsetof(Vertex, texcoord2)); vao.setAttributeFloat<float>(5, 2, sizeof(PicaVertex), offsetof(PicaVertex, s.texcoord2));
vao.enableAttribute(5); vao.enableAttribute(5);
dummyVBO.create(); dummyVBO.create();
@ -548,7 +553,7 @@ void Renderer::bindTexturesToSlots() {
} }
} }
void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices) { void Renderer::drawVertices(PICA::PrimType primType, std::span<const PicaVertex> vertices) {
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is // The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
static constexpr std::array<OpenGL::Primitives, 4> primTypes = { static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle