Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 157 additions & 146 deletions src/gl/gl_renderer.c

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/gl/gl_renderer.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef _BS_GL_RENDERER_H_
#define _BS_GL_RENDERER_H_

#include "gl_common.h"

#include "common.h"
#include "renderer.h"
#include "runner.h"
Expand Down Expand Up @@ -28,6 +30,12 @@ typedef struct {
bool compiled;
uint32_t uniformCount;
GLShaderUniform* uniforms;

GLShaderUniform* gmBaseTexture;
GLShaderUniform* gmMatrices;
GLShaderUniform* gmFogColour;
GLShaderUniform* gmAlphaTestEnabled;
GLShaderUniform* gmAlphaRefValue;
} GMLShader;

typedef struct {
Expand Down Expand Up @@ -98,6 +106,10 @@ typedef struct {
GLShaderUniform* uAlphaTestRef;
GLShaderUniform* uAlphaTestEnabled;
GLShaderUniform* uTexture;

GLuint currentProgram;

GLState state;
} GLRenderer;

bool GLRenderer_ensureTextureLoaded(GLRenderer* gl, uint32_t pageId);
Expand Down
124 changes: 98 additions & 26 deletions src/gl_common/gl_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,83 @@
#include "utils.h"
#include "renderer.h" // for bm_* constants

// ===[ State Tracking Wrappers ]===

void glBindFramebufferCached(GLState* state, GLenum target, GLuint fbo) {
switch (target) {
case GL_FRAMEBUFFER:
state->currentReadFbo = fbo;
state->currentDrawFbo = fbo;
break;
case GL_READ_FRAMEBUFFER:
if (state->currentReadFbo == fbo) return;
state->currentReadFbo = fbo;
break;
case GL_DRAW_FRAMEBUFFER:
if (state->currentDrawFbo == fbo) return;
state->currentDrawFbo = fbo;
break;
}
state->currentFbo = fbo;
glBindFramebuffer(target, fbo);
}

void glViewportCached(GLState* state, int32_t x, int32_t y, int32_t w, int32_t h) {
if (state->viewport[0] == x && state->viewport[1] == y &&
state->viewport[2] == w && state->viewport[3] == h) {
return;
}
state->viewport[0] = x; state->viewport[1] = y;
state->viewport[2] = w; state->viewport[3] = h;
glViewport(x, y, w, h);
}

void glScissorCached(GLState* state, int32_t x, int32_t y, int32_t w, int32_t h) {
if (state->scissor[0] == x && state->scissor[1] == y &&
state->scissor[2] == w && state->scissor[3] == h) {
return;
}
state->scissor[0] = x; state->scissor[1] = y;
state->scissor[2] = w; state->scissor[3] = h;
glScissor(x, y, w, h);
}

void glSetCap(GLState* state, GLenum cap, bool enable) {
switch (cap) {
case GL_SCISSOR_TEST:
if (state->scissorEnabled == enable) return;
state->scissorEnabled = enable;
break;
case GL_BLEND:
if (state->blendEnabled == enable) return;
state->blendEnabled = enable;
break;
default:
break;
}
enable ? glEnable(cap) : glDisable(cap);
}

void glClearColorCached(GLState* state, float r, float g, float b, float a) {
if (state->clearColor[0] == r && state->clearColor[1] == g &&
state->clearColor[2] == b && state->clearColor[3] == a) return;
state->clearColor[0] = r; state->clearColor[1] = g;
state->clearColor[2] = b; state->clearColor[3] = a;
glClearColor(r, g, b, a);
}

void glActiveTextureCached(GLState* state, int32_t unit) {
if (state->activeTexUnit == unit) return;
state->activeTexUnit = unit;
glActiveTexture(unit);
}

void glPixelStoreiCached(GLState* state, GLenum pname, GLint param) {
if (state->currentPixelStorei == param) return;
state->currentPixelStorei = param;
glPixelStorei(pname, param);
}

// ===[ Letterbox blit ]===

void GLCommon_computeLetterbox(int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, int32_t* outStartX, int32_t* outStartY, int32_t* outEndX, int32_t* outEndY) {
Expand All @@ -27,17 +104,17 @@ void GLCommon_computeLetterbox(int32_t gameW, int32_t gameH, int32_t windowW, in
*outEndY = startY + effH;
}

void GLCommon_beginLetterboxBlit(GLuint fbo, GLuint hostFbo) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hostFbo);
void GLCommon_beginLetterboxBlit(GLState* state, GLuint fbo, GLuint hostFbo) {
glBindFramebufferCached(state, GL_READ_FRAMEBUFFER, fbo);
glBindFramebufferCached(state, GL_DRAW_FRAMEBUFFER, hostFbo);
}

void GLCommon_endLetterboxBlit(int32_t fboWidth, int32_t fboHeight, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, GLuint hostFbo) {
void GLCommon_endLetterboxBlit(GLState* state, int32_t fboWidth, int32_t fboHeight, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, GLuint hostFbo) {
int32_t sx, sy, ex, ey;
glClearColor(0.0, 0.0, 0.0, 1.0); //please remove if it breaks something like borders, it was just my quick-fix for the color to not be randomly changed
glClearColorCached(state, 0.0f, 0.0f, 0.0f, 1.0f); //please remove if it breaks something like borders, it was just my quick-fix for the color to not be randomly changed
GLCommon_computeLetterbox(gameW, gameH, windowW, windowH, &sx, &sy, &ex, &ey);
glBlitFramebuffer(0, 0, fboWidth, fboHeight, sx, ey, ex, sy, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, hostFbo);
glBindFramebufferCached(state, GL_FRAMEBUFFER, hostFbo);
}

// ===[ Surface arrays ]===
Expand Down Expand Up @@ -71,7 +148,7 @@ static bool resolveSurfaceFBO(GLuint* surfaces, int32_t* surfaceWidth, int32_t*
return true;
}

void GLCommon_surfaceBlit(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t dstId, int32_t dstX, int32_t dstY, int32_t srcId, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, bool part) {
void GLCommon_surfaceBlit(GLState* state, GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t dstId, int32_t dstX, int32_t dstY, int32_t srcId, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, bool part) {
GLuint srcFbo, dstFbo;
int32_t srcFboW, srcFboH;
MAYBE_UNUSED int32_t dstFboW, dstFboH;
Expand All @@ -82,16 +159,13 @@ void GLCommon_surfaceBlit(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surf
if (!resolveSurfaceFBO(surfaces, surfaceWidth, surfaceHeight, count, dstId, &dstFbo, &dstFboW, &dstFboH))
return;

int originalFramebufferBinding;

// Yes, in OpenGL you need to use _BINDING to query things
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &originalFramebufferBinding);
int originalFramebufferBinding = state->currentDrawFbo;
bool scissorWasEnabled = state->scissorEnabled;

GLboolean scissorWasEnabled = glIsEnabled(GL_SCISSOR_TEST);
if (scissorWasEnabled) glDisable(GL_SCISSOR_TEST);
glSetCap(state, GL_SCISSOR_TEST, false);

glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo);
glBindFramebufferCached(state, GL_READ_FRAMEBUFFER, srcFbo);
glBindFramebufferCached(state, GL_DRAW_FRAMEBUFFER, dstFbo);

if (part) {
// GL Y is bottom-up; convert GML's top-down (srcX, srcY) source rect.
Expand All @@ -101,11 +175,11 @@ void GLCommon_surfaceBlit(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surf
glBlitFramebuffer(0, 0, srcFboW, srcFboH, dstX, dstY, dstX + srcFboW, dstY + srcFboH, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, originalFramebufferBinding);
if (scissorWasEnabled) glEnable(GL_SCISSOR_TEST);
glBindFramebufferCached(state, GL_DRAW_FRAMEBUFFER, originalFramebufferBinding);
glSetCap(state, GL_SCISSOR_TEST, scissorWasEnabled);
}

bool GLCommon_surfaceGetPixels(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t surfaceId, uint8_t* outRGBA) {
bool GLCommon_surfaceGetPixels(GLState* state, GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t surfaceId, uint8_t* outRGBA) {
if (0 > surfaceId || (uint32_t) surfaceId >= count)
return false;

Expand All @@ -117,13 +191,11 @@ bool GLCommon_surfaceGetPixels(GLuint* surfaces, int32_t* surfaceWidth, int32_t*
if (0 >= w || 0 >= h)
return false;

GLint prevFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
GLint prevPackAlign = 4;
glGetIntegerv(GL_PACK_ALIGNMENT, &prevPackAlign);
GLint prevFbo = state->currentFbo;
GLint prevPackAlign = state->currentPixelStorei;

glBindFramebuffer(GL_FRAMEBUFFER, surfaces[surfaceId]);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindFramebufferCached(state, GL_FRAMEBUFFER, surfaces[surfaceId]);
glPixelStoreiCached(state, GL_PACK_ALIGNMENT, 1);

uint8_t* tmp = (uint8_t *)safeMalloc((size_t) w * (size_t) h * 4);
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
Expand All @@ -135,8 +207,8 @@ bool GLCommon_surfaceGetPixels(GLuint* surfaces, int32_t* surfaceWidth, int32_t*
}
free(tmp);

glPixelStorei(GL_PACK_ALIGNMENT, prevPackAlign);
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) prevFbo);
glPixelStoreiCached(state, GL_PACK_ALIGNMENT, prevPackAlign);
glBindFramebufferCached(state, GL_FRAMEBUFFER, (GLuint) prevFbo);
return true;
}

Expand Down
48 changes: 44 additions & 4 deletions src/gl_common/gl_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define _BS_GL_COMMON_H_

#include "common.h"
#include "matrix_math.h"
#include <stdint.h>

#if defined(__EMSCRIPTEN__) || defined(__ANDROID__)
Expand All @@ -13,15 +14,54 @@
#include <glad/glad.h>
#endif

// ===[ GL State ]===

typedef struct {
Matrix4f wvp;
float fogColor[4]; // RGB + A (A=0 disabled, A=1 enabled)
float alphaTestRef;
bool alphaTestEnabled;
} DefaultShaderUniforms;

typedef struct {
// Cached GL state
GLuint currentFbo;
GLuint currentReadFbo;
GLuint currentDrawFbo;
GLint currentPixelStorei;

int32_t viewport[4];
int32_t scissor[4];
float clearColor[4];
int32_t activeTexUnit;
bool blendEnabled;
bool scissorEnabled;
bool depthTestEnabled;
bool texture2DEnabled;

// Default shader uniform tracking (modern-gl only)
bool uniformsDirty;
DefaultShaderUniforms last;
} GLState;

// ===[ GL state tracking wrappers ]===
void glBindFramebufferCached(GLState* state, GLenum target, GLuint fbo);
void glViewportCached(GLState* state, int32_t x, int32_t y, int32_t w, int32_t h);
void glScissorCached(GLState* state, int32_t x, int32_t y, int32_t w, int32_t h);
void glSetCap(GLState* state, GLenum cap, bool enabled);
void glClearColorCached(GLState* state, float r, float g, float b, float a);
void glActiveTextureCached(GLState* state, int32_t unit);
void glPixelStoreiCached(GLState* state, GLenum pname, int32_t param);

// ===[ Letterbox blit ]===

// Computes the letterboxed destination rect for a gameW x gameH frame inside a windowW x windowH window.
// All four outputs are pixel coordinates with (0,0) at the bottom-left of the window (OpenGL convention).
void GLCommon_computeLetterbox(int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, int32_t* outStartX, int32_t* outStartY, int32_t* outEndX, int32_t* outEndY);

// Blits the given FBO (typically the application_surface) into hostFbo with letterboxing (hostFbo 0 == the window).
void GLCommon_beginLetterboxBlit(GLuint fbo, GLuint hostFbo);
void GLCommon_endLetterboxBlit(int32_t fboWidth, int32_t fboHeight, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, GLuint hostFbo);
void GLCommon_beginLetterboxBlit(GLState* state, GLuint fbo, GLuint hostFbo);
void GLCommon_endLetterboxBlit(GLState* state, int32_t fboWidth, int32_t fboHeight, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, GLuint hostFbo);

// ===[ Surface arrays ]===

Expand All @@ -37,12 +77,12 @@ uint32_t GLCommon_findOrAllocateSurfaceSlot(GLuint** surfaces, GLuint** surfaceT
// If part == false, ignores src{X,Y,W,H} and copies the whole source to a matching-size box at (dstX, dstY) on the destination.
// If part == true, copies a src{W,H}-sized region starting at (srcX, srcY) on the source (top-down GML coords) to (dstX, dstY) on the destination.
// Silently returns if either id is invalid.
void GLCommon_surfaceBlit(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t dstId, int32_t dstX, int32_t dstY, int32_t srcId, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, bool part);
void GLCommon_surfaceBlit(GLState* state, GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t dstId, int32_t dstX, int32_t dstY, int32_t srcId, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, bool part);

// Reads a surface's pixels into a top-down RGBA8 buffer of size width*height*4.
// Returns false on invalid surfaceId.
// Saves/restores GL_FRAMEBUFFER_BINDING and GL_PACK_ALIGNMENT around the read.
bool GLCommon_surfaceGetPixels(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t surfaceId, uint8_t* outRGBA);
bool GLCommon_surfaceGetPixels(GLState* state, GLuint* surfaces, int32_t* surfaceWidth, int32_t* surfaceHeight, uint32_t count, int32_t surfaceId, uint8_t* outRGBA);

// ===[ Blend mode translation ]===

Expand Down
4 changes: 3 additions & 1 deletion src/gl_common/gl_wrappers.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#if !defined(_BS_GL_WRAPPERS_H_) && !defined(__EMSCRIPTEN__) && !defined(PLATFORM_PS3) && !defined(__ANDROID__)
#define _BS_GL_WRAPPERS_H_

#include "gl_renderer.h"

static inline void gl_init_wrappers(void) {
if (!glBindVertexArray)
glBindVertexArray = glBindVertexArrayOES;
Expand Down Expand Up @@ -36,4 +38,4 @@ static inline void gl_init_wrappers(void) {
glBlendFuncSeparate = glBlendFuncSeparateEXT;
}

#endif
#endif
Loading
Loading