Merge pull request #141 from GPUCode/stencil-logicop-clear

Depth buffer fixes
This commit is contained in:
wheremyfoodat 2023-07-28 03:24:02 +03:00 committed by GitHub
commit 6a6b3cf892
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 295 additions and 86 deletions

View file

@ -53,9 +53,13 @@ namespace PICA {
// Framebuffer registers // Framebuffer registers
ColourOperation = 0x100, ColourOperation = 0x100,
BlendFunc = 0x101, BlendFunc = 0x101,
LogicOp = 0x102,
BlendColour = 0x103, BlendColour = 0x103,
AlphaTestConfig = 0x104, AlphaTestConfig = 0x104,
StencilTest = 0x105,
StencilOp = 0x106,
DepthAndColorMask = 0x107, DepthAndColorMask = 0x107,
DepthBufferWrite = 0x115,
DepthBufferFormat = 0x116, DepthBufferFormat = 0x116,
ColourBufferFormat = 0x117, ColourBufferFormat = 0x117,
DepthBufferLoc = 0x11C, DepthBufferLoc = 0x11C,
@ -292,4 +296,4 @@ namespace PICA {
GeometryPrimitive = 3, GeometryPrimitive = 3,
}; };
} // namespace PICA } // namespace PICA

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include "helpers.hpp"
#include "opengl.hpp" #include "opengl.hpp"
// GL state manager object for use in the OpenGL GPU renderer and potentially other things in the future (such as a potential ImGui GUI) // GL state manager object for use in the OpenGL GPU renderer and potentially other things in the future (such as a potential ImGui GUI)
@ -18,28 +19,39 @@
// backend-agnostic as possible // backend-agnostic as possible
struct GLStateManager { struct GLStateManager {
// We only support 6 clipping planes in our state manager because that's the minimum for GL_MAX_CLIP_PLANES
// And nobody needs more than 6 clip planes anyways
static constexpr GLint clipPlaneCount = 6;
bool blendEnabled; bool blendEnabled;
bool logicOpEnabled;
bool depthEnabled; bool depthEnabled;
bool scissorEnabled; bool scissorEnabled;
bool stencilEnabled;
u32 enabledClipPlanes; // Bitfield of enabled clip planes
// Colour/depth masks // Colour/depth masks
bool redMask, greenMask, blueMask, alphaMask; bool redMask, greenMask, blueMask, alphaMask;
bool depthMask; bool depthMask;
GLuint stencilMask;
GLuint boundVAO; GLuint boundVAO;
GLuint boundVBO; GLuint boundVBO;
GLuint currentProgram; GLuint currentProgram;
GLenum depthFunc; GLenum depthFunc;
GLenum logicOp;
void reset(); void reset();
void resetBlend(); void resetBlend();
void resetClipping();
void resetColourMask(); void resetColourMask();
void resetDepth(); void resetDepth();
void resetVAO(); void resetVAO();
void resetVBO(); void resetVBO();
void resetProgram(); void resetProgram();
void resetScissor(); void resetScissor();
void resetStencil();
void enableDepth() { void enableDepth() {
if (!depthEnabled) { if (!depthEnabled) {
@ -83,6 +95,70 @@ struct GLStateManager {
} }
} }
void enableStencil() {
if (!stencilEnabled) {
stencilEnabled = true;
OpenGL::enableStencil();
}
}
void disableStencil() {
if (stencilEnabled) {
stencilEnabled = false;
OpenGL::disableStencil();
}
}
void enableLogicOp() {
if (!logicOpEnabled) {
logicOpEnabled = true;
OpenGL::enableLogicOp();
}
}
void disableLogicOp() {
if (logicOpEnabled) {
logicOpEnabled = false;
OpenGL::disableLogicOp();
}
}
void setLogicOp(GLenum op) {
if (logicOp != op) {
logicOp = op;
OpenGL::setLogicOp(op);
}
}
void enableClipPlane(GLuint index) {
if (index >= clipPlaneCount) [[unlikely]] {
Helpers::panic("Enabled invalid clipping plane %d\n", index);
}
if ((enabledClipPlanes & (1 << index)) == 0) {
enabledClipPlanes |= 1 << index; // Enable relevant bit in clipping plane bitfield
OpenGL::enableClipPlane(index); // Enable plane
}
}
void disableClipPlane(GLuint index) {
if (index >= clipPlaneCount) [[unlikely]] {
Helpers::panic("Disabled invalid clipping plane %d\n", index);
}
if ((enabledClipPlanes & (1 << index)) != 0) {
enabledClipPlanes ^= 1 << index; // Disable relevant bit in bitfield by flipping it
OpenGL::disableClipPlane(index); // Disable plane
}
}
void setStencilMask(GLuint mask) {
if (stencilMask != mask) {
stencilMask = mask;
OpenGL::setStencilMask(mask);
}
}
void bindVAO(GLuint handle) { void bindVAO(GLuint handle) {
if (boundVAO != handle) { if (boundVAO != handle) {
boundVAO = handle; boundVAO = handle;

View file

@ -333,18 +333,18 @@ namespace OpenGL {
void bind(FramebufferTypes target) const { bind(static_cast<GLenum>(target)); } void bind(FramebufferTypes target) const { bind(static_cast<GLenum>(target)); }
void free() { glDeleteFramebuffers(1, &m_handle); } void free() { glDeleteFramebuffers(1, &m_handle); }
void createWithTexture(Texture& tex, GLenum mode = GL_FRAMEBUFFER, GLenum textureType = GL_TEXTURE_2D) { void createWithTexture(Texture& tex, GLenum mode = GL_FRAMEBUFFER, GLenum attachment = GL_COLOR_ATTACHMENT0, GLenum textureType = GL_TEXTURE_2D) {
m_textureType = textureType; m_textureType = textureType;
create(); create();
bind(mode); bind(mode);
glFramebufferTexture2D(mode, GL_COLOR_ATTACHMENT0, textureType, tex.handle(), 0); glFramebufferTexture2D(mode, attachment, textureType, tex.handle(), 0);
} }
void createWithReadTexture(Texture& tex, GLenum textureType = GL_TEXTURE_2D) { void createWithReadTexture(Texture& tex, GLenum attachment = GL_COLOR_ATTACHMENT0, GLenum textureType = GL_TEXTURE_2D) {
createWithTexture(tex, GL_READ_FRAMEBUFFER, textureType); createWithTexture(tex, GL_READ_FRAMEBUFFER, attachment, textureType);
} }
void createWithDrawTexture(Texture& tex, GLenum textureType = GL_TEXTURE_2D) { void createWithDrawTexture(Texture& tex, GLenum attachment = GL_COLOR_ATTACHMENT0, GLenum textureType = GL_TEXTURE_2D) {
createWithTexture(tex, GL_DRAW_FRAMEBUFFER, textureType); createWithTexture(tex, GL_DRAW_FRAMEBUFFER, attachment, textureType);
} }
void createWithTextureMSAA(Texture& tex, GLenum mode = GL_FRAMEBUFFER) { void createWithTextureMSAA(Texture& tex, GLenum mode = GL_FRAMEBUFFER) {
@ -497,40 +497,45 @@ namespace OpenGL {
}; };
static void setClearColor(float val) { glClearColor(val, val, val, val); } static void setClearColor(float val) { glClearColor(val, val, val, val); }
static void setClearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); } static void setClearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); }
static void setClearDepth(float depth) { glClearDepthf(depth); } static void setClearDepth(float depth) { glClearDepthf(depth); }
static void setClearStencil(GLint stencil) { glClearStencil(stencil); } static void setClearStencil(GLint stencil) { glClearStencil(stencil); }
static void clearColor() { glClear(GL_COLOR_BUFFER_BIT); } static void clearColor() { glClear(GL_COLOR_BUFFER_BIT); }
static void clearDepth() { glClear(GL_DEPTH_BUFFER_BIT); } static void clearDepth() { glClear(GL_DEPTH_BUFFER_BIT); }
static void clearStencil() { glClear(GL_STENCIL_BUFFER_BIT); } static void clearStencil() { glClear(GL_STENCIL_BUFFER_BIT); }
static void clearColorAndDepth() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } static void clearColorAndDepth() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
static void clearColorAndStencil() { glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } static void clearColorAndStencil() { glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
static void clearDepthAndStencil() { glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } static void clearDepthAndStencil() { glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
static void clearAll() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } static void clearAll() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
static void setViewport(GLsizei width, GLsizei height) { glViewport(0, 0, width, height); } static void setViewport(GLsizei width, GLsizei height) { glViewport(0, 0, width, height); }
static void setViewport(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glViewport(x, y, width, height); } static void setViewport(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glViewport(x, y, width, height); }
static void setScissor(GLsizei width, GLsizei height) { glScissor(0, 0, width, height); } static void setScissor(GLsizei width, GLsizei height) { glScissor(0, 0, width, height); }
static void setScissor(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glScissor(x, y, width, height); } static void setScissor(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glScissor(x, y, width, height); }
static void setStencilMask(GLuint mask) { glStencilMask(mask); } static void setStencilMask(GLuint mask) { glStencilMask(mask); }
static void bindScreenFramebuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } static void bindScreenFramebuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
static void enableScissor() { glEnable(GL_SCISSOR_TEST); } static void enableScissor() { glEnable(GL_SCISSOR_TEST); }
static void disableScissor() { glDisable(GL_SCISSOR_TEST); } static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
static void enableBlend() { glEnable(GL_BLEND); } static void enableBlend() { glEnable(GL_BLEND); }
static void disableBlend() { glDisable(GL_BLEND); } static void disableBlend() { glDisable(GL_BLEND); }
static void enableDepth() { glEnable(GL_DEPTH_TEST); } static void enableLogicOp() { glEnable(GL_COLOR_LOGIC_OP); }
static void disableDepth() { glDisable(GL_DEPTH_TEST); } static void disableLogicOp() { glDisable(GL_COLOR_LOGIC_OP); }
static void enableStencil() { glEnable(GL_STENCIL_TEST); } static void enableDepth() { glEnable(GL_DEPTH_TEST); }
static void disableStencil() { glDisable(GL_STENCIL_TEST); } static void disableDepth() { glDisable(GL_DEPTH_TEST); }
static void enableStencil() { glEnable(GL_STENCIL_TEST); }
static void disableStencil() { glDisable(GL_STENCIL_TEST); }
static void enableClipPlane(GLuint index) { glEnable(GL_CLIP_DISTANCE0 + index); } static void enableClipPlane(GLuint index) { glEnable(GL_CLIP_DISTANCE0 + index); }
static void disableClipPlane(GLuint index) { glDisable(GL_CLIP_DISTANCE0 + index); } static void disableClipPlane(GLuint index) { glDisable(GL_CLIP_DISTANCE0 + index); }
static void setDepthFunc(DepthFunc func) { glDepthFunc(static_cast<GLenum>(func)); } static void setDepthFunc(DepthFunc func) { glDepthFunc(static_cast<GLenum>(func)); }
static void setColourMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { glColorMask(r, g, b, a); } static void setColourMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { glColorMask(r, g, b, a); }
static void setDepthMask(GLboolean mask) { glDepthMask(mask); } static void setDepthMask(GLboolean mask) { glDepthMask(mask); }
// TODO: Add a proper enum for this
static void setLogicOp(GLenum op) { glLogicOp(op); }
enum Primitives { enum Primitives {
Triangle = GL_TRIANGLES, Triangle = GL_TRIANGLES,
Triangles = Triangle, Triangles = Triangle,

View file

@ -61,6 +61,7 @@ class RendererGL final : public Renderer {
MAKE_LOG_FUNCTION(log, rendererLogger) MAKE_LOG_FUNCTION(log, rendererLogger)
void setupBlending(); void setupBlending();
void setupStencilTest(bool stencilEnable);
void bindDepthBuffer(); void bindDepthBuffer();
void setupTextureEnvState(); void setupTextureEnvState();
void bindTexturesToSlots(); void bindTexturesToSlots();
@ -79,4 +80,4 @@ class RendererGL final : public Renderer {
// Take a screenshot of the screen and store it in a file // Take a screenshot of the screen and store it in a file
void screenshot(const std::string& name) override; void screenshot(const std::string& name) override;
}; };

View file

@ -86,6 +86,7 @@ struct DepthBuffer {
Interval<u32> range; Interval<u32> range;
// OpenGL texture used for storing depth/stencil // OpenGL texture used for storing depth/stencil
OpenGL::Texture texture; OpenGL::Texture texture;
OpenGL::Framebuffer fbo;
DepthBuffer() : valid(false) {} DepthBuffer() : valid(false) {}
@ -127,6 +128,11 @@ struct DepthBuffer {
texture.setMagFilter(OpenGL::Nearest); texture.setMagFilter(OpenGL::Nearest);
glBindTexture(GL_TEXTURE_2D, prevTexture); glBindTexture(GL_TEXTURE_2D, prevTexture);
fbo.createWithDrawTexture(texture, fmt == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
Helpers::panic("Incomplete framebuffer");
} }
void free() { void free() {
@ -144,4 +150,4 @@ struct DepthBuffer {
size_t sizeInBytes() { size_t sizeInBytes() {
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format); return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
} }
}; };

View file

@ -2,7 +2,20 @@
void GLStateManager::resetBlend() { void GLStateManager::resetBlend() {
blendEnabled = false; blendEnabled = false;
logicOpEnabled = false;
logicOp = GL_COPY;
OpenGL::disableBlend(); OpenGL::disableBlend();
OpenGL::disableLogicOp();
OpenGL::setLogicOp(GL_COPY);
}
void GLStateManager::resetClipping() {
// Disable all (supported) clip planes
enabledClipPlanes = 0;
for (int i = 0; i < clipPlaneCount; i++) {
OpenGL::disableClipPlane(i);
}
} }
void GLStateManager::resetColourMask() { void GLStateManager::resetColourMask() {
@ -26,6 +39,14 @@ void GLStateManager::resetScissor() {
OpenGL::setScissor(0, 0, 0, 0); OpenGL::setScissor(0, 0, 0, 0);
} }
void GLStateManager::resetStencil() {
stencilEnabled = false;
stencilMask = 0xff;
OpenGL::disableStencil();
OpenGL::setStencilMask(0xff);
}
void GLStateManager::resetVAO() { void GLStateManager::resetVAO() {
boundVAO = 0; boundVAO = 0;
glBindVertexArray(0); glBindVertexArray(0);
@ -43,6 +64,7 @@ void GLStateManager::resetProgram() {
void GLStateManager::reset() { void GLStateManager::reset() {
resetBlend(); resetBlend();
resetClipping();
resetColourMask(); resetColourMask();
resetDepth(); resetDepth();
@ -50,4 +72,5 @@ void GLStateManager::reset() {
resetVBO(); resetVBO();
resetProgram(); resetProgram();
resetScissor(); resetScissor();
resetStencil();
} }

View file

@ -149,8 +149,6 @@ void RendererGL::initGraphicsContext(SDL_Window* window) {
// Set up the OpenGL blending context to match the emulated PICA // Set up the OpenGL blending context to match the emulated PICA
void RendererGL::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) // 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 = { 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, GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX, GL_FUNC_ADD, GL_FUNC_ADD, GL_FUNC_ADD,
@ -176,10 +174,23 @@ void RendererGL::setupBlending() {
GL_ONE, GL_ONE,
}; };
if (!blendingEnabled) { static constexpr std::array<GLenum, 16> logicOps = {
gl.disableBlend(); GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_SET, GL_COPY_INVERTED, GL_NOOP, GL_INVERT,
GL_NAND, GL_OR, GL_NOR, GL_XOR, GL_EQUIV, GL_AND_INVERTED, GL_OR_REVERSE, GL_OR_INVERTED,
};
// Shows if blending is enabled. If it is not enabled, then logic ops are enabled instead
const bool blendingEnabled = (regs[PICA::InternalRegs::ColourOperation] & (1 << 8)) != 0;
if (!blendingEnabled) { // Logic ops are enabled
const u32 logicOp = getBits<0, 4>(regs[PICA::InternalRegs::LogicOp]);
gl.setLogicOp(logicOps[logicOp]);
// If logic ops are enabled we don't need to disable blending because they override it
gl.enableLogicOp();
} else { } else {
gl.enableBlend(); gl.enableBlend();
gl.disableLogicOp();
// Get blending equations // Get blending equations
const u32 blendControl = regs[PICA::InternalRegs::BlendFunc]; const u32 blendControl = regs[PICA::InternalRegs::BlendFunc];
@ -205,6 +216,55 @@ void RendererGL::setupBlending() {
} }
} }
void RendererGL::setupStencilTest(bool stencilEnable) {
if (!stencilEnable) {
gl.disableStencil();
return;
}
static constexpr std::array<GLenum, 8> stencilFuncs = {
GL_NEVER,
GL_ALWAYS,
GL_EQUAL,
GL_NOTEQUAL,
GL_LESS,
GL_LEQUAL,
GL_GREATER,
GL_GEQUAL
};
gl.enableStencil();
const u32 stencilConfig = regs[PICA::InternalRegs::StencilTest];
const u32 stencilFunc = getBits<4, 3>(stencilConfig);
const s32 reference = s8(getBits<16, 8>(stencilConfig)); // Signed reference value
const u32 stencilRefMask = getBits<24, 8>(stencilConfig);
const bool stencilWrite = regs[PICA::InternalRegs::DepthBufferWrite];
const u32 stencilBufferMask = stencilWrite ? getBits<8, 8>(stencilConfig) : 0;
// TODO: Throw stencilFunc/stencilOp to the GL state manager
glStencilFunc(stencilFuncs[stencilFunc], reference, stencilRefMask);
gl.setStencilMask(stencilBufferMask);
static constexpr std::array<GLenum, 8> stencilOps = {
GL_KEEP,
GL_ZERO,
GL_REPLACE,
GL_INCR,
GL_DECR,
GL_INVERT,
GL_INCR_WRAP,
GL_DECR_WRAP
};
const u32 stencilOpConfig = regs[PICA::InternalRegs::StencilOp];
const u32 stencilFailOp = getBits<0, 3>(stencilOpConfig);
const u32 depthFailOp = getBits<4, 3>(stencilOpConfig);
const u32 passOp = getBits<8, 3>(stencilOpConfig);
glStencilOp(stencilOps[stencilFailOp], stencilOps[depthFailOp], stencilOps[passOp]);
}
void RendererGL::setupTextureEnvState() { void RendererGL::setupTextureEnvState() {
// TODO: Only update uniforms when the TEV config changed. Use an UBO potentially. // TODO: Only update uniforms when the TEV config changed. Use an UBO potentially.
@ -302,9 +362,9 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
gl.bindVAO(vao); gl.bindVAO(vao);
gl.useProgram(triangleProgram); gl.useProgram(triangleProgram);
OpenGL::enableClipPlane(0); // Clipping plane 0 is always enabled gl.enableClipPlane(0); // Clipping plane 0 is always enabled
if (regs[PICA::InternalRegs::ClipEnable] & 1) { if (regs[PICA::InternalRegs::ClipEnable] & 1) {
OpenGL::enableClipPlane(1); gl.enableClipPlane(1);
} }
setupBlending(); setupBlending();
@ -312,6 +372,7 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
poop.bind(OpenGL::DrawAndReadFramebuffer); poop.bind(OpenGL::DrawAndReadFramebuffer);
const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask]; const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask];
const bool depthWrite = regs[PICA::InternalRegs::DepthBufferWrite];
const bool depthEnable = depthControl & 1; const bool depthEnable = depthControl & 1;
const bool depthWriteEnable = getBit<12>(depthControl); const bool depthWriteEnable = getBit<12>(depthControl);
const int depthFunc = getBits<4, 3>(depthControl); const int depthFunc = getBits<4, 3>(depthControl);
@ -356,11 +417,14 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
GLsizei viewportHeight = GLsizei(f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0f); GLsizei viewportHeight = GLsizei(f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0f);
OpenGL::setViewport(viewportWidth, viewportHeight); OpenGL::setViewport(viewportWidth, viewportHeight);
const u32 stencilConfig = regs[PICA::InternalRegs::StencilTest];
const bool stencilEnable = getBit<0>(stencilConfig);
// Note: The code below must execute after we've bound the colour buffer & its framebuffer // Note: The code below must execute after we've bound the colour buffer & its framebuffer
// Because it attaches a depth texture to the aforementioned colour buffer // Because it attaches a depth texture to the aforementioned colour buffer
if (depthEnable) { if (depthEnable) {
gl.enableDepth(); gl.enableDepth();
gl.setDepthMask(depthWriteEnable ? GL_TRUE : GL_FALSE); gl.setDepthMask(depthWriteEnable && depthWrite ? GL_TRUE : GL_FALSE);
gl.setDepthFunc(depthModes[depthFunc]); gl.setDepthFunc(depthModes[depthFunc]);
bindDepthBuffer(); bindDepthBuffer();
} else { } else {
@ -371,16 +435,19 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
bindDepthBuffer(); bindDepthBuffer();
} else { } else {
gl.disableDepth(); gl.disableDepth();
if (stencilEnable) {
bindDepthBuffer();
}
} }
} }
setupStencilTest(stencilEnable);
vbo.bufferVertsSub(vertices); vbo.bufferVertsSub(vertices);
OpenGL::draw(primitiveTopology, GLsizei(vertices.size())); OpenGL::draw(primitiveTopology, GLsizei(vertices.size()));
} }
constexpr u32 topScreenBuffer = 0x1f000000;
constexpr u32 bottomScreenBuffer = 0x1f05dc00;
void RendererGL::display() { void RendererGL::display() {
gl.disableScissor(); gl.disableScissor();
@ -390,25 +457,50 @@ void RendererGL::display() {
} }
void RendererGL::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); log("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control);
gl.disableScissor();
const float r = float(getBits<24, 8>(value)) / 255.0f; const auto color = colourBufferCache.findFromAddress(startAddress);
const float g = float(getBits<16, 8>(value)) / 255.0f; if (color) {
const float b = float(getBits<8, 8>(value)) / 255.0f; const float r = getBits<24, 8>(value) / 255.0f;
const float a = float(value & 0xff) / 255.0f; const float g = getBits<16, 8>(value) / 255.0f;
const float b = getBits<8, 8>(value) / 255.0f;
if (startAddress == topScreenBuffer) { const float a = (value & 0xff) / 255.0f;
log("GPU: Cleared top screen\n"); color->get().fbo.bind(OpenGL::DrawFramebuffer);
} else if (startAddress == bottomScreenBuffer) { gl.setColourMask(true, true, true, true);
log("GPU: Tried to clear bottom screen\n"); OpenGL::setClearColor(r, g, b, a);
OpenGL::clearColor();
return; return;
} else {
log("GPU: Clearing some unknown buffer\n");
} }
OpenGL::setClearColor(r, g, b, a); const auto depth = depthBufferCache.findFromAddress(startAddress);
OpenGL::clearColor(); if (depth) {
depth->get().fbo.bind(OpenGL::DrawFramebuffer);
float depthVal;
const auto format = depth->get().format;
if (format == DepthFmt::Depth16) {
depthVal = (value & 0xffff) / 65535.0f;
} else {
depthVal = (value & 0xffffff) / 16777215.0f;
}
gl.setDepthMask(true);
OpenGL::setClearDepth(depthVal);
if (format == DepthFmt::Depth24Stencil8) {
const u8 stencil = (value >> 24);
gl.setStencilMask(0xff);
OpenGL::setClearStencil(stencil);
OpenGL::clearDepthAndStencil();
} else {
OpenGL::clearDepth();
}
return;
}
log("[RendererGL::ClearBuffer] No buffer found!\n");
} }
OpenGL::Framebuffer RendererGL::getColourFBO() { OpenGL::Framebuffer RendererGL::getColourFBO() {
@ -474,14 +566,16 @@ void RendererGL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u
screenFramebuffer.bind(OpenGL::DrawFramebuffer); screenFramebuffer.bind(OpenGL::DrawFramebuffer);
gl.disableBlend(); gl.disableBlend();
gl.disableLogicOp();
gl.disableDepth(); gl.disableDepth();
gl.disableScissor(); gl.disableScissor();
gl.disableStencil();
gl.setColourMask(true, true, true, true); gl.setColourMask(true, true, true, true);
gl.useProgram(displayProgram); gl.useProgram(displayProgram);
gl.bindVAO(dummyVAO); gl.bindVAO(dummyVAO);
OpenGL::disableClipPlane(0); gl.disableClipPlane(0);
OpenGL::disableClipPlane(1); gl.disableClipPlane(1);
// Hack: Detect whether we are writing to the top or bottom screen by checking output gap and drawing to the proper part of the output texture // Hack: Detect whether we are writing to the top or bottom screen by checking output gap and drawing to the proper part of the output texture
// We consider output gap == 320 to mean bottom, and anything else to mean top // We consider output gap == 320 to mean bottom, and anything else to mean top

View file

@ -299,6 +299,28 @@ void GPUService::processCommandBuffer() {
} }
} }
static u32 VaddrToPaddr(u32 addr) {
if (addr >= VirtualAddrs::VramStart && addr < (VirtualAddrs::VramStart + VirtualAddrs::VramSize)) [[likely]] {
return addr - VirtualAddrs::VramStart + PhysicalAddrs::VRAM;
}
else if (addr >= VirtualAddrs::LinearHeapStartOld && addr < VirtualAddrs::LinearHeapEndOld) {
return addr - VirtualAddrs::LinearHeapStartOld + PhysicalAddrs::FCRAM;
}
else if (addr >= VirtualAddrs::LinearHeapStartNew && addr < VirtualAddrs::LinearHeapEndNew) {
return addr - VirtualAddrs::LinearHeapStartNew + PhysicalAddrs::FCRAM;
}
else if (addr == 0) {
return 0;
}
Helpers::warn("[GSP::GPU VaddrToPaddr] Unknown virtual address %08X", addr);
// Obviously garbage address
return 0xF3310932;
}
// Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value // Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value
void GPUService::memoryFill(u32* cmd) { void GPUService::memoryFill(u32* cmd) {
u32 control = cmd[7]; u32 control = cmd[7];
@ -316,38 +338,16 @@ void GPUService::memoryFill(u32* cmd) {
u32 control1 = control >> 16; u32 control1 = control >> 16;
if (start0 != 0) { if (start0 != 0) {
gpu.clearBuffer(start0, end0, value0, control0); gpu.clearBuffer(VaddrToPaddr(start0), VaddrToPaddr(end0), value0, control0);
requestInterrupt(GPUInterrupt::PSC0); requestInterrupt(GPUInterrupt::PSC0);
} }
if (start1 != 0) { if (start1 != 0) {
gpu.clearBuffer(start1, end1, value1, control1); gpu.clearBuffer(VaddrToPaddr(start1), VaddrToPaddr(end1), value1, control1);
requestInterrupt(GPUInterrupt::PSC1); requestInterrupt(GPUInterrupt::PSC1);
} }
} }
static u32 VaddrToPaddr(u32 addr) {
if (addr >= VirtualAddrs::VramStart && addr < (VirtualAddrs::VramStart + VirtualAddrs::VramSize)) [[likely]] {
return addr - VirtualAddrs::VramStart + PhysicalAddrs::VRAM;
}
else if (addr >= VirtualAddrs::LinearHeapStartOld && addr < VirtualAddrs::LinearHeapEndOld) {
return addr - VirtualAddrs::LinearHeapStartOld + PhysicalAddrs::FCRAM;
}
else if (addr >= VirtualAddrs::LinearHeapStartNew && addr < VirtualAddrs::LinearHeapEndNew) {
return addr - VirtualAddrs::LinearHeapStartNew + PhysicalAddrs::FCRAM;
}
else if (addr == 0) {
return 0;
}
Helpers::warn("[GSP::GPU VaddrToPaddr] Unknown virtual address %08X", addr);
// Obviously garbage address
return 0xF3310932;
}
void GPUService::triggerDisplayTransfer(u32* cmd) { void GPUService::triggerDisplayTransfer(u32* cmd) {
const u32 inputAddr = VaddrToPaddr(cmd[1]); const u32 inputAddr = VaddrToPaddr(cmd[1]);
const u32 outputAddr = VaddrToPaddr(cmd[2]); const u32 outputAddr = VaddrToPaddr(cmd[2]);
@ -394,4 +394,4 @@ void GPUService::triggerTextureCopy(u32* cmd) {
// This uses the transfer engine and thus needs to fire a PPF interrupt. // This uses the transfer engine and thus needs to fire a PPF interrupt.
// NSMB2 relies on this // NSMB2 relies on this
requestInterrupt(GPUInterrupt::PPF); requestInterrupt(GPUInterrupt::PPF);
} }