Skip to content

Instantly share code, notes, and snippets.

@chrisdill
Created January 23, 2020 15:37
Show Gist options
  • Save chrisdill/291c938605c200d079a88d0a7855f31a to your computer and use it in GitHub Desktop.
Save chrisdill/291c938605c200d079a88d0a7855f31a to your computer and use it in GitHub Desktop.
A fun little test of a SDL2 platform layer for hot code reloading with rayfork
#!/bin/bash
INCLUDES="-I../../dependencies/cgltf -I../../dependencies/dr_libs -I../../dependencies/jar -I../../dependencies/miniaudio -I../../dependencies/par -I../../dependencies/stb -I../../dependencies/tinyobjloader-c -I../../examples/dependencies/ -I../../examples/dependencies/sokol -I../../rayfork"
if [ "$(uname)" = "Darwin" ]
then
FLAGS="-g -D_DEFAULT_SOURCE -ObjC -fobjc-arc"
LIBRARIES="-framework Cocoa"
else
FLAGS="-g -D_DEFAULT_SOURCE"
LIBRARIES="-lGL -lm -lpthread -ldl -lrt -lX11"
fi
mkdir -p bin
pushd bin
mkdir -p hot_code_reloading
pushd hot_code_reloading
echo -e '\n'
echo Building hot_code_reloading example
#gcc -o hot_code_reloading ../../examples/hot_code_reloading/sdl_main.c ../../examples/hot_code_reloading/game.c ../../examples/dependencies/glad/glad.c $FLAGS $INCLUDES $LIBRARIES -I/usr/include/SDL2/ -L/usr/lib/ -lSDL2;
gcc -c -fpic -o hot_code_reloading_dynamic.o ../../examples/hot_code_reloading/game.c -I../../examples/hot_code_reloading/ $FLAGS $INCLUDES $LIBRARIES #/EXPORT:game_init /EXPORT:game_update /EXPORT:game_refresh
#gcc -c -fpic -o glad.o ../../examples/dependencies/glad/glad.c -I../../examples/hot_code_reloading/ $FLAGS $INCLUDES $LIBRARIES #/EXPORT:game_init /EXPORT:game_update /EXPORT:game_refresh
gcc -shared hot_code_reloading_dynamic.o glad.o -o hot_code_reloading_dynamic.so
popd
popd
// In this file we only initialise the window using sdl
#include <SDL2/SDL.h>
#include <assert.h>
void on_init(void);
void on_frame(void);
#include "game.h"
//Functions we call from the dll
typedef void (*game_init_func_t)(game_data* game_data);
typedef void (*game_refresh_func_t)(game_data* game_data);
typedef void (*game_update_func_t)(void);
//Stubs for when we the dll functions are not loaded
void game_init_stub(game_data* it) {}
void game_refresh_stub(game_data* it) {}
void game_update_stub(void) {}
typedef struct sdl_game_code sdl_game_code;
struct sdl_game_code
{
void* game_code_dll;
game_init_func_t game_init;
game_refresh_func_t game_refresh;
game_update_func_t game_update;
bool is_valid;
};
sdl_game_code game_code;
game_data game;
SDL_bool CopyFile(const char* source_dll_name, const char* temp_dll_name)
{
SDL_RWops *source = SDL_RWFromFile(source_dll_name, "r");
SDL_RWops *target = SDL_RWFromFile(temp_dll_name, "w");
// Read source into buffer
Sint64 size = SDL_RWsize(source);
void * buffer = SDL_calloc(1, size);
SDL_RWread(source, buffer, size, 1);
// Write buffer to target
SDL_RWwrite(target, buffer, size, 1);
printf("File copied successfully.\n");
SDL_RWclose(source);
SDL_RWclose(target);
SDL_free(buffer);
return SDL_TRUE;
}
sdl_game_code sdl_load_game_code(const char* source_dll_name, const char* temp_dll_name)
{
sdl_game_code result = {0};
assert(CopyFile(source_dll_name, temp_dll_name));
result.game_code_dll = SDL_LoadObject(source_dll_name);
if (result.game_code_dll == NULL)
{
printf("SDL_LoadObject failed: %s\n", SDL_GetError());
}
assert(result.game_code_dll);
result.game_init = (game_init_func_t) SDL_LoadFunction(result.game_code_dll, "game_init");
result.game_update = (game_update_func_t) SDL_LoadFunction(result.game_code_dll, "game_update");
result.game_refresh = (game_refresh_func_t) SDL_LoadFunction(result.game_code_dll, "game_refresh");
assert(result.game_init && result.game_update && result.game_refresh);
result.is_valid = true;
return result;
}
void sdl_unload_game_code(sdl_game_code* game_code)
{
if (game_code->game_code_dll)
{
SDL_UnloadObject(game_code->game_code_dll);
game_code->game_code_dll = NULL;
}
game_code->is_valid = false;
game_code->game_init = game_init_stub;
game_code->game_update = game_update_stub;
game_code->game_refresh = game_refresh_stub;
}
void on_init(void)
{
game_code = sdl_load_game_code("./hot_code_reloading_dynamic.so", "./hot_code_reloading_temp.so");
assert(game_code.is_valid);
game.alloc = malloc;
game.free = free;
game.screen_width = 800;
game.screen_height = 450;
game_code.game_init(&game);
}
int main(int argc, char *argv[])
{
// --------------------------
// Initialization
// --------------------------
SDL_bool running = SDL_TRUE;
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
fprintf(stderr, "SDL_Init Error: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// 3.3 core mode
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
SDL_Window *window = SDL_CreateWindow("SDL PLATFORM LAYER!", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 450,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
SDL_WINDOW_RESIZABLE);
if (window == NULL)
{
fprintf(stderr, "SDL_CreateWindow Error: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_GLContext context = SDL_GL_CreateContext(window);
if (context == NULL)
{
fprintf(stderr, "SDL_CreateContext Error: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
on_init();
while (running)
{
// Update
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
running = SDL_FALSE;
case SDL_WINDOWEVENT:
{
if (event.window.event == SDL_WINDOWEVENT_CLOSE)
{
running = SDL_FALSE;
}
else if (event.window.event == SDL_WINDOWEVENT_RESIZED)
{
rf_gl_viewport(0, 0, event.window.data1, event.window.data2);
}
break;
}
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
{
running = SDL_FALSE;
}
// // When the user presses R we try to hot reload the code.
if (event.key.keysym.sym == SDLK_r)
{
sdl_unload_game_code(&game_code);
game_code = sdl_load_game_code("./hot_code_reloading_dynamic.so", "./hot_code_reloading_temp.so");
game_code.game_refresh(&game);
}
}
}
if (game_code.is_valid)
{
game_code.game_update();
}
SDL_GL_SwapWindow(window);
}
// Cleanup
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
@chrisdill
Copy link
Author

chrisdill commented Jan 23, 2020

I was interested in the idea of the platform layer being seperate from the game but the example for rayfork only works on windows. I thought it would be cool to try a layer with SDL2. Mostly converting win32 calls to similar SDL2 versions.

I compile glad.c and game.c as seperate objects then link them into one shared library, hot_code_reloading_dynamic.so.

This can be improved further. I think there might be a way to skip compiling the individual object files first but I am not a expert in compiling. Also adding better error checking and making it work with .dlls as well so the SDL2 layer can work on Windows as well as Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment