Add clear colour to GL state manager

This commit is contained in:
wheremyfoodat 2023-08-12 17:02:00 +03:00
parent 4107a84c0d
commit 0f5c41cd69
4 changed files with 138 additions and 128 deletions

View file

@ -34,6 +34,8 @@ struct GLStateManager {
bool redMask, greenMask, blueMask, alphaMask; bool redMask, greenMask, blueMask, alphaMask;
bool depthMask; bool depthMask;
float clearRed, clearBlue, clearGreen, clearAlpha;
GLuint stencilMask; GLuint stencilMask;
GLuint boundVAO; GLuint boundVAO;
GLuint boundVBO; GLuint boundVBO;
@ -44,6 +46,7 @@ struct GLStateManager {
void reset(); void reset();
void resetBlend(); void resetBlend();
void resetClearing();
void resetClipping(); void resetClipping();
void resetColourMask(); void resetColourMask();
void resetDepth(); void resetDepth();
@ -209,8 +212,19 @@ struct GLStateManager {
} }
} }
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)); } 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_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!"); static_assert(std::is_trivially_destructible<GLStateManager>(), "OpenGL State Manager class is not trivially destructible!");

View file

@ -9,83 +9,63 @@ template <typename T>
using Interval = boost::icl::right_open_interval<T>; using Interval = boost::icl::right_open_interval<T>;
struct ColourBuffer { struct ColourBuffer {
u32 location; u32 location;
PICA::ColorFmt format; PICA::ColorFmt format;
OpenGL::uvec2 size; OpenGL::uvec2 size;
bool valid; bool valid;
// Range of VRAM taken up by buffer // Range of VRAM taken up by buffer
Interval<u32> range; Interval<u32> range;
// OpenGL resources allocated to buffer // OpenGL resources allocated to buffer
OpenGL::Texture texture; OpenGL::Texture texture;
OpenGL::Framebuffer fbo; OpenGL::Framebuffer fbo;
GLenum internalFormat; ColourBuffer() : valid(false) {}
GLenum fmt;
GLenum type;
ColourBuffer() : valid(false) {} ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true) : location(loc), format(format), size({x, y}), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes();
ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true) // Check if start and end are valid here
: location(loc), format(format), size({x, y}), valid(valid) { range = Interval<u32>(loc, (u32)endLoc);
}
u64 endLoc = (u64)loc + sizeInBytes();
// Check if start and end are valid here
range = Interval<u32>(loc, (u32)endLoc);
}
void allocate() { void allocate() {
// Internal formats for the texture based on format // Create texture for the FBO, setting up filters and the like
static constexpr std::array<GLenum, 5> internalFormats = { // Reading back the current texture is slow, but allocate calls should be few and far between.
GL_RGBA8, GL_RGB8, GL_RGB5_A1, GL_RGB565, GL_RGBA4 // If this becomes a bottleneck, we can fix it semi-easily
}; auto prevTexture = OpenGL::getTex2D();
texture.create(size.x(), size.y(), GL_RGBA8);
// Format of the texture texture.bind();
static constexpr std::array<GLenum, 5> formats = { texture.setMinFilter(OpenGL::Linear);
GL_RGBA, GL_BGR, GL_RGBA, GL_RGB, GL_RGBA, texture.setMagFilter(OpenGL::Linear);
}; glBindTexture(GL_TEXTURE_2D, prevTexture);
static constexpr std::array<GLenum, 5> types = {
GL_UNSIGNED_INT_8_8_8_8, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_5_5_1,
GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4,
};
internalFormat = internalFormats[(int)format];
fmt = formats[(int)format];
type = types[(int)format];
// Create texture for the FBO, setting up filters and the like
// Reading back the current texture is slow, but allocate calls should be few and far between.
// If this becomes a bottleneck, we can fix it semi-easily
auto prevTexture = OpenGL::getTex2D();
texture.create(size.x(), size.y(), internalFormat);
texture.bind();
texture.setMinFilter(OpenGL::Linear);
texture.setMagFilter(OpenGL::Linear);
glBindTexture(GL_TEXTURE_2D, prevTexture);
#ifdef GPU_DEBUG_INFO #ifdef GPU_DEBUG_INFO
const auto name = Helpers::format("Surface %dx%d %s from 0x%08X", size.x(), size.y(), PICA::textureFormatToString(format), location); const auto name = Helpers::format("Surface %dx%d %s from 0x%08X", size.x(), size.y(), PICA::textureFormatToString(format), location);
OpenGL::setObjectLabel(GL_TEXTURE, texture.handle(), name.c_str()); OpenGL::setObjectLabel(GL_TEXTURE, texture.handle(), name.c_str());
#endif #endif
fbo.createWithDrawTexture(texture); fbo.createWithDrawTexture(texture);
fbo.bind(OpenGL::DrawAndReadFramebuffer); fbo.bind(OpenGL::DrawAndReadFramebuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
Helpers::panic("Incomplete framebuffer"); Helpers::panic("Incomplete framebuffer");
}
// TODO: This should not clear the framebuffer contents. It should load them from VRAM. // TODO: This should not clear the framebuffer contents. It should load them from VRAM.
GLint oldViewport[4]; GLint oldViewport[4];
glGetIntegerv(GL_VIEWPORT, oldViewport); GLfloat oldClearColour[4];
OpenGL::setViewport(size.x(), size.y());
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
OpenGL::clearColor();
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
}
void free() { glGetIntegerv(GL_VIEWPORT, oldViewport);
glGetFloatv(GL_COLOR_CLEAR_VALUE, oldClearColour);
OpenGL::setViewport(size.x(), size.y());
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
OpenGL::clearColor();
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
OpenGL::setClearColor(oldClearColour[0], oldClearColour[1], oldClearColour[2], oldClearColour[3]);
}
void free() {
valid = false; valid = false;
if (texture.exists() || fbo.exists()) { if (texture.exists() || fbo.exists()) {
@ -103,88 +83,93 @@ struct ColourBuffer {
return Math::Rect<u32>{x0, size.y() - y0, x0 + width, size.y() - height - y0}; return Math::Rect<u32>{x0, size.y() - y0, x0 + width, size.y() - height - y0};
} }
bool matches(ColourBuffer& other) { bool matches(ColourBuffer& other) {
return location == other.location && format == other.format && return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
size.x() == other.size.x() && size.y() == other.size.y(); }
}
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);
} }
}; };
struct DepthBuffer { struct DepthBuffer {
u32 location; u32 location;
PICA::DepthFmt format; PICA::DepthFmt format;
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
bool valid; bool valid;
// Range of VRAM taken up by buffer // Range of VRAM taken up by buffer
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; OpenGL::Framebuffer fbo;
DepthBuffer() : valid(false) {} DepthBuffer() : valid(false) {}
DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) : DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) : location(loc), format(format), size({x, y}), valid(valid) {
location(loc), format(format), size({x, y}), valid(valid) { u64 endLoc = (u64)loc + sizeInBytes();
// Check if start and end are valid here
range = Interval<u32>(loc, (u32)endLoc);
}
u64 endLoc = (u64)loc + sizeInBytes(); void allocate() {
// Check if start and end are valid here // Create texture for the FBO, setting up filters and the like
range = Interval<u32>(loc, (u32)endLoc); // Reading back the current texture is slow, but allocate calls should be few and far between.
} // If this becomes a bottleneck, we can fix it semi-easily
auto prevTexture = OpenGL::getTex2D();
void allocate() { // Internal formats for the texture based on format
// Create texture for the FBO, setting up filters and the like static constexpr std::array<GLenum, 4> internalFormats = {
// Reading back the current texture is slow, but allocate calls should be few and far between. GL_DEPTH_COMPONENT16,
// If this becomes a bottleneck, we can fix it semi-easily GL_DEPTH_COMPONENT24,
auto prevTexture = OpenGL::getTex2D(); GL_DEPTH_COMPONENT24,
GL_DEPTH24_STENCIL8,
};
// Internal formats for the texture based on format // Format of the texture
static constexpr std::array<GLenum, 4> internalFormats = { static constexpr std::array<GLenum, 4> formats = {
GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT24, GL_DEPTH24_STENCIL8 GL_DEPTH_COMPONENT,
}; GL_DEPTH_COMPONENT,
GL_DEPTH_COMPONENT,
GL_DEPTH_STENCIL,
};
static constexpr std::array<GLenum, 4> types = {
GL_UNSIGNED_SHORT,
GL_UNSIGNED_INT,
GL_UNSIGNED_INT,
GL_UNSIGNED_INT_24_8,
};
// Format of the texture auto internalFormat = internalFormats[(int)format];
static constexpr std::array<GLenum, 4> formats = { auto fmt = formats[(int)format];
GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL auto type = types[(int)format];
};
static constexpr std::array<GLenum, 4> types = { texture.createDSTexture(size.x(), size.y(), internalFormat, fmt, nullptr, type, GL_TEXTURE_2D);
GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, GL_UNSIGNED_INT, GL_UNSIGNED_INT_24_8 texture.bind();
}; texture.setMinFilter(OpenGL::Nearest);
texture.setMagFilter(OpenGL::Nearest);
auto internalFormat = internalFormats[(int)format]; glBindTexture(GL_TEXTURE_2D, prevTexture);
auto fmt = formats[(int)format]; fbo.createWithDrawTexture(texture, fmt == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);
auto type = types[(int)format];
texture.createDSTexture(size.x(), size.y(), internalFormat, fmt, nullptr, type, GL_TEXTURE_2D); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
texture.bind();
texture.setMinFilter(OpenGL::Nearest);
texture.setMagFilter(OpenGL::Nearest);
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"); Helpers::panic("Incomplete framebuffer");
} }
}
void free() { void free() {
valid = false; valid = false;
if (texture.exists()) { if (texture.exists()) {
texture.free(); texture.free();
} }
} }
bool matches(DepthBuffer& other) { bool matches(DepthBuffer& other) {
return location == other.location && format == other.format && return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
size.x() == other.size.x() && size.y() == other.size.y(); }
}
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

@ -10,6 +10,15 @@ void GLStateManager::resetBlend() {
OpenGL::setLogicOp(GL_COPY); OpenGL::setLogicOp(GL_COPY);
} }
void GLStateManager::resetClearing() {
clearRed = 0.f;
clearBlue = 0.f;
clearGreen = 0.f;
clearAlpha = 1.f;
OpenGL::setClearColor(clearRed, clearBlue, clearGreen, clearAlpha);
}
void GLStateManager::resetClipping() { void GLStateManager::resetClipping() {
// Disable all (supported) clip planes // Disable all (supported) clip planes
enabledClipPlanes = 0; enabledClipPlanes = 0;
@ -64,6 +73,7 @@ void GLStateManager::resetProgram() {
void GLStateManager::reset() { void GLStateManager::reset() {
resetBlend(); resetBlend();
resetClearing();
resetClipping(); resetClipping();
resetColourMask(); resetColourMask();
resetDepth(); resetDepth();

View file

@ -141,7 +141,7 @@ void RendererGL::initGraphicsContext(SDL_Window* window) {
GLint oldViewport[4]; GLint oldViewport[4];
glGetIntegerv(GL_VIEWPORT, oldViewport); glGetIntegerv(GL_VIEWPORT, oldViewport);
OpenGL::setViewport(screenTextureWidth, screenTextureHeight); OpenGL::setViewport(screenTextureWidth, screenTextureHeight);
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); gl.setClearColour(0.0, 0.0, 0.0, 1.0);
OpenGL::clearColor(); OpenGL::clearColor();
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
@ -473,7 +473,7 @@ void RendererGL::display() {
auto bottomScreen = colourBufferCache.findFromAddress(bottomScreenAddr); auto bottomScreen = colourBufferCache.findFromAddress(bottomScreenAddr);
screenFramebuffer.bind(OpenGL::DrawFramebuffer); screenFramebuffer.bind(OpenGL::DrawFramebuffer);
OpenGL::setClearColor(0.f); gl.setClearColour(0.f, 0.f, 0.f, 1.f);
OpenGL::clearColor(); OpenGL::clearColor();
if (topScreen) { if (topScreen) {
@ -504,8 +504,9 @@ void RendererGL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 co
const float b = getBits<8, 8>(value) / 255.0f; const float b = getBits<8, 8>(value) / 255.0f;
const float a = (value & 0xff) / 255.0f; const float a = (value & 0xff) / 255.0f;
color->get().fbo.bind(OpenGL::DrawFramebuffer); color->get().fbo.bind(OpenGL::DrawFramebuffer);
gl.setColourMask(true, true, true, true); gl.setColourMask(true, true, true, true);
OpenGL::setClearColor(r, g, b, a); gl.setClearColour(r, g, b, a);
OpenGL::clearColor(); OpenGL::clearColor();
return; return;
} }