Last active
June 29, 2021 12:47
-
-
Save KageKirin/2412d1665ba7f5fbcc450d0d755093d5 to your computer and use it in GitHub Desktop.
SDL Test 2 -- WebGL2 compatible fade black -> white
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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