From 78a3f9fa232467aff74e7561a65b8b78a18b24bd Mon Sep 17 00:00:00 2001
From: Wunkolo <Wunkolo@gmail.com>
Date: Sat, 17 Jun 2023 11:59:45 -0700
Subject: [PATCH] Add PICA texel-format and topology types

Slowly stepping the codebase towards having renderer-agnostic types and keeping the translation of PICA-types to OpenGL/VK/DX/Software/etc to the renderer-backend.
---
 include/PICA/regs.hpp                | 49 ++++++++++++++++++++++++-
 include/renderer_gl/renderer_gl.hpp  | 19 +++++-----
 include/renderer_gl/surfaces.hpp     | 53 +++++-----------------------
 src/core/PICA/gpu.cpp                | 14 +++-----
 src/core/PICA/regs.cpp               |  8 ++---
 src/core/renderer_gl/renderer_gl.cpp | 18 ++++++----
 6 files changed, 85 insertions(+), 76 deletions(-)

diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp
index 0b3246db..607e72e6 100644
--- a/include/PICA/regs.hpp
+++ b/include/PICA/regs.hpp
@@ -127,4 +127,51 @@ namespace PICAInternalRegs {
 		VertexShaderOpDescriptorData6 = 0x2DC,
 		VertexShaderOpDescriptorData7 = 0x2DD,
 	};
-}
\ No newline at end of file
+}
+
+enum class PICAColorFmt : u32 {
+	RGBA8 = 0,
+	BGR8 = 1,
+	RGB5A1 = 2,
+	RGB565 = 3,
+	RGBA4 = 4,
+
+	// Technically selectable, but their function is unknown
+	Unknown5 = 5,
+	Unknown6 = 6,
+	Unknown7 = 7,
+};
+
+enum class PICADepthFmt : u32 {
+	Depth16 = 0,
+	Unknown1 = 1,  // Technically selectable, but function is unknown
+	Depth24 = 2,
+	Depth24Stencil8 = 3,
+};
+
+// Size occupied by each pixel in bytes
+
+// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
+inline constexpr usize sizePerPixel(PICAColorFmt format) {
+	switch (format) {
+		case PICAColorFmt::BGR8: return 3;
+		case PICAColorFmt::RGBA8: return 4;
+		default: return 2;
+	}
+}
+
+inline constexpr usize sizePerPixel(PICADepthFmt format) {
+	switch (format) {
+		case PICADepthFmt::Depth16: return 2;
+		case PICADepthFmt::Depth24: return 3;
+		case PICADepthFmt::Depth24Stencil8: return 4;
+		default: return 1;  // Invalid format
+	}
+}
+
+enum class PICAPrimType : u32 {
+	TriangleList = 0,
+	TriangleStrip = 1,
+	TriangleFan = 2,
+	GeometryPrimitive = 3,
+};
\ No newline at end of file
diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp
index f387cb69..86ebcfe2 100644
--- a/include/renderer_gl/renderer_gl.hpp
+++ b/include/renderer_gl/renderer_gl.hpp
@@ -7,6 +7,7 @@
 #include "opengl.hpp"
 #include "surface_cache.hpp"
 #include "textures.hpp"
+#include "PICA/regs.hpp"
 
 // More circular dependencies!
 class GPU;
@@ -46,11 +47,11 @@ class Renderer {
 	OpenGL::uvec2 fbSize;  // The size of the framebuffer (ie both the colour and depth buffer)'
 
 	u32 colourBufferLoc;                       // Location in 3DS VRAM for the colour buffer
-	ColourBuffer::Formats colourBufferFormat;  // Format of the colours stored in the colour buffer
+	PICAColorFmt colourBufferFormat;  // Format of the colours stored in the colour buffer
 
 	// Same for the depth/stencil buffer
 	u32 depthBufferLoc;
-	DepthBuffer::Formats depthBufferFormat;
+	PICADepthFmt depthBufferFormat;
 
 	// Dummy VAO/VBO for blitting the final output
 	OpenGL::VertexArray dummyVAO;
@@ -75,23 +76,19 @@ class Renderer {
 	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 displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags);  // Perform display transfer
-	void drawVertices(OpenGL::Primitives primType, std::span<const Vertex> vertices);               // Draw the given vertices
+	void drawVertices(PICAPrimType primType, std::span<const Vertex> vertices);               // Draw the given vertices
 
 	void setFBSize(u32 width, u32 height) {
 		fbSize.x() = width;
 		fbSize.y() = height;
 	}
 
-	void setColourFormat(ColourBuffer::Formats format) { colourBufferFormat = format; }
-	void setColourFormat(u32 format) { colourBufferFormat = static_cast<ColourBuffer::Formats>(format); }
-
-	void setDepthFormat(DepthBuffer::Formats format) { depthBufferFormat = format; }
-	void setDepthFormat(u32 format) {
-		if (format == 1) {
+	void setColourFormat(PICAColorFmt format) { colourBufferFormat = format; }
+	void setDepthFormat(PICADepthFmt format) {
+		if (format == PICADepthFmt::Unknown1) {
 			Helpers::panic("[PICA] Undocumented depth-stencil mode!");
 		}
-
-		depthBufferFormat = static_cast<DepthBuffer::Formats>(format);
+		depthBufferFormat = format;
 	}
 
 	void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
diff --git a/include/renderer_gl/surfaces.hpp b/include/renderer_gl/surfaces.hpp
index 680a1454..7685a765 100644
--- a/include/renderer_gl/surfaces.hpp
+++ b/include/renderer_gl/surfaces.hpp
@@ -1,4 +1,5 @@
 #pragma once
+#include "PICA/regs.hpp"
 #include "boost/icl/interval.hpp"
 #include "helpers.hpp"
 #include "opengl.hpp"
@@ -7,18 +8,8 @@ template <typename T>
 using Interval = boost::icl::right_open_interval<T>;
 
 struct ColourBuffer {
-    enum class Formats : u32 {
-        RGBA8 = 0,
-        BGR8 = 1,
-        RGB5A1 = 2,
-        RGB565 = 3,
-        RGBA4 = 4,
-        
-        Trash1 = 5, Trash2 = 6, Trash3 = 7 // Technically selectable, but their function is unknown
-    };
-
     u32 location;
-    Formats format;
+    PICAColorFmt format;
     OpenGL::uvec2 size;
     bool valid;
 
@@ -30,7 +21,7 @@ struct ColourBuffer {
 
     ColourBuffer() : valid(false) {}
 
-    ColourBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true)
+    ColourBuffer(u32 loc, PICAColorFmt format, u32 x, u32 y, bool valid = true)
         : location(loc), format(format), size({x, y}), valid(valid) {
 
         u64 endLoc = (u64)loc + sizeInBytes();
@@ -78,31 +69,14 @@ struct ColourBuffer {
             size.x() == other.size.x() && size.y() == other.size.y();
     }
 
-    // Size occupied by each pixel in bytes
-    // All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
-    size_t sizePerPixel() {
-        switch (format) {
-            case Formats::BGR8: return 3;
-            case Formats::RGBA8: return 4;
-            default: return 2;
-        }
-    }
-
     size_t sizeInBytes() {
-        return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
+        return (size_t)size.x() * (size_t)size.y() * sizePerPixel(format);
     }
 };
 
 struct DepthBuffer {
-    enum class Formats : u32 {
-        Depth16 = 0,
-        Garbage = 1,
-        Depth24 = 2,
-        Depth24Stencil8 = 3
-    };
-
     u32 location;
-    Formats format;
+    PICADepthFmt format;
     OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
     bool valid;
 
@@ -113,7 +87,7 @@ struct DepthBuffer {
 
     DepthBuffer() : valid(false) {}
 
-    DepthBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true) :
+    DepthBuffer(u32 loc, PICADepthFmt format, u32 x, u32 y, bool valid = true) :
         location(loc), format(format), size({x, y}), valid(valid) {
 
         u64 endLoc = (u64)loc + sizeInBytes();
@@ -122,7 +96,7 @@ struct DepthBuffer {
     }
 
     bool hasStencil() {
-        return format == Formats::Depth24Stencil8;
+        return format == PICADepthFmt::Depth24Stencil8;
     }
 
     void allocate() {
@@ -167,18 +141,7 @@ struct DepthBuffer {
             size.x() == other.size.x() && size.y() == other.size.y();
     }
 
-    // Size occupied by each pixel in bytes
-    size_t sizePerPixel() {
-        switch (format) {
-            case Formats::Depth16: return 2;
-            case Formats::Depth24: return 3;
-            case Formats::Depth24Stencil8: return 4;
-
-            default: return 1; // Invalid format
-        }
-    }
-
     size_t sizeInBytes() {
-        return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
+        return (size_t)size.x() * (size_t)size.y() * sizePerPixel(format);
     }
 };
\ No newline at end of file
diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp
index f0e832a2..a8e79417 100644
--- a/src/core/PICA/gpu.cpp
+++ b/src/core/PICA/gpu.cpp
@@ -55,11 +55,12 @@ void GPU::drawArrays() {
 
 	// Configures the type of primitive and the number of vertex shader outputs
 	const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
-	const u32 primType = Helpers::getBits<8, 2>(primConfig);
-	if (primType != 0 && primType != 1 && primType != 3) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
+	const PICAPrimType primType = static_cast<PICAPrimType>(Helpers::getBits<8, 2>(primConfig));
+	if (primType == PICAPrimType::TriangleFan) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
 	if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
 
-	if ((primType == 0 && vertexCount % 3) || (primType == 1 && vertexCount < 3)) {
+	if ((primType == PICAPrimType::TriangleList && vertexCount % 3) ||
+		(primType == PICAPrimType::TriangleStrip && vertexCount < 3)) {
 		Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
 	}
 
@@ -203,12 +204,7 @@ void GPU::drawArrays() {
 		//printf("(u, v      ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
 	}
 
-	// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
-	static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
-		OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
-	};
-	const auto shape = primTypes[primType];
-	renderer.drawVertices(shape, std::span(vertices).first(vertexCount));
+	renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
 }
 
 Vertex GPU::getImmediateModeVertex() {
diff --git a/src/core/PICA/regs.cpp b/src/core/PICA/regs.cpp
index 26feaf9d..9dbd584b 100644
--- a/src/core/PICA/regs.cpp
+++ b/src/core/PICA/regs.cpp
@@ -68,7 +68,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
 
 		case ColourBufferFormat: {
 			u32 format = getBits<16, 3>(value);
-			renderer.setColourFormat(format);
+			renderer.setColourFormat(static_cast<PICAColorFmt>(format));
 			break;
 		}
 
@@ -79,8 +79,8 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
 		}
 
 		case DepthBufferFormat: {
-			u32 fmt = value & 0x3;
-			renderer.setDepthFormat(fmt);
+			u32 format = value & 0x3;
+			renderer.setDepthFormat(static_cast<PICADepthFmt>(format));
 			break;
 		}
 
@@ -157,7 +157,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
 						// If we've reached 3 verts, issue a draw call
 						// Handle rendering depending on the primitive type
 						if (immediateModeVertIndex == 3) {
-							renderer.drawVertices(OpenGL::Triangle, immediateModeVertices);
+							renderer.drawVertices(PICAPrimType::TriangleList, immediateModeVertices);
 
 							switch (primType) {
 								// Triangle or geometry primitive. Draw a triangle and discard all vertices
diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp
index 9265e2f4..1a8f1f5b 100644
--- a/src/core/renderer_gl/renderer_gl.cpp
+++ b/src/core/renderer_gl/renderer_gl.cpp
@@ -144,10 +144,10 @@ void Renderer::reset() {
 
 	// Init the colour/depth buffer settings to some random defaults on reset
 	colourBufferLoc = 0;
-	colourBufferFormat = ColourBuffer::Formats::RGBA8;
+	colourBufferFormat = PICAColorFmt::RGBA8;
 
 	depthBufferLoc = 0;
-	depthBufferFormat = DepthBuffer::Formats::Depth16;
+	depthBufferFormat = PICADepthFmt::Depth16;
 
 	if (triangleProgram.exists()) {
 		const auto oldProgram = OpenGL::getProgram();
@@ -264,7 +264,13 @@ void Renderer::setupBlending() {
 	}
 }
 
-void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex> vertices) {
+void Renderer::drawVertices(PICAPrimType primType, std::span<const Vertex> vertices) {
+	// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
+	static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
+		OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
+	};
+	const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
+
 	// Adjust alpha test if necessary
 	const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
 	if (alphaControl != oldAlphaControl) {
@@ -352,7 +358,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
 	}
 
 	vbo.bufferVertsSub(vertices);
-	OpenGL::draw(primType, vertices.size());
+	OpenGL::draw(primitiveTopology, vertices.size());
 }
 
 constexpr u32 topScreenBuffer = 0x1f000000;
@@ -423,8 +429,8 @@ void Renderer::bindDepthBuffer() {
 		tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
 	}
 
-	if (DepthBuffer::Formats::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
-	auto attachment = depthBufferFormat == DepthBuffer::Formats::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
+	if (PICADepthFmt::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
+	auto attachment = depthBufferFormat == PICADepthFmt::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
 	glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
 }