Skip to content

Instantly share code, notes, and snippets.

@KageKirin
Last active June 29, 2021 12:47
Show Gist options
  • Save KageKirin/2412d1665ba7f5fbcc450d0d755093d5 to your computer and use it in GitHub Desktop.
Save KageKirin/2412d1665ba7f5fbcc450d0d755093d5 to your computer and use it in GitHub Desktop.
SDL Test 2 -- WebGL2 compatible fade black -> white
/// Common header to include for OpenGL
#ifndef GL_HEADER_H_INC
#define GL_HEADER_H_INC 1
#ifndef USE_GLEW
#define USE_GLEW 0
#endif
#ifndef USE_GLAD
#define USE_GLAD 0
#endif
#ifndef USE_GLAD_GLES
#define USE_GLAD_GLES 0
#endif
#if USE_GLEW
#include <GL/glew.h>
#elif USE_GLAD
#if USE_GLAD_GLES
#include <glad/gles2.h>
#define gladLoadGL(x) gladLoadGLES2(x)
#else
#include <glad/gl.h>
#endif // USE_GLAD_GLES
#else
#if USE_GLES3
#include <GLES3/gl3.h>
#include <GLES3/gl31.h>
#include <GLES3/gl32.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <gl/gl.h>
#endif // USE_GLES3
#endif // USE_GLEW || USE_GLAD
#endif // GL_HEADER_H_INC
#include <SDL2/SDL.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "glheader.h"
//-----------------------------------------------------------------------------
/// config defines
#define _DEBUG_PRINT
#define _SAFE_OPENGL
//-----------------------------------------------------------------------------
/// macros
#define ASSERT(x) assert(x)
#if USE_GLEW
#define PUSH_DEBUG_GROUP(id, msg) \
{ \
if (glPushDebugGroup) \
{ \
SAFEGL(glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, sizeof(msg), msg)); \
} \
}
#define POP_DEBUG_GROUP() \
{ \
if (glPopDebugGroup) \
{ \
SAFEGL(glPopDebugGroup()); \
} \
}
#elif USE_GLAD
#define PUSH_DEBUG_GROUP(id, msg) \
{ \
if (GLAD_GL_KHR_debug) \
{ \
SAFEGL(glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, sizeof(msg), msg)); \
} \
}
#define POP_DEBUG_GROUP() \
{ \
if (GLAD_GL_KHR_debug) \
{ \
SAFEGL(glPopDebugGroup()); \
} \
}
#elif __EMSCRIPTEN__
#define PUSH_DEBUG_GROUP(id, msg)
#define POP_DEBUG_GROUP()
#else
#define PUSH_DEBUG_GROUP(id, msg) \
{ \
SAFEGL(glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, sizeof(msg), msg)); \
}
#define POP_DEBUG_GROUP() \
{ \
SAFEGL(glPopDebugGroup()); \
}
#endif // USE_GLEW || USE_GLAD
void safegl_print(int err, char* call, char* file, int line);
#ifdef _SAFE_OPENGL
#define SAFEGL(call) \
(call); \
{ \
GLenum err = glGetError(); \
if (err != GL_NO_ERROR) \
{ \
safegl_print(err, #call, __FILE__, __LINE__); \
} \
}
#else
#define SAFEGL(call) (call);
#endif
#ifdef _DEBUG_PRINT
#define DEBUG_PRINT(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
//-----------------------------------------------------------------------------
/// typedefs
typedef uint8_t u8;
typedef int8_t i8;
typedef uint16_t u16;
typedef int16_t i16;
typedef int32_t u32;
typedef uint32_t i32;
typedef GLboolean bvec2_a[2];
typedef GLboolean bvec3_a[3];
typedef GLboolean bvec4_a[4];
typedef GLint ivec2_a[2];
typedef GLint ivec3_a[3];
typedef GLint ivec4_a[4];
typedef GLfloat fvec2_a[2];
typedef GLfloat fvec3_a[3];
typedef GLfloat fvec4_a[4];
typedef fvec4_a fmat4_a[4];
typedef struct bvec2_t
{
GLboolean x, y;
} bvec2_t;
typedef struct bvec3_t
{
GLboolean x, y, z;
} bvec3_t;
typedef struct bvec4_t
{
GLboolean x, y, z, w;
} bvec4_t;
typedef struct ivec2_t
{
GLint x, y;
} ivec2_t;
typedef struct ivec3_t
{
GLint x, y, z;
} ivec3_t;
typedef struct ivec4_t
{
GLint x, y, z, w;
} ivec4_t;
typedef struct fvec2_t
{
GLfloat x, y;
} fvec2_t;
typedef struct fvec3_t
{
GLfloat x, y, z;
} fvec3_t;
typedef struct fvec4_t
{
GLfloat x, y, z, w;
} fvec4_t;
typedef struct fmat4_t
{
fvec4_t r0, r1, r2, r3;
} fmat4_t;
//-----------------------------------------------------------------------------
/// globals
SDL_Window* s_window = NULL;
SDL_GLContext s_glcontext = 0;
//-----------------------------------------------------------------------------
/// forward declarations
int initGraphics();
void exitGraphics();
int initRenderer(u32 width, u32 height);
void exitRenderer();
void runMainLoop();
int mainLoopIteration();
void onFrameRendered();
bool isRenderTargetComplete(GLuint fbo);
GLuint getCurrentRenderTarget();
void initDefaultRenderTarget();
void restoreDefaultRenderTarget();
void pushRenderTarget(GLuint fbo);
void popRenderTarget();
//-----------------------------------------------------------------------------
/// main
int main(int argc, char* argv[])
{
initGraphics();
runMainLoop();
exitGraphics();
return 0;
}
void runMainLoop()
{
int status = 0;
do
{
status = mainLoopIteration();
} while (status > 0);
}
int mainLoopIteration()
{
static int internalState = 0;
if (internalState == 0)
{
int err = initRenderer(640, 480);
internalState++;
if (err) return 0;
}
static u8 rgb[3];
static u8 rgbIdx = 0;
SAFEGL(glClearColor(
((float)rgb[0]) / 255.0f,
((float)rgb[1]) / 255.0f,
((float)rgb[2]) / 255.0f,
1.0f
))
rgb[rgbIdx]++;
rgbIdx = (rgbIdx + 1) % 3;
SAFEGL(glClear(GL_COLOR_BUFFER_BIT));
onFrameRendered();
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
return 0;
break;
case SDL_KEYDOWN:
return 0;
break;
}
}
return internalState;
}
//-----------------------------------------------------------------------------
/// implementations
int initGraphics()
{
int initOk = SDL_Init(SDL_INIT_VIDEO);
if (initOk < 0)
{
DEBUG_PRINT("SDL2 SDL_Init failed: %s", SDL_GetError());
}
ASSERT(initOk >= 0);
SDL_version sdlVersion;
SDL_GetVersion(&sdlVersion);
DEBUG_PRINT("using SDL version %i.%i.%i, rev. %s [%i]", sdlVersion.major, sdlVersion.minor, sdlVersion.patch,
SDL_GetRevision(), SDL_GetRevisionNumber());
int loadOk = SDL_GL_LoadLibrary(NULL); // Default OpenGL is fine.
if (loadOk < 0)
{
DEBUG_PRINT("SDL2 SDL_GL_LoadLibrary failed: %s", SDL_GetError());
}
DEBUG_PRINT("SDL2 video driver: %s", SDL_GetCurrentVideoDriver());
ASSERT(loadOk >= 0);
// TODO: SDL_GL_SetAttribute if needed
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
#if USE_GLES3 || __EMSCRIPTEN__
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles");
SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_EGL, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif // USE_GLES3 || __EMSCRIPTEN__
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
#if __EMSCRIPTEN__
SDL_GL_SetSwapInterval(0);
#else
SDL_GL_SetSwapInterval(1); // Use VSYNC
#endif // __EMSCRIPTEN__
return 0;
}
void exitGraphics()
{
SDL_GL_DeleteContext(s_glcontext);
SDL_DestroyWindow(s_window);
SDL_Quit();
}
int initRenderer(u32 width, u32 height)
{
// create window
if (!s_window)
{
s_window =
SDL_CreateWindow("SDL2 TEST 2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS);
}
if (!s_window)
{
DEBUG_PRINT("SDL2 SDL_CreateWindow failed: %s", SDL_GetError());
}
ASSERT(s_window);
// create GL context
if (!s_glcontext)
{
s_glcontext = SDL_GL_CreateContext(s_window);
SDL_GL_MakeCurrent(s_window, s_glcontext);
}
if (s_glcontext)
{
DEBUG_PRINT("SDL2 SDL_GL_CreateContext succeeded");
}
else
{
DEBUG_PRINT("SDL2 SDL_GL_CreateContext failed: %s", SDL_GetError());
}
ASSERT(s_glcontext);
SDL_GL_MakeCurrent(s_window, s_glcontext);
#if USE_GLEW
DEBUG_PRINT("initializing GLEW");
glewExperimental = GL_TRUE;
GLenum glewErr = glewInit();
if (glewErr != GLEW_OK)
{
DEBUG_PRINT("GLEW glewInit failed: %s", glewGetErrorString(glewErr));
}
ASSERT(glewErr == GLEW_OK);
DEBUG_PRINT("initializing GLEW done");
#elif USE_GLAD
gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress);
#endif // USE_GLEW || USE_GLAD
DEBUG_PRINT("GL version: %s", glGetString(GL_VERSION));
DEBUG_PRINT("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
DEBUG_PRINT("GL vendor: %s", glGetString(GL_VENDOR));
DEBUG_PRINT("GL renderer: %s", glGetString(GL_RENDERER));
PUSH_DEBUG_GROUP(__LINE__, __FUNCTION__);
initDefaultRenderTarget();
restoreDefaultRenderTarget();
POP_DEBUG_GROUP();
return 0;
}
void exitRenderer()
{
PUSH_DEBUG_GROUP(__LINE__, __FUNCTION__);
POP_DEBUG_GROUP();
}
void onFrameRendered()
{
PUSH_DEBUG_GROUP(__LINE__, __FUNCTION__);
SDL_GL_SwapWindow(s_window);
restoreDefaultRenderTarget();
ASSERT(isRenderTargetComplete(getCurrentRenderTarget()));
POP_DEBUG_GROUP();
}
//-----------------------------------------------------------------------------
/// render target
bool isRenderTargetComplete(GLuint fbo)
{
pushRenderTarget(fbo);
int status = SAFEGL(glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (status != GL_FRAMEBUFFER_COMPLETE)
{
DEBUG_PRINT("unexpected fbo status: 0x%x", status);
}
popRenderTarget();
return status == GL_FRAMEBUFFER_COMPLETE;
}
GLuint getCurrentRenderTarget()
{
GLuint fbo = 0;
SAFEGL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&fbo));
return fbo;
}
enum
{
FBO_STACK_SIZE = 256
};
static GLuint fboStack[FBO_STACK_SIZE];
u16 curStack = 0; // we wouldn't want it to overflow and loop in [0,255] right?
static GLuint defaultFbo = 0;
void initDefaultRenderTarget()
{
PUSH_DEBUG_GROUP(__LINE__, __FUNCTION__);
SAFEGL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&defaultFbo));
DEBUG_PRINT("Default FBO bound to: %i", defaultFbo);
fboStack[0] = defaultFbo;
POP_DEBUG_GROUP();
}
void restoreDefaultRenderTarget()
{
if (getCurrentRenderTarget() != defaultFbo)
{
SAFEGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo));
}
}
void pushRenderTarget(GLuint fbo)
{
ASSERT(curStack < FBO_STACK_SIZE);
curStack++;
fboStack[curStack] = fbo;
if (getCurrentRenderTarget() != fbo)
{
SAFEGL(glBindFramebuffer(GL_FRAMEBUFFER, fbo));
}
}
void popRenderTarget()
{
curStack--;
if (getCurrentRenderTarget() != fboStack[curStack])
{
SAFEGL(glBindFramebuffer(GL_FRAMEBUFFER, fboStack[curStack]));
}
}
//-----------------------------------------------------------------------------
/// error handling
static const char* getErrorDesc(int err)
{
switch (err)
{
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
case GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
#ifdef GL_CONTEXT_LOST
case GL_CONTEXT_LOST:
return "GL_CONTEXT_LOST";
#endif // GL_CONTEXT_LOST
#ifdef GL_CONTEXT_LOST_KHR
case GL_CONTEXT_LOST_KHR:
return "GL_CONTEXT_LOST_KHR";
#endif // GL_CONTEXT_LOST_KHR
}
return "unknown error";
}
void safegl_print(int err, char* call, char* file, int line)
{
fprintf(stdout, "GL ERROR: 0x%x (%s) at '%s' %s:%d\n", err, getErrorDesc(err), call, file, line);
}
//-----------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment