Panda3DS/include/renderer_gl/gl_state.hpp
2023-08-12 17:02:00 +03:00

230 lines
No EOL
6 KiB
C++

#pragma once
#include <type_traits>
#include "helpers.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)
// This object is meant to help us avoid duplicate OpenGL calls (such as binding the same program twice, enabling/disabling a setting twice, etc)
// by checking if we actually *need* a state change. This is meant to avoid expensive driver calls and minimize unneeded state changes
// A lot of code is in the header file instead of the relevant source file to make sure stuff gets inlined even without LTO, and
// because this header should ideally not be getting included in too many places
// Code that does not need inlining however, like the reset() function should be in gl_state.cpp
// This state manager may not handle every aspect of OpenGL, in which case anything not handled here should just be manipulated with raw
// OpenGL/opengl.hpp calls However, anything that can be handled through the state manager should, or at least there should be an attempt to keep it
// consistent with the current GL state to avoid bugs/suboptimal code.
// The state manager must *also* be a trivially constructible/destructible type, to ensure that no OpenGL functions get called sneakily without us
// knowing. This is important for when we want to eg add a Vulkan or misc backend. Would definitely not want to refactor all this. So we try to be as
// backend-agnostic as possible
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 logicOpEnabled;
bool depthEnabled;
bool scissorEnabled;
bool stencilEnabled;
u32 enabledClipPlanes; // Bitfield of enabled clip planes
// Colour/depth masks
bool redMask, greenMask, blueMask, alphaMask;
bool depthMask;
float clearRed, clearBlue, clearGreen, clearAlpha;
GLuint stencilMask;
GLuint boundVAO;
GLuint boundVBO;
GLuint currentProgram;
GLenum depthFunc;
GLenum logicOp;
void reset();
void resetBlend();
void resetClearing();
void resetClipping();
void resetColourMask();
void resetDepth();
void resetVAO();
void resetVBO();
void resetProgram();
void resetScissor();
void resetStencil();
void enableDepth() {
if (!depthEnabled) {
depthEnabled = true;
OpenGL::enableDepth();
}
}
void disableDepth() {
if (depthEnabled) {
depthEnabled = false;
OpenGL::disableDepth();
}
}
void enableBlend() {
if (!blendEnabled) {
blendEnabled = true;
OpenGL::enableBlend();
}
}
void disableBlend() {
if (blendEnabled) {
blendEnabled = false;
OpenGL::disableBlend();
}
}
void enableScissor() {
if (!scissorEnabled) {
scissorEnabled = true;
OpenGL::enableScissor();
}
}
void disableScissor() {
if (scissorEnabled) {
scissorEnabled = false;
OpenGL::disableScissor();
}
}
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) {
if (boundVAO != handle) {
boundVAO = handle;
glBindVertexArray(handle);
}
}
void bindVBO(GLuint handle) {
if (boundVBO != handle) {
boundVBO = handle;
glBindBuffer(GL_ARRAY_BUFFER, handle);
}
}
void useProgram(GLuint handle) {
if (currentProgram != handle) {
currentProgram = handle;
glUseProgram(handle);
}
}
void bindVAO(const OpenGL::VertexArray& vao) { bindVAO(vao.handle()); }
void bindVBO(const OpenGL::VertexBuffer& vbo) { bindVBO(vbo.handle()); }
void useProgram(const OpenGL::Program& program) { useProgram(program.handle()); }
void setColourMask(bool r, bool g, bool b, bool a) {
if (r != redMask || g != greenMask || b != blueMask || a != alphaMask) {
r = redMask;
g = greenMask;
b = blueMask;
a = alphaMask;
OpenGL::setColourMask(r, g, b, a);
}
}
void setDepthMask(bool mask) {
if (depthMask != mask) {
depthMask = mask;
OpenGL::setDepthMask(mask);
}
}
void setDepthFunc(GLenum func) {
if (depthFunc != func) {
depthFunc = func;
glDepthFunc(func);
}
}
void setClearColour(float r, float g, float b, float a) {
if (clearRed != r || clearGreen != g || clearBlue != b || clearAlpha != a) {
clearRed = r;
clearGreen = g;
clearBlue = b;
clearAlpha = a;
OpenGL::setClearColor(r, g, b, a);
}
}
void setDepthFunc(OpenGL::DepthFunc func) { setDepthFunc(static_cast<GLenum>(func)); }
};
static_assert(std::is_trivially_constructible<GLStateManager>(), "OpenGL State Manager class is not trivially constructible!");
static_assert(std::is_trivially_destructible<GLStateManager>(), "OpenGL State Manager class is not trivially destructible!");