mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-09 07:35:41 +12:00
Introduce "Renderer" abstraction layer
Adds a `renderer` class for which a rendering backend must implement and will conditionally use OpenGL in the case that `ENABLE_GL` is enabled.
This commit is contained in:
parent
d664d5caf0
commit
2a1683ba62
9 changed files with 224 additions and 156 deletions
|
@ -93,7 +93,7 @@ endif()
|
|||
|
||||
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/io_file.cpp src/config.cpp
|
||||
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
||||
src/core/memory.cpp src/httpserver.cpp src/stb_image_write.c
|
||||
src/core/memory.cpp src/renderer.cpp src/httpserver.cpp src/stb_image_write.c
|
||||
)
|
||||
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||
|
|
|
@ -21,7 +21,7 @@ class ShaderJIT {
|
|||
ShaderCache cache;
|
||||
#endif
|
||||
|
||||
public:
|
||||
public:
|
||||
#ifdef PANDA3DS_SHADER_JIT_SUPPORTED
|
||||
// Call this before starting to process a batch of vertices
|
||||
// This will read the PICA config (uploaded shader and shader operand descriptors) and search if we've already compiled this shader
|
||||
|
@ -29,9 +29,7 @@ public:
|
|||
// The caller must make sure the entrypoint has been properly set beforehand
|
||||
void prepare(PICAShader& shaderUnit);
|
||||
void reset();
|
||||
void run(PICAShader& shaderUnit) {
|
||||
prologueCallback(shaderUnit, entrypointCallback);
|
||||
}
|
||||
void run(PICAShader& shaderUnit) { prologueCallback(shaderUnit, entrypointCallback); }
|
||||
|
||||
static constexpr bool isAvailable() { return true; }
|
||||
#else
|
||||
|
@ -44,7 +42,7 @@ public:
|
|||
}
|
||||
|
||||
// Define dummy callback. This should never be called if the shader JIT is not supported
|
||||
using Callback = void(*)(PICAShader& shaderUnit);
|
||||
using Callback = void (*)(PICAShader& shaderUnit);
|
||||
Callback activeShaderCallback = nullptr;
|
||||
|
||||
void reset() {}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "PICA/dynapica/shader_rec.hpp"
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/pica_vertex.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/shader_unit.hpp"
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/shader_unit.hpp"
|
||||
#include "PICA/dynapica/shader_rec.hpp"
|
||||
#include "renderer_gl/renderer_gl.hpp"
|
||||
#include "PICA/pica_vertex.hpp"
|
||||
#include "renderer.hpp"
|
||||
|
||||
class GPU {
|
||||
static constexpr u32 regNum = 0x300;
|
||||
using vec4f = OpenGL::Vector<Floats::f24, 4>;
|
||||
using vec4f = std::array<Floats::f24, 4>;
|
||||
using Registers = std::array<u32, regNum>;
|
||||
|
||||
Memory& mem;
|
||||
|
@ -48,9 +48,7 @@ class GPU {
|
|||
u32 config2 = 0;
|
||||
u32 componentCount = 0; // Number of components for the attribute
|
||||
|
||||
u64 getConfigFull() {
|
||||
return u64(config1) | (u64(config2) << 32);
|
||||
}
|
||||
u64 getConfigFull() { return u64(config1) | (u64(config2) << 32); }
|
||||
};
|
||||
|
||||
u64 getVertexShaderInputConfig() {
|
||||
|
@ -70,7 +68,7 @@ class GPU {
|
|||
u32* cmdBuffEnd = nullptr;
|
||||
u32* cmdBuffCurr = nullptr;
|
||||
|
||||
Renderer renderer;
|
||||
std::unique_ptr<Renderer> renderer;
|
||||
PICA::Vertex getImmediateModeVertex();
|
||||
|
||||
public:
|
||||
|
@ -84,11 +82,9 @@ class GPU {
|
|||
// Set to false by the renderer when the lighting_lut is uploaded ot the GPU
|
||||
bool lightingLUTDirty = false;
|
||||
|
||||
GPU(Memory& mem, GLStateManager& gl, EmulatorConfig& config);
|
||||
void initGraphicsContext() { renderer.initGraphicsContext(); }
|
||||
void getGraphicsContext() { renderer.getGraphicsContext(); }
|
||||
void display() { renderer.display(); }
|
||||
void screenshot(const std::string& name) { renderer.screenshot(name); }
|
||||
GPU(Memory& mem, EmulatorConfig& config);
|
||||
void initGraphicsContext() { renderer->initGraphicsContext(); }
|
||||
void display() { renderer->display(); }
|
||||
|
||||
void fireDMA(u32 dest, u32 source, u32 size);
|
||||
void reset();
|
||||
|
@ -106,14 +102,12 @@ class GPU {
|
|||
|
||||
// TODO: Emulate the transfer engine & its registers
|
||||
// Then this can be emulated by just writing the appropriate values there
|
||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
renderer.clearBuffer(startAddress, endAddress, value, control);
|
||||
}
|
||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { renderer->clearBuffer(startAddress, endAddress, value, control); }
|
||||
|
||||
// TODO: Emulate the transfer engine & its registers
|
||||
// Then this can be emulated by just writing the appropriate values there
|
||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {
|
||||
renderer.displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags);
|
||||
renderer->displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags);
|
||||
}
|
||||
|
||||
// Read a value of type T from physical address paddr
|
||||
|
|
37
include/renderer.hpp
Normal file
37
include/renderer.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
#include "PICA/pica_vertex.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
class GPU;
|
||||
|
||||
class Renderer {
|
||||
protected:
|
||||
GPU& gpu;
|
||||
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
||||
const std::array<u32, regNum>& regs;
|
||||
|
||||
public:
|
||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs);
|
||||
virtual ~Renderer();
|
||||
|
||||
static constexpr u32 vertexBufferSize = 0x10000;
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void display() = 0; // Display the 3DS screen contents to the window
|
||||
virtual void initGraphicsContext() = 0; // Initialize graphics context
|
||||
virtual void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) = 0; // Clear a GPU buffer in VRAM
|
||||
virtual void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) = 0; // Perform display transfer
|
||||
virtual void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) = 0; // Draw the given vertices
|
||||
|
||||
virtual void setFBSize(u32 width, u32 height) = 0;
|
||||
|
||||
virtual void setColourFormat(PICA::ColorFmt format) = 0;
|
||||
virtual void setDepthFormat(PICA::DepthFmt format) = 0;
|
||||
|
||||
virtual void setColourBufferLoc(u32 loc) = 0;
|
||||
virtual void setDepthBufferLoc(u32 loc) = 0;
|
||||
};
|
|
@ -4,20 +4,20 @@
|
|||
#include <stb_image_write.h>
|
||||
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/pica_vertex.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "gl_state.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "surface_cache.hpp"
|
||||
#include "textures.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/pica_vertex.hpp"
|
||||
|
||||
// More circular dependencies!
|
||||
class GPU;
|
||||
|
||||
class Renderer {
|
||||
GPU& gpu;
|
||||
GLStateManager& gl;
|
||||
class RendererGL final : public Renderer {
|
||||
GLStateManager gl = {};
|
||||
|
||||
OpenGL::Program triangleProgram;
|
||||
OpenGL::Program displayProgram;
|
||||
|
@ -61,9 +61,6 @@ class Renderer {
|
|||
OpenGL::VertexArray dummyVAO;
|
||||
OpenGL::VertexBuffer dummyVBO;
|
||||
|
||||
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
||||
const std::array<u32, regNum>& regs;
|
||||
|
||||
OpenGL::Texture screenTexture;
|
||||
GLuint lightLUTTextureArray;
|
||||
OpenGL::Framebuffer screenFramebuffer;
|
||||
|
@ -79,12 +76,11 @@ class Renderer {
|
|||
void updateLightingLUT();
|
||||
|
||||
public:
|
||||
Renderer(GPU& gpu, GLStateManager& gl, const std::array<u32, regNum>& internalRegs) : gpu(gpu), gl(gl), regs(internalRegs) {}
|
||||
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs) : Renderer(gpu, internalRegs) {}
|
||||
|
||||
void reset();
|
||||
void display(); // Display the 3DS screen contents to the window
|
||||
void initGraphicsContext(); // Initialize graphics context
|
||||
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(PICA::PrimType primType, std::span<const PICA::Vertex> vertices); // Draw the given vertices
|
||||
|
@ -107,6 +103,4 @@ class Renderer {
|
|||
|
||||
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
||||
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
|
||||
|
||||
static constexpr u32 vertexBufferSize = 0x10000;
|
||||
};
|
|
@ -2,19 +2,28 @@
|
|||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
|
||||
#if ENABLE_OPENGL
|
||||
#include "renderer_gl/renderer_gl.hpp"
|
||||
#endif
|
||||
|
||||
using namespace Floats;
|
||||
|
||||
// Note: For when we have multiple backends, the GL state manager can stay here and have the constructor for the Vulkan-or-whatever renderer ignore it
|
||||
// Thus, our GLStateManager being here does not negatively impact renderer-agnosticness
|
||||
GPU::GPU(Memory& mem, GLStateManager& gl, EmulatorConfig& config) : mem(mem), renderer(*this, gl, regs), config(config) {
|
||||
GPU::GPU(Memory& mem, EmulatorConfig& config) : mem(mem), config(config) {
|
||||
vram = new u8[vramSize];
|
||||
mem.setVRAM(vram); // Give the bus a pointer to our VRAM
|
||||
|
||||
// TODO: configurable backend
|
||||
#if ENABLE_OPENGL
|
||||
renderer.reset(new RendererGL(*this, regs));
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPU::reset() {
|
||||
|
@ -41,7 +50,7 @@ void GPU::reset() {
|
|||
e.config2 = 0;
|
||||
}
|
||||
|
||||
renderer.reset();
|
||||
renderer->reset();
|
||||
}
|
||||
|
||||
// Call the correct version of drawArrays based on whether this is an indexed draw (first template parameter)
|
||||
|
@ -80,8 +89,7 @@ void GPU::drawArrays() {
|
|||
const PICA::PrimType primType = static_cast<PICA::PrimType>(Helpers::getBits<8, 2>(primConfig));
|
||||
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
||||
|
||||
if ((primType == PICA::PrimType::TriangleList && vertexCount % 3) ||
|
||||
(primType == PICA::PrimType::TriangleStrip && vertexCount < 3) ||
|
||||
if ((primType == PICA::PrimType::TriangleList && vertexCount % 3) || (primType == PICA::PrimType::TriangleStrip && vertexCount < 3) ||
|
||||
(primType == PICA::PrimType::TriangleFan && vertexCount < 3)) {
|
||||
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
|
||||
}
|
||||
|
@ -182,7 +190,7 @@ void GPU::drawArrays() {
|
|||
u32 attribType = attribInfo & 0x3; // Type of attribute(sbyte/ubyte/short/float)
|
||||
u32 size = (attribInfo >> 2) + 1; // Total number of components
|
||||
|
||||
//printf("vertex_attribute_strides[%d] = %d\n", attrCount, attr.size);
|
||||
// printf("vertex_attribute_strides[%d] = %d\n", attrCount, attr.size);
|
||||
vec4f& attribute = currentAttributes[attrCount];
|
||||
uint component; // Current component
|
||||
|
||||
|
@ -271,7 +279,7 @@ void GPU::drawArrays() {
|
|||
}
|
||||
}
|
||||
|
||||
renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
|
||||
renderer->drawVertices(primType, std::span(vertices).first(vertexCount));
|
||||
}
|
||||
|
||||
PICA::Vertex GPU::getImmediateModeVertex() {
|
||||
|
@ -289,7 +297,9 @@ PICA::Vertex GPU::getImmediateModeVertex() {
|
|||
std::memcpy(&v.s.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
|
||||
std::memcpy(&v.s.texcoord0, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
|
||||
|
||||
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(
|
||||
"(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.s.colour[0], (double)v.s.colour[1], (double)v.s.colour[2], (double)v.s.colour[3]);
|
||||
printf("(u, v ) = (%f, %f)\n", (double)v.s.texcoord0[0], (double)v.s.texcoord0[1]);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "renderer_gl/renderer_gl.hpp"
|
||||
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
|
@ -576,7 +577,7 @@ const char* displayFragmentShader = R"(
|
|||
}
|
||||
)";
|
||||
|
||||
void Renderer::reset() {
|
||||
void RendererGL::reset() {
|
||||
depthBufferCache.reset();
|
||||
colourBufferCache.reset();
|
||||
textureCache.reset();
|
||||
|
@ -605,10 +606,10 @@ void Renderer::reset() {
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::initGraphicsContext() {
|
||||
void RendererGL::initGraphicsContext() {
|
||||
OpenGL::Shader vert(vertexShader, OpenGL::Vertex);
|
||||
OpenGL::Shader frag(fragmentShader, OpenGL::Fragment);
|
||||
triangleProgram.create({ vert, frag });
|
||||
triangleProgram.create({vert, frag});
|
||||
gl.useProgram(triangleProgram);
|
||||
|
||||
textureEnvSourceLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvSource");
|
||||
|
@ -630,7 +631,7 @@ void Renderer::initGraphicsContext() {
|
|||
|
||||
OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex);
|
||||
OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment);
|
||||
displayProgram.create({ vertDisplay, fragDisplay });
|
||||
displayProgram.create({vertDisplay, fragDisplay});
|
||||
|
||||
gl.useProgram(displayProgram);
|
||||
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
|
||||
|
@ -672,7 +673,7 @@ void Renderer::initGraphicsContext() {
|
|||
const u32 screenTextureWidth = 400; // Top screen is 400 pixels wide, bottom is 320
|
||||
const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall
|
||||
|
||||
glGenTextures(1,&lightLUTTextureArray);
|
||||
glGenTextures(1, &lightLUTTextureArray);
|
||||
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
screenTexture.create(screenTextureWidth, screenTextureHeight, GL_RGBA8);
|
||||
|
@ -684,8 +685,7 @@ void Renderer::initGraphicsContext() {
|
|||
screenFramebuffer.createWithDrawTexture(screenTexture);
|
||||
screenFramebuffer.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
Helpers::panic("Incomplete framebuffer");
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) Helpers::panic("Incomplete framebuffer");
|
||||
|
||||
// TODO: This should not clear the framebuffer contents. It should load them from VRAM.
|
||||
GLint oldViewport[4];
|
||||
|
@ -699,20 +699,31 @@ void Renderer::initGraphicsContext() {
|
|||
}
|
||||
|
||||
// Set up the OpenGL blending context to match the emulated PICA
|
||||
void Renderer::setupBlending() {
|
||||
void RendererGL::setupBlending() {
|
||||
const bool blendingEnabled = (regs[PICA::InternalRegs::ColourOperation] & (1 << 8)) != 0;
|
||||
|
||||
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
|
||||
static constexpr std::array<GLenum, 8> blendingEquations = {
|
||||
GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX, GL_FUNC_ADD, GL_FUNC_ADD, GL_FUNC_ADD
|
||||
};
|
||||
static constexpr std::array<GLenum, 8> blendingEquations = {GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX, GL_FUNC_ADD,
|
||||
GL_FUNC_ADD, GL_FUNC_ADD};
|
||||
|
||||
// Map of PICA blending funcs to OpenGL blending funcs. Func = 15 is undocumented and stubbed to GL_ONE for now
|
||||
static constexpr std::array<GLenum, 16> blendingFuncs = {
|
||||
GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA,
|
||||
GL_SRC_ALPHA_SATURATE, GL_ONE
|
||||
};
|
||||
GL_ZERO,
|
||||
GL_ONE,
|
||||
GL_SRC_COLOR,
|
||||
GL_ONE_MINUS_SRC_COLOR,
|
||||
GL_DST_COLOR,
|
||||
GL_ONE_MINUS_DST_COLOR,
|
||||
GL_SRC_ALPHA,
|
||||
GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_DST_ALPHA,
|
||||
GL_ONE_MINUS_DST_ALPHA,
|
||||
GL_CONSTANT_COLOR,
|
||||
GL_ONE_MINUS_CONSTANT_COLOR,
|
||||
GL_CONSTANT_ALPHA,
|
||||
GL_ONE_MINUS_CONSTANT_ALPHA,
|
||||
GL_SRC_ALPHA_SATURATE,
|
||||
GL_ONE};
|
||||
|
||||
if (!blendingEnabled) {
|
||||
gl.disableBlend();
|
||||
|
@ -743,14 +754,12 @@ void Renderer::setupBlending() {
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::setupTextureEnvState() {
|
||||
void RendererGL::setupTextureEnvState() {
|
||||
// TODO: Only update uniforms when the TEV config changed. Use an UBO potentially.
|
||||
|
||||
static constexpr std::array<u32, 6> ioBases = {
|
||||
PICA::InternalRegs::TexEnv0Source, PICA::InternalRegs::TexEnv1Source,
|
||||
static constexpr std::array<u32, 6> ioBases = {PICA::InternalRegs::TexEnv0Source, PICA::InternalRegs::TexEnv1Source,
|
||||
PICA::InternalRegs::TexEnv2Source, PICA::InternalRegs::TexEnv3Source,
|
||||
PICA::InternalRegs::TexEnv4Source, PICA::InternalRegs::TexEnv5Source
|
||||
};
|
||||
PICA::InternalRegs::TexEnv4Source, PICA::InternalRegs::TexEnv5Source};
|
||||
|
||||
u32 textureEnvSourceRegs[6];
|
||||
u32 textureEnvOperandRegs[6];
|
||||
|
@ -775,10 +784,9 @@ void Renderer::setupTextureEnvState() {
|
|||
glUniform1uiv(textureEnvScaleLoc, 6, textureEnvScaleRegs);
|
||||
}
|
||||
|
||||
void Renderer::bindTexturesToSlots() {
|
||||
void RendererGL::bindTexturesToSlots() {
|
||||
static constexpr std::array<u32, 3> ioBases = {
|
||||
PICA::InternalRegs::Tex0BorderColor, PICA::InternalRegs::Tex1BorderColor, PICA::InternalRegs::Tex2BorderColor
|
||||
};
|
||||
PICA::InternalRegs::Tex0BorderColor, PICA::InternalRegs::Tex1BorderColor, PICA::InternalRegs::Tex2BorderColor};
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if ((regs[PICA::InternalRegs::TexUnitCfg] & (1 << i)) == 0) {
|
||||
|
@ -805,7 +813,7 @@ void Renderer::bindTexturesToSlots() {
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void Renderer::updateLightingLUT() {
|
||||
void RendererGL::updateLightingLUT() {
|
||||
gpu.lightingLUTDirty = false;
|
||||
std::array<u16, GPU::LightingLutSize> u16_lightinglut;
|
||||
|
||||
|
@ -824,11 +832,9 @@ void Renderer::updateLightingLUT() {
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices) {
|
||||
void RendererGL::drawVertices(PICA::PrimType 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
|
||||
};
|
||||
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle};
|
||||
const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
|
||||
|
||||
gl.disableScissor();
|
||||
|
@ -852,9 +858,7 @@ void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> ver
|
|||
const int colourMask = getBits<8, 4>(depthControl);
|
||||
gl.setColourMask(colourMask & 1, colourMask & 2, colourMask & 4, colourMask & 8);
|
||||
|
||||
static constexpr std::array<GLenum, 8> depthModes = {
|
||||
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL
|
||||
};
|
||||
static constexpr std::array<GLenum, 8> depthModes = {GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL};
|
||||
|
||||
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
|
||||
const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
||||
|
@ -917,7 +921,7 @@ void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> ver
|
|||
constexpr u32 topScreenBuffer = 0x1f000000;
|
||||
constexpr u32 bottomScreenBuffer = 0x1f05dc00;
|
||||
|
||||
void Renderer::display() {
|
||||
void RendererGL::display() {
|
||||
gl.disableScissor();
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
@ -925,7 +929,7 @@ void Renderer::display() {
|
|||
glBlitFramebuffer(0, 0, 400, 480, 0, 0, 400, 480, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
|
||||
void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
void RendererGL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
return;
|
||||
log("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control);
|
||||
|
||||
|
@ -947,8 +951,8 @@ void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 cont
|
|||
OpenGL::clearColor();
|
||||
}
|
||||
|
||||
OpenGL::Framebuffer Renderer::getColourFBO() {
|
||||
//We construct a colour buffer object and see if our cache has any matching colour buffers in it
|
||||
OpenGL::Framebuffer RendererGL::getColourFBO() {
|
||||
// We construct a colour buffer object and see if our cache has any matching colour buffers in it
|
||||
// If not, we allocate a texture & FBO for our framebuffer and store it in the cache
|
||||
ColourBuffer sampleBuffer(colourBufferLoc, colourBufferFormat, fbSize.x(), fbSize.y());
|
||||
auto buffer = colourBufferCache.find(sampleBuffer);
|
||||
|
@ -960,7 +964,7 @@ OpenGL::Framebuffer Renderer::getColourFBO() {
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::bindDepthBuffer() {
|
||||
void RendererGL::bindDepthBuffer() {
|
||||
// Similar logic as the getColourFBO function
|
||||
DepthBuffer sampleBuffer(depthBufferLoc, depthBufferFormat, fbSize.x(), fbSize.y());
|
||||
auto buffer = depthBufferCache.find(sampleBuffer);
|
||||
|
@ -979,7 +983,7 @@ void Renderer::bindDepthBuffer() {
|
|||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
|
||||
}
|
||||
|
||||
OpenGL::Texture Renderer::getTexture(Texture& tex) {
|
||||
OpenGL::Texture RendererGL::getTexture(Texture& tex) {
|
||||
// Similar logic as the getColourFBO/bindDepthBuffer functions
|
||||
auto buffer = textureCache.find(tex);
|
||||
|
||||
|
@ -994,7 +998,7 @@ OpenGL::Texture Renderer::getTexture(Texture& tex) {
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {
|
||||
void RendererGL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {
|
||||
const u32 inputWidth = inputSize & 0xffff;
|
||||
const u32 inputGap = inputSize >> 16;
|
||||
|
||||
|
@ -1036,7 +1040,34 @@ void Renderer::screenshot(const std::string& name) {
|
|||
|
||||
std::vector<uint8_t> pixels, flippedPixels;
|
||||
pixels.resize(width * height * 4);
|
||||
flippedPixels.resize(pixels.size());;
|
||||
flippedPixels.resize(pixels.size());
|
||||
;
|
||||
|
||||
OpenGL::bindScreenFramebuffer();
|
||||
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
|
||||
// Flip the image vertically
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(&flippedPixels[y * width * 4], &pixels[(height - y - 1) * width * 4], width * 4);
|
||||
// Swap R and B channels
|
||||
for (int x = 0; x < width; x++) {
|
||||
std::swap(flippedPixels[y * width * 4 + x * 4 + 0], flippedPixels[y * width * 4 + x * 4 + 2]);
|
||||
// Set alpha to 0xFF
|
||||
flippedPixels[y * width * 4 + x * 4 + 3] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
stbi_write_png(name.c_str(), width, height, 4, flippedPixels.data(), 0);
|
||||
}
|
||||
|
||||
void Renderer::screenshot(const std::string& name) {
|
||||
constexpr uint width = 400;
|
||||
constexpr uint height = 2 * 240;
|
||||
|
||||
std::vector<uint8_t> pixels, flippedPixels;
|
||||
pixels.resize(width * height * 4);
|
||||
flippedPixels.resize(pixels.size());
|
||||
;
|
||||
|
||||
OpenGL::bindScreenFramebuffer();
|
||||
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
|
|
|
@ -16,7 +16,7 @@ _declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
|
|||
}
|
||||
#endif
|
||||
|
||||
Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, gl, config), memory(cpu.getTicksRef()) {
|
||||
Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config), memory(cpu.getTicksRef()) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||
Helpers::panic("Failed to initialize SDL2");
|
||||
}
|
||||
|
|
4
src/renderer.cpp
Normal file
4
src/renderer.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "renderer.hpp"
|
||||
|
||||
Renderer::Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
||||
Renderer::~Renderer() {}
|
Loading…
Add table
Reference in a new issue