poll workers for non-threaded support, emscripten main loop, remove twn_model.c
This commit is contained in:
parent
dc6b298532
commit
d76ea06470
@ -5,6 +5,8 @@ project(townengine LANGUAGES C)
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
|
||||
set(CMAKE_INSTALL_MESSAGE NEVER)
|
||||
|
||||
# TODO: test whether webgl 1 is good enough.
|
||||
|
||||
# SDL dependencies
|
||||
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
||||
if(NOT EMSCRIPTEN)
|
||||
@ -171,7 +173,8 @@ function(give_options_without_warnings target)
|
||||
set(LINK_FLAGS
|
||||
-Bsymbolic-functions
|
||||
|
||||
$<$<BOOL:${EMSCRIPTEN}>:-sLEGACY_GL_EMULATION -sGL_FFP_ONLY -sGL_UNSAFE_OPTS=1>
|
||||
$<$<BOOL:${EMSCRIPTEN}>:-sLEGACY_GL_EMULATION -sGL_FFP_ONLY -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
|
||||
-sENVIRONMENT=web -sDEFAULT_TO_CXX=0>
|
||||
$<$<BOOL:${EMSCRIPTEN}>:--preload-file ${TWN_OUT_DIR}/data@data -sALLOW_MEMORY_GROWTH>
|
||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:-Wl,--as-needed>
|
||||
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
|
||||
|
@ -17,6 +17,15 @@ void game_tick(void) {
|
||||
|
||||
struct state *state = ctx.udata;
|
||||
++state->counter;
|
||||
|
||||
m_sprite("nothing!",
|
||||
(Rect) {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.w = 32,
|
||||
.h = 32,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "twn_types.h"
|
||||
#include "twn_deferred_commands.h"
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
@ -41,7 +41,7 @@ static void APIENTRY opengl_log(GLenum source,
|
||||
|
||||
|
||||
bool render_init(void) {
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (gladLoadGLLoader(&SDL_GL_GetProcAddress) == 0) {
|
||||
CRY("Init", "GLAD failed");
|
||||
return false;
|
||||
@ -50,7 +50,7 @@ bool render_init(void) {
|
||||
|
||||
log_info("OpenGL context: %s\n", glGetString(GL_VERSION));
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
|
||||
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
|
||||
glHint(GL_FOG_HINT, GL_FASTEST);
|
||||
@ -98,7 +98,7 @@ static void finally_use_space_pipeline(void) {
|
||||
depth_range_high = 1.0;
|
||||
depth_range_low = 0.0;
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static GLuint list = 0;
|
||||
if (!list) {
|
||||
list = glGenLists(1);
|
||||
@ -109,7 +109,7 @@ static void finally_use_space_pipeline(void) {
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
#endif
|
||||
@ -125,7 +125,7 @@ static void finally_use_space_pipeline(void) {
|
||||
|
||||
/* solid white, no modulation */
|
||||
glColor4ub(255, 255, 255, 255);
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
} glEndList();
|
||||
|
||||
glCallList(list);
|
||||
@ -152,7 +152,7 @@ static void finally_use_2d_pipeline(void) {
|
||||
if (pipeline_last_used == PIPELINE_2D)
|
||||
return;
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static GLuint list = 0;
|
||||
if (!list) {
|
||||
list = glGenLists(1);
|
||||
@ -161,7 +161,7 @@ static void finally_use_2d_pipeline(void) {
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
/* removes near/far plane comparison and discard */
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
@ -171,7 +171,7 @@ static void finally_use_2d_pipeline(void) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
} glEndList();
|
||||
|
||||
} glCallList(list);
|
||||
@ -201,7 +201,7 @@ static void finally_use_texture_mode(TextureMode mode) {
|
||||
if (texture_mode_last_used == mode)
|
||||
return;
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static GLuint lists = 0;
|
||||
if (!lists) {
|
||||
lists = glGenLists(3);
|
||||
@ -214,7 +214,7 @@ static void finally_use_texture_mode(TextureMode mode) {
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
} glEndList();
|
||||
|
||||
/* seethrough */
|
||||
@ -225,7 +225,7 @@ static void finally_use_texture_mode(TextureMode mode) {
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_EQUAL, 1.0f);
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
} glEndList();
|
||||
|
||||
/* opaque */
|
||||
@ -235,7 +235,7 @@ static void finally_use_texture_mode(TextureMode mode) {
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
} glEndList();
|
||||
}
|
||||
|
||||
@ -257,7 +257,7 @@ VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) {
|
||||
SDL_assert(bytes != 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW);
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
||||
#else
|
||||
void *mapping = SDL_malloc(bytes);
|
||||
@ -273,7 +273,7 @@ VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) {
|
||||
|
||||
|
||||
void finish_vertex_builder(VertexBufferBuilder *builder) {
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (!glUnmapBuffer(GL_ARRAY_BUFFER))
|
||||
CRY("finish_vertex_builder", "Error unmapping a vertex array buffer");
|
||||
#else
|
||||
@ -372,7 +372,7 @@ void finally_render_skybox(DeferredCommandDrawSkybox command) {
|
||||
/* TODO: figure out which coordinates to use to not have issues with far z */
|
||||
/* TODO: recalculate the list if far z requirement changes */
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static GLuint list = 0;
|
||||
if (!list) {
|
||||
list = glGenLists(1);
|
||||
@ -384,7 +384,7 @@ void finally_render_skybox(DeferredCommandDrawSkybox command) {
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
/* removes near/far plane comparison and discard */
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
@ -452,7 +452,7 @@ void finally_render_skybox(DeferredCommandDrawSkybox command) {
|
||||
glVertex3f(-50.f, 50.f, -50.f);
|
||||
} glEnd();
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (GLAD_GL_ARB_depth_clamp)
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
#endif
|
||||
@ -460,7 +460,7 @@ void finally_render_skybox(DeferredCommandDrawSkybox command) {
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_TEXTURE_CUBE_MAP);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
} glEndList();
|
||||
glCallList(list);
|
||||
#endif
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <stb_ds.h>
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
|
@ -1,235 +0,0 @@
|
||||
#include "twn_draw_c.h"
|
||||
#include "twn_draw.h"
|
||||
#include "twn_workers_c.h"
|
||||
#include "twn_textures_c.h"
|
||||
|
||||
#define FAST_OBJ_IMPLEMENTATION
|
||||
#define FAST_OBJ_REALLOC SDL_realloc
|
||||
#define FAST_OBJ_FREE SDL_free
|
||||
#include <fast_obj.h>
|
||||
#include <stb_ds.h>
|
||||
#include <physfs.h>
|
||||
#include <physfsrwops.h>
|
||||
|
||||
|
||||
static struct ModelCacheItem {
|
||||
char *key;
|
||||
struct ModelCacheItemValue {
|
||||
fastObjMesh *mesh;
|
||||
} value;
|
||||
} *model_cache;
|
||||
|
||||
/* TODO: store index to model cache instead */
|
||||
static struct ModelDrawCommand {
|
||||
char *model;
|
||||
Vec3 position;
|
||||
Vec3 rotation;
|
||||
Vec3 scale;
|
||||
} *model_draw_commands;
|
||||
|
||||
/* deferred queue of model files to load from worker threads */
|
||||
static SDL_mutex *model_load_mutex;
|
||||
static char const **model_load_queue;
|
||||
static size_t model_load_queued;
|
||||
static bool model_load_initialized;
|
||||
|
||||
/* use streaming via callbacks to reduce memory congestion */
|
||||
static void model_load_callback_close(void *handle, void *udata) {
|
||||
(void)udata;
|
||||
((SDL_RWops *)handle)->close(handle);
|
||||
}
|
||||
|
||||
static void *model_load_callback_open(const char *path, void *udata) {
|
||||
(void)udata;
|
||||
return PHYSFSRWOPS_openRead(path);
|
||||
}
|
||||
|
||||
static size_t model_load_callback_read(void *handle, void *dst, size_t bytes, void *udata) {
|
||||
(void)udata;
|
||||
return ((SDL_RWops *)handle)->read(handle, dst, 1, bytes);
|
||||
}
|
||||
|
||||
static unsigned long model_load_callback_size(void *handle, void *udata) {
|
||||
(void)udata;
|
||||
return ((SDL_RWops *)handle)->size(handle);
|
||||
}
|
||||
|
||||
|
||||
/* TODO: is there a way to do this nicely while locking main thread? */
|
||||
/* sleeping over atomic counter might be good enough i guess */
|
||||
/* it's safe to access everything without lock after this returns true and no public api is possible to call */
|
||||
static bool model_load_workers_finished(void) {
|
||||
bool result;
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
result = model_load_queued == 0;
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* entry point for workers, polled every time a job semaphore is posted */
|
||||
/* returns false if there was nothing to do */
|
||||
bool model_load_workers_thread(void) {
|
||||
/* attempt to grab something to work on */
|
||||
char const *load_request = NULL;
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
if (arrlenu(model_load_queue) != 0)
|
||||
load_request = arrpop(model_load_queue);
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
/* nothing to do, bail */
|
||||
if (!load_request)
|
||||
return false;
|
||||
|
||||
fastObjCallbacks const callbacks = {
|
||||
.file_close = model_load_callback_close,
|
||||
.file_open = model_load_callback_open,
|
||||
.file_read = model_load_callback_read,
|
||||
.file_size = model_load_callback_size
|
||||
};
|
||||
|
||||
fastObjMesh *const mesh = fast_obj_read_with_callbacks(load_request, &callbacks, NULL);
|
||||
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
struct ModelCacheItem *item = shgetp(model_cache, load_request);
|
||||
item->value.mesh = mesh;
|
||||
model_load_queued--;
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void draw_model(const char *model,
|
||||
Vec3 position,
|
||||
Vec3 rotation,
|
||||
Vec3 scale)
|
||||
{
|
||||
if (!model_load_initialized) {
|
||||
model_load_mutex = SDL_CreateMutex();
|
||||
model_load_initialized = true;
|
||||
}
|
||||
|
||||
struct ModelCacheItem const *item;
|
||||
|
||||
/* TODO: make it lockless */
|
||||
/* if model is missing, queue it up for loading */
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
if (!(item = shgetp_null(model_cache, model))) {
|
||||
model = SDL_strdup(model);
|
||||
shput(model_cache, model, (struct ModelCacheItemValue){0});
|
||||
arrpush(model_load_queue, model);
|
||||
model_load_queued++;
|
||||
SDL_SemPost(workers_job_semaphore);
|
||||
} else
|
||||
model = item->key;
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
|
||||
struct ModelDrawCommand const command = {
|
||||
.model = (char *)model,
|
||||
.position = position,
|
||||
.rotation = rotation,
|
||||
.scale = scale
|
||||
};
|
||||
arrpush(model_draw_commands, command);
|
||||
}
|
||||
|
||||
|
||||
void finally_draw_models(void) {
|
||||
while (!model_load_workers_finished())
|
||||
SDL_Delay(1);
|
||||
|
||||
/* TODO: have special path for them, preserving the buffers and potentially using instanced draw */
|
||||
for (int i = 0; i < arrlen(model_draw_commands); ++i) {
|
||||
struct ModelDrawCommand const *const command = &model_draw_commands[i];
|
||||
fastObjMesh const *const mesh = model_cache[shgeti(model_cache, command->model)].value.mesh;
|
||||
SDL_assert(mesh);
|
||||
for (unsigned int g = 0; g < mesh->group_count; ++g) {
|
||||
fastObjGroup const *const group = &mesh->groups[g];
|
||||
unsigned int idx = 0;
|
||||
for (unsigned int f = 0; f < group->face_count; ++f) {
|
||||
unsigned int const vertices = mesh->face_vertices[group->face_offset + f];
|
||||
// fastObjTexture const *const texture = &mesh->textures[group->face_offset + f];
|
||||
// log_info("material: %s", material->name);
|
||||
/* TODO: support arbitrary fans */
|
||||
unsigned int const material_index = mesh->face_materials[group->index_offset + f];
|
||||
fastObjMaterial const *const material = mesh->materials ? &mesh->materials[material_index] : NULL;
|
||||
if (vertices == 4) {
|
||||
fastObjIndex const i0 = mesh->indices[group->index_offset + idx + 0];
|
||||
fastObjIndex const i1 = mesh->indices[group->index_offset + idx + 1];
|
||||
fastObjIndex const i2 = mesh->indices[group->index_offset + idx + 2];
|
||||
fastObjIndex const i3 = mesh->indices[group->index_offset + idx + 3];
|
||||
draw_quad(
|
||||
material ? mesh->textures[material->map_Kd].name : NULL,
|
||||
(Vec3) { mesh->positions[3 * i0.p + 0] * command->scale.x, mesh->positions[3 * i0.p + 1] * command->scale.y, mesh->positions[3 * i0.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i1.p + 0] * command->scale.x, mesh->positions[3 * i1.p + 1] * command->scale.y, mesh->positions[3 * i1.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i2.p + 0] * command->scale.x, mesh->positions[3 * i2.p + 1] * command->scale.y, mesh->positions[3 * i2.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i3.p + 0] * command->scale.x, mesh->positions[3 * i3.p + 1] * command->scale.y, mesh->positions[3 * i3.p + 2] * command->scale.z },
|
||||
(Rect) { .w = 64, .h = 64 },
|
||||
(Color) { 255, 255, 255, 255 }
|
||||
);
|
||||
} else if (vertices == 3) {
|
||||
fastObjIndex const i0 = mesh->indices[group->index_offset + idx + 0];
|
||||
fastObjIndex const i1 = mesh->indices[group->index_offset + idx + 1];
|
||||
fastObjIndex const i2 = mesh->indices[group->index_offset + idx + 2];
|
||||
draw_triangle(
|
||||
material ? mesh->textures[material->map_Kd].name : NULL,
|
||||
(Vec3) { mesh->positions[3 * i0.p + 0] * command->scale.x, mesh->positions[3 * i0.p + 1] * command->scale.y, mesh->positions[3 * i0.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i1.p + 0] * command->scale.x, mesh->positions[3 * i1.p + 1] * command->scale.y, mesh->positions[3 * i1.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i2.p + 0] * command->scale.x, mesh->positions[3 * i2.p + 1] * command->scale.y, mesh->positions[3 * i2.p + 2] * command->scale.z },
|
||||
(Vec2) {0,0},
|
||||
(Vec2) {0,0},
|
||||
(Vec2) {0,0},
|
||||
(Color){255, 255, 255, 255},
|
||||
(Color){255, 255, 255, 255},
|
||||
(Color){255, 255, 255, 255}
|
||||
);
|
||||
} else {
|
||||
fastObjIndex const i0 = mesh->indices[group->index_offset + idx];
|
||||
for (unsigned int z = 0; z < vertices - 2; ++z) {
|
||||
fastObjIndex const i1 = mesh->indices[group->index_offset + idx + 1 + z];
|
||||
fastObjIndex const i2 = mesh->indices[group->index_offset + idx + 2 + z];
|
||||
draw_triangle(
|
||||
material ? mesh->textures[material->map_Kd].name : NULL,
|
||||
(Vec3) { mesh->positions[3 * i0.p + 0] * command->scale.x, mesh->positions[3 * i0.p + 1] * command->scale.y, mesh->positions[3 * i0.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i1.p + 0] * command->scale.x, mesh->positions[3 * i1.p + 1] * command->scale.y, mesh->positions[3 * i1.p + 2] * command->scale.z },
|
||||
(Vec3) { mesh->positions[3 * i2.p + 0] * command->scale.x, mesh->positions[3 * i2.p + 1] * command->scale.y, mesh->positions[3 * i2.p + 2] * command->scale.z },
|
||||
(Vec2) {0,0},
|
||||
(Vec2) {0,0},
|
||||
(Vec2) {0,0},
|
||||
(Color){255, 255, 255, 255},
|
||||
(Color){255, 255, 255, 255},
|
||||
(Color){255, 255, 255, 255}
|
||||
);
|
||||
}
|
||||
}
|
||||
idx += vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrsetlen(model_draw_commands, 0);
|
||||
}
|
||||
|
||||
|
||||
/* drop model caches */
|
||||
void free_model_cache(void) {
|
||||
while (!model_load_workers_finished())
|
||||
SDL_Delay(1);
|
||||
|
||||
for (size_t i = 0; i < shlenu(model_cache); ++i) {
|
||||
fast_obj_destroy(model_cache[i].value.mesh);
|
||||
SDL_free(model_cache[i].key);
|
||||
}
|
||||
|
||||
shfree(model_cache);
|
||||
}
|
||||
|
||||
|
||||
void model_state_deinit(void) {
|
||||
if (!model_load_initialized)
|
||||
return;
|
||||
free_model_cache();
|
||||
arrfree(model_load_queue);
|
||||
SDL_DestroyMutex(model_load_mutex);
|
||||
model_load_initialized = false;
|
||||
}
|
@ -162,7 +162,7 @@ void draw_model(const char *model,
|
||||
};
|
||||
arrpush(model_load_queue, request);
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
SDL_SemPost(workers_job_semaphore);
|
||||
workers_add_job();
|
||||
} else
|
||||
modelcopy = item->key;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "twn_util.h"
|
||||
#include "twn_engine_context_c.h"
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#define DMON_IMPL
|
||||
#include <dmon.h>
|
||||
#include <stb_ds.h>
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#define TICKS_PER_SECOND_DEFAULT 60
|
||||
#define PACKAGE_EXTENSION "btw"
|
||||
@ -24,7 +26,7 @@
|
||||
|
||||
static void pack_contents_modified(char const *path, enum FilewatchAction action);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static SDL_sem *opengl_load_semaphore;
|
||||
#endif
|
||||
|
||||
@ -105,14 +107,20 @@ static void update_viewport(void) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void garbage_collect(void) {
|
||||
file_read_garbage_collect();
|
||||
}
|
||||
|
||||
|
||||
static void main_loop(void) {
|
||||
/*
|
||||
if (!ctx.is_running) {
|
||||
end(ctx);
|
||||
clean_up(ctx);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (!ctx.is_running)
|
||||
emscripten_cancel_main_loop();
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
/* dispatch all filewatch driven events, such as game object and asset pack reload */
|
||||
filewatch_poll();
|
||||
|
||||
/* frame timer */
|
||||
int64_t current_frame_time = SDL_GetPerformanceCounter();
|
||||
@ -228,6 +236,8 @@ static void main_loop(void) {
|
||||
ctx.game.initialization_needed = false;
|
||||
}
|
||||
|
||||
workers_poll();
|
||||
|
||||
/* TODO: in some cases machine might want to assume frames will be fed as much as possible */
|
||||
/* which for now is broken as glBufferData with NULL is used all over right after render */
|
||||
if (frames != 0)
|
||||
@ -236,6 +246,8 @@ static void main_loop(void) {
|
||||
/* don't waste clock cycles on useless work */
|
||||
/* TODO: make it adjustable from config */
|
||||
SDL_Delay((uint32_t)(ctx.desired_frametime - ctx.frame_accumulator) / 1250000);
|
||||
|
||||
garbage_collect();
|
||||
}
|
||||
|
||||
|
||||
@ -425,7 +437,7 @@ static bool initialize(void) {
|
||||
toml_datum_t datum_debug = toml_bool_in(game, "debug");
|
||||
ctx.game.debug = datum_debug.ok ? datum_debug.u.b : true;
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
|
||||
|
||||
@ -445,6 +457,8 @@ static bool initialize(void) {
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
|
||||
#endif
|
||||
|
||||
toml_datum_t datum_title = toml_string_in(about, "title");
|
||||
@ -630,7 +644,7 @@ static bool initialize(void) {
|
||||
profile_end("game object load");
|
||||
|
||||
/* delayed as further as possible so that more work is done before we have to wait */
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_SemWait(opengl_load_semaphore);
|
||||
SDL_DestroySemaphore(opengl_load_semaphore);
|
||||
profile_end("opengl loading");
|
||||
@ -756,11 +770,6 @@ static bool try_mounting_root_pack(char *path) {
|
||||
}
|
||||
|
||||
|
||||
static void garbage_collect(void) {
|
||||
file_read_garbage_collect();
|
||||
}
|
||||
|
||||
|
||||
int enter_loop(int argc, char **argv) {
|
||||
profile_start("startup");
|
||||
|
||||
@ -771,7 +780,7 @@ int enter_loop(int argc, char **argv) {
|
||||
}
|
||||
profile_end("SDL initialization");
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
profile_start("opengl loading");
|
||||
opengl_load_semaphore = SDL_CreateSemaphore(0);
|
||||
SDL_Thread *opengl_load_thread = SDL_CreateThread(opengl_load_thread_fn, "opengl loader", opengl_load_semaphore);
|
||||
@ -871,12 +880,12 @@ int enter_loop(int argc, char **argv) {
|
||||
|
||||
profile_end("startup");
|
||||
|
||||
while (ctx.is_running) {
|
||||
/* dispatch all filewatch driven events, such as game object and asset pack reload */
|
||||
filewatch_poll();
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_main_loop(main_loop, ctx.desired_frametime, true);
|
||||
#else
|
||||
while (ctx.is_running)
|
||||
main_loop();
|
||||
garbage_collect();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx.game.debug)
|
||||
profile_list_stats();
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "twn_loop_c.h"
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#define SDL_MAIN_HANDLED
|
||||
#endif
|
||||
#include <SDL2/SDL.h>
|
||||
|
@ -466,7 +466,7 @@ static TextureKey textures_load(TextureCache *cache, const char *path) {
|
||||
SDL_UnlockMutex(textures_load_mutex);
|
||||
|
||||
/* signal work to do */
|
||||
SDL_SemPost(workers_job_semaphore);
|
||||
workers_add_job();
|
||||
|
||||
cache->is_dirty = true;
|
||||
|
||||
|
@ -2,12 +2,19 @@
|
||||
#include "twn_workers_c.h"
|
||||
#include "rendering/twn_draw_c.h"
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_sem *workers_job_semaphore;
|
||||
|
||||
static size_t workers_pool_size;
|
||||
static SDL_mutex *workers_mutex;
|
||||
static bool workers_should_exit;
|
||||
static SDL_sem *workers_exit_semaphore; /* should come to count of `workers_pool_size` */
|
||||
static bool workers_should_exit;
|
||||
#else
|
||||
/* accomulate to late poll from main thread itself */
|
||||
static uint32_t workers_job_count;
|
||||
#endif
|
||||
|
||||
static size_t workers_pool_size;
|
||||
|
||||
|
||||
/* logic is such that when job is posted, worker threads attempt to grab it from any possible entry point */
|
||||
/* if it did something, which is signaled by `true` return, go back to waiting on semaphore, so that it's decremented properly */
|
||||
@ -15,6 +22,7 @@ static int worker_thread(void *udata) {
|
||||
(void)udata;
|
||||
|
||||
while (true) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
/* check whether loop should end */
|
||||
SDL_LockMutex(workers_mutex);
|
||||
if (workers_should_exit) {
|
||||
@ -26,6 +34,11 @@ static int worker_thread(void *udata) {
|
||||
/* wait and occasionally go back to check whether it all should end */
|
||||
if (SDL_SemWaitTimeout(workers_job_semaphore, 100) == SDL_MUTEX_TIMEDOUT)
|
||||
continue;
|
||||
#else
|
||||
if (workers_job_count <= 0)
|
||||
break;
|
||||
workers_job_count--;
|
||||
#endif
|
||||
|
||||
/* process models, which will trigger texture loads */
|
||||
if (models_load_workers_thread())
|
||||
@ -35,8 +48,10 @@ static int worker_thread(void *udata) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
/* let the main thread collect it */
|
||||
SDL_SemPost(workers_exit_semaphore);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -50,7 +65,7 @@ bool workers_init(size_t worker_count) {
|
||||
if (worker_count > MAX_WORKERS)
|
||||
worker_count = MAX_WORKERS;
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
/* spawn a bunch of detached threads without references to them */
|
||||
for (size_t i = 0; i < worker_count; ++i) {
|
||||
SDL_Thread *thread = SDL_CreateThread(worker_thread, "worker", NULL);
|
||||
@ -68,7 +83,7 @@ bool workers_init(size_t worker_count) {
|
||||
|
||||
|
||||
void workers_deinit(void) {
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_LockMutex(workers_mutex);
|
||||
workers_should_exit = true;
|
||||
SDL_UnlockMutex(workers_mutex);
|
||||
@ -83,3 +98,19 @@ void workers_deinit(void) {
|
||||
#endif
|
||||
workers_pool_size = 0;
|
||||
}
|
||||
|
||||
|
||||
void workers_add_job(void) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_SemPost(workers_job_semaphore);
|
||||
#else
|
||||
workers_job_count++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void workers_poll(void) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
worker_thread(NULL);
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
@ -7,11 +7,10 @@
|
||||
|
||||
#define MAX_WORKERS 9
|
||||
|
||||
/* workers are waiting on this, increment this value when some work needs to be done */
|
||||
/* for now every possible job path is hardcoded in twn_workers.c itself */
|
||||
extern SDL_sem *workers_job_semaphore;
|
||||
|
||||
bool workers_init(size_t worker_count);
|
||||
void workers_deinit(void);
|
||||
void workers_add_job(void);
|
||||
/* for targets lacking threading support main thread could do the work */
|
||||
void workers_poll(void);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user