Compare commits

..

No commits in common. "45b8b21ec3219966e0a9409115126adc4b49dc50" and "9d0a2cab814137020ce8607e206a3519a43b5204" have entirely different histories.

5 changed files with 67 additions and 106 deletions

View File

@ -22,12 +22,6 @@ static const char *audio_exts[AUDIO_FILE_TYPE_COUNT] = {
".xm", /* AUDIO_FILE_TYPE_XM */ ".xm", /* AUDIO_FILE_TYPE_XM */
}; };
static const uint8_t audio_exts_len[AUDIO_FILE_TYPE_COUNT] = {
sizeof ".ogg" - 1,
sizeof ".wav" - 1,
sizeof ".xm" - 1,
};
/* TODO: allow for vectorization and packed vectors (alignment care and alike) */ /* TODO: allow for vectorization and packed vectors (alignment care and alike) */
/* TODO: count frames without use, free the memory when threshold is met */ /* TODO: count frames without use, free the memory when threshold is met */
@ -65,10 +59,14 @@ static int64_t get_audio_data(const char *path, unsigned char **data) {
static AudioFileType infer_audio_file_type(const char *path) { static AudioFileType infer_audio_file_type(const char *path) {
size_t const path_len = SDL_strlen(path); size_t path_len = SDL_strlen(path);
for (int i = 0; i < AUDIO_FILE_TYPE_COUNT; ++i) { for (int i = 0; i < AUDIO_FILE_TYPE_COUNT; ++i) {
if (SDL_strncmp(&path[path_len - audio_exts_len[i]], audio_exts[i], audio_exts_len[i]) == 0) size_t ext_length = SDL_strlen(audio_exts[i]);
if (path_len <= ext_length)
continue;
if (SDL_strcmp(&path[path_len - ext_length], audio_exts[i]) == 0)
return (AudioFileType)i; return (AudioFileType)i;
} }
@ -247,35 +245,6 @@ void audio_play(const char *path,
float volume, float volume,
float panning) float panning)
{ {
if (!ctx.audio_initialized) {
profile_start("audio initialization");
SDL_AudioSpec request, got;
SDL_zero(request);
request.freq = AUDIO_FREQUENCY;
request.format = AUDIO_F32;
request.channels = 2;
#ifndef TWN_FEATURE_PUSH_AUDIO
request.callback = audio_callback;
#endif
/* TODO: check for errors */
ctx.audio_device = SDL_OpenAudioDevice(NULL, 0, &request, &got, 0);
ctx.audio_stream_format = got.format;
ctx.audio_stream_frequency = got.freq;
ctx.audio_stream_channel_count = got.channels;
/* TODO: relax this */
SDL_assert_always(got.freq == AUDIO_FREQUENCY);
SDL_assert_always(got.format == AUDIO_F32);
SDL_assert_always(got.channels == 2);
SDL_PauseAudioDevice(ctx.audio_device, 0);
profile_end("audio initialization");
ctx.audio_initialized = true;
}
if (channel) { if (channel) {
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel); AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
@ -328,17 +297,17 @@ TWN_API void audio_parameter(const char *channel, const char *param, float value
return; return;
} }
if (SDL_strncmp(param, "repeat", sizeof "repeat" - 1) == 0) { if (SDL_strcmp(param, "repeat") == 0) {
pair->value.repeat = (bool)value; pair->value.repeat = (bool)value;
} else if (SDL_strncmp(param, "volume", sizeof "volume" - 1) == 0) { } else if (SDL_strcmp(param, "volume") == 0) {
if (value > 1.0f || value < 0.0f) { if (value > 1.0f || value < 0.0f) {
log_warn("Out of range volume for channel %s set", channel); log_warn("Out of range volume for channel %s set", channel);
value = clampf(value, 0.0f, 1.0f); value = clampf(value, 0.0f, 1.0f);
} }
pair->value.volume = value; pair->value.volume = value;
} else if (SDL_strncmp(param, "panning", sizeof "panning" - 1) == 0) { } else if (SDL_strcmp(param, "panning") == 0) {
if (value > 1.0f || value < -1.0f) { if (value > 1.0f || value < -1.0f) {
log_warn("Out of range panning for channel %s set", channel); log_warn("Out of range panning for channel %s set", channel);
value = clampf(value, -1.0f, +1.0f); value = clampf(value, -1.0f, +1.0f);

View File

@ -79,7 +79,6 @@ typedef struct EngineContext {
bool render_double_buffered; bool render_double_buffered;
/* signals mouse focus, used to disable mouse capture */ /* signals mouse focus, used to disable mouse capture */
bool window_mouse_resident; bool window_mouse_resident;
bool audio_initialized;
} EngineContext; } EngineContext;
/* TODO: does it need to be marked with TWN_API? */ /* TODO: does it need to be marked with TWN_API? */

View File

@ -215,14 +215,11 @@ static void main_loop(void) {
game_object_tick(); game_object_tick();
input_state_update_postframe(&ctx.input); input_state_update_postframe(&ctx.input);
/* TODO: make it works when ctx.ticks_per_second != 60 */
#ifdef TWN_FEATURE_PUSH_AUDIO #ifdef TWN_FEATURE_PUSH_AUDIO
static uint8_t audio_buffer[(AUDIO_FREQUENCY / 60) * sizeof (float) * 2]; static uint8_t audio_buffer[(AUDIO_FREQUENCY / 60) * sizeof (float) * 2];
if (ctx.audio_initialized) {
audio_callback(NULL, audio_buffer, sizeof audio_buffer); audio_callback(NULL, audio_buffer, sizeof audio_buffer);
if (SDL_QueueAudio(ctx.audio_device, audio_buffer, sizeof audio_buffer)) if (SDL_QueueAudio(ctx.audio_device, audio_buffer, sizeof audio_buffer))
CRY_SDL("Error queueing audio: "); CRY_SDL("Error queueing audio: ");
}
#endif #endif
/* TODO: ctx.game_copy = ctx.game should be placed after it, but it messes with state used in render() */ /* TODO: ctx.game_copy = ctx.game should be placed after it, but it messes with state used in render() */
@ -343,22 +340,18 @@ ERR_PACK_MANIFEST_PATH_ALLOC_FAIL:
static bool initialize(void) { static bool initialize(void) {
profile_start("SDL initialization"); if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) == -1) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) == -1) {
CRY_SDL("SDL initialization failed."); CRY_SDL("SDL initialization failed.");
return false; return false;
} }
profile_end("SDL initialization");
/* first things first, most things here will be loaded from the config file */ /* first things first, most things here will be loaded from the config file */
/* it's expected to be present in the data directory, no matter what */ /* it's expected to be present in the data directory, no matter what */
/* that is why PhysicsFS is initialized before anything else */ /* that is why PhysicsFS is initialized before anything else */
toml_set_memutil(SDL_malloc, SDL_free); toml_set_memutil(SDL_malloc, SDL_free);
profile_start("pack dependency resolution");
/* time to orderly resolve any dependencies present */ /* time to orderly resolve any dependencies present */
resolve_pack_dependencies("data"); resolve_pack_dependencies("data");
profile_end("pack dependency resolution");
/* load the config file into an opaque table */ /* load the config file into an opaque table */
{ {
@ -485,8 +478,6 @@ static bool initialize(void) {
ctx.game.resolution.x = (float)ctx.base_render_width; ctx.game.resolution.x = (float)ctx.base_render_width;
ctx.game.resolution.y = (float)ctx.base_render_height; ctx.game.resolution.y = (float)ctx.base_render_height;
/* TODO: investigate viability of detached thread driver and window creation, as it's the worst load time offender */
profile_start("window creation");
ctx.window = SDL_CreateWindow(datum_title.u.s, ctx.window = SDL_CreateWindow(datum_title.u.s,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
@ -495,7 +486,6 @@ static bool initialize(void) {
//SDL_WINDOW_ALLOW_HIGHDPI | //SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_RESIZABLE | SDL_WINDOW_RESIZABLE |
SDL_WINDOW_OPENGL); SDL_WINDOW_OPENGL);
profile_end("window creation");
if (datum_title.ok) if (datum_title.ok)
SDL_free(datum_title.u.s); SDL_free(datum_title.u.s);
@ -508,7 +498,6 @@ static bool initialize(void) {
goto fail; goto fail;
} }
profile_start("opengl context creation");
ctx.gl_context = SDL_GL_CreateContext(ctx.window); ctx.gl_context = SDL_GL_CreateContext(ctx.window);
if (!ctx.gl_context) { if (!ctx.gl_context) {
CRY_SDL("GL context creation failed."); CRY_SDL("GL context creation failed.");
@ -526,12 +515,11 @@ static bool initialize(void) {
if (!render_init()) if (!render_init())
goto fail; goto fail;
setup_viewport(0, 0, (int)ctx.base_render_width, (int)ctx.base_render_height);
profile_end("opengl context creation");
/* might need this to have multiple windows */ /* might need this to have multiple windows */
ctx.window_id = SDL_GetWindowID(ctx.window); ctx.window_id = SDL_GetWindowID(ctx.window);
setup_viewport(0, 0, (int)ctx.base_render_width, (int)ctx.base_render_height);
/* TODO: */ /* TODO: */
// SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); // SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
ctx.window_dims.x = (float)ctx.base_render_width; ctx.window_dims.x = (float)ctx.base_render_width;
@ -540,6 +528,32 @@ static bool initialize(void) {
/* add a watcher for immediate updates on window size */ /* add a watcher for immediate updates on window size */
SDL_AddEventWatch(event_callback, NULL); SDL_AddEventWatch(event_callback, NULL);
/* audio initialization */
{
SDL_AudioSpec request, got;
SDL_zero(request);
request.freq = AUDIO_FREQUENCY;
request.format = AUDIO_F32;
request.channels = 2;
#ifndef TWN_FEATURE_PUSH_AUDIO
request.callback = audio_callback;
#endif
/* TODO: check for errors */
ctx.audio_device = SDL_OpenAudioDevice(NULL, 0, &request, &got, 0);
ctx.audio_stream_format = got.format;
ctx.audio_stream_frequency = got.freq;
ctx.audio_stream_channel_count = got.channels;
/* TODO: relax this */
SDL_assert_always(got.freq == AUDIO_FREQUENCY);
SDL_assert_always(got.format == AUDIO_F32);
SDL_assert_always(got.channels == 2);
SDL_PauseAudioDevice(ctx.audio_device, 0);
}
/* random seeding */ /* random seeding */
/* SDL_GetPerformanceCounter returns some platform-dependent number. */ /* SDL_GetPerformanceCounter returns some platform-dependent number. */
/* it should vary between game instances. i checked! random enough for me. */ /* it should vary between game instances. i checked! random enough for me. */
@ -613,9 +627,9 @@ static bool initialize(void) {
if (!datum_font_filtering.ok) { if (!datum_font_filtering.ok) {
ctx.font_filtering = TEXT_FONT_FILTERING_DEFAULT; ctx.font_filtering = TEXT_FONT_FILTERING_DEFAULT;
} else { } else {
if (SDL_strncmp(datum_font_filtering.u.s, "nearest", sizeof "nearest") == 0) { if (SDL_strcmp(datum_font_filtering.u.s, "nearest") == 0) {
ctx.font_filtering = TEXTURE_FILTER_NEAREAST; ctx.font_filtering = TEXTURE_FILTER_NEAREAST;
} else if (SDL_strncmp(datum_font_filtering.u.s, "linear", sizeof "nearest") == 0) { } else if (SDL_strcmp(datum_font_filtering.u.s, "linear") == 0) {
ctx.font_filtering = TEXTURE_FILTER_LINEAR; ctx.font_filtering = TEXTURE_FILTER_LINEAR;
} else { } else {
ctx.font_filtering = TEXT_FONT_FILTERING_DEFAULT; ctx.font_filtering = TEXT_FONT_FILTERING_DEFAULT;
@ -628,10 +642,8 @@ static bool initialize(void) {
ctx.render_queue_2d = NULL; ctx.render_queue_2d = NULL;
ctx.uncolored_mesh_batches = NULL; ctx.uncolored_mesh_batches = NULL;
profile_start("texture and text cache initialization");
textures_cache_init(&ctx.texture_cache, ctx.window); textures_cache_init(&ctx.texture_cache, ctx.window);
text_cache_init(&ctx.text_cache); text_cache_init(&ctx.text_cache);
profile_end("texture and text cache initialization");
/* input */ /* input */
toml_datum_t datum_keybind_slots = toml_int_in(engine, "keybind_slots"); toml_datum_t datum_keybind_slots = toml_int_in(engine, "keybind_slots");
@ -646,6 +658,13 @@ static bool initialize(void) {
} }
input_state_init(&ctx.input); input_state_init(&ctx.input);
/* scripting */
/*
if (!scripting_init(ctx)) {
goto fail;
}
*/
ctx.render_double_buffered = true; ctx.render_double_buffered = true;
ctx.window_mouse_resident = true; ctx.window_mouse_resident = true;
@ -662,6 +681,10 @@ fail:
/* will not be called on an abnormal exit */ /* will not be called on an abnormal exit */
static void clean_up(void) { static void clean_up(void) {
/*
scripting_deinit(ctx);
*/
input_state_deinit(&ctx.input); input_state_deinit(&ctx.input);
text_cache_deinit(&ctx.text_cache); text_cache_deinit(&ctx.text_cache);
textures_cache_deinit(&ctx.texture_cache); textures_cache_deinit(&ctx.texture_cache);
@ -686,8 +709,6 @@ static void reset_state(void) {
int enter_loop(int argc, char **argv) { int enter_loop(int argc, char **argv) {
profile_start("startup");
ctx.argc = argc; ctx.argc = argc;
ctx.argv = argv; ctx.argv = argv;
ctx.base_dir = SDL_GetBasePath(); ctx.base_dir = SDL_GetBasePath();
@ -706,7 +727,7 @@ int enter_loop(int argc, char **argv) {
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
/* override data directory */ /* override data directory */
if (SDL_strncmp(argv[i], "--data-dir", sizeof "--data-dir" - 1) == 0) { if (SDL_strcmp(argv[i], "--data-dir") == 0) {
if (argv[i+1] == NULL || SDL_strncmp(argv[i+1], "--", 2) == 0) { if (argv[i+1] == NULL || SDL_strncmp(argv[i+1], "--", 2) == 0) {
CRY("Data dir mount override failed.", "No arguments passed (expected 1)."); CRY("Data dir mount override failed.", "No arguments passed (expected 1).");
return EXIT_FAILURE; return EXIT_FAILURE;
@ -723,13 +744,13 @@ int enter_loop(int argc, char **argv) {
} }
/* force debug mode */ /* force debug mode */
if (SDL_strncmp(argv[i], "--debug", sizeof "--debug" - 1) == 0) { if (SDL_strcmp(argv[i], "--debug") == 0) {
force_debug = true; force_debug = true;
continue; continue;
} }
/* force release mode */ /* force release mode */
if (SDL_strncmp(argv[i], "--release", sizeof "--release" - 1) == 0) { if (SDL_strcmp(argv[i], "--release") == 0) {
force_release = false; force_release = false;
continue; continue;
} }
@ -765,16 +786,12 @@ int enter_loop(int argc, char **argv) {
update_viewport(); update_viewport();
profile_start("game object load");
/* now we can actually start doing stuff */ /* now we can actually start doing stuff */
game_object_load(); game_object_load();
profile_end("game object load");
ctx.was_successful = true; ctx.was_successful = true;
ctx.game.initialization_needed = true; ctx.game.initialization_needed = true;
profile_end("startup");
while (ctx.is_running) { while (ctx.is_running) {
if (game_object_try_reloading()) { if (game_object_try_reloading()) {
ctx.game.initialization_needed = true; ctx.game.initialization_needed = true;
@ -784,7 +801,6 @@ int enter_loop(int argc, char **argv) {
main_loop(); main_loop();
} }
if (ctx.game.debug)
profile_list_stats(); profile_list_stats();
/* loop is over */ /* loop is over */

View File

@ -186,8 +186,6 @@ static void upload_texture_from_surface(GPUTexture texture, SDL_Surface *surface
static void recreate_current_atlas_texture(TextureCache *cache) { static void recreate_current_atlas_texture(TextureCache *cache) {
profile_start("atlas recreation");
/* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */ /* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */
/* for example, if full page of 64x64 tiles was already filled, there's no real reason to process them further */ /* for example, if full page of 64x64 tiles was already filled, there's no real reason to process them further */
SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index]; SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index];
@ -218,8 +216,6 @@ static void recreate_current_atlas_texture(TextureCache *cache) {
/* texturize it! */ /* texturize it! */
upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface); upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface);
profile_end("atlas recreation");
} }
@ -311,9 +307,10 @@ void textures_cache_init(TextureCache *cache, SDL_Window *window) {
cache->window = window; cache->window = window;
sh_new_arena(cache->hash); sh_new_arena(cache->hash);
cache->node_buffer = SDL_malloc(ctx.texture_atlas_size * sizeof *cache->node_buffer); cache->node_buffer = SDL_calloc(ctx.texture_atlas_size, sizeof *cache->node_buffer);
add_new_atlas(cache); add_new_atlas(cache);
recreate_current_atlas_texture(cache);
} }

View File

@ -15,7 +15,6 @@ static struct ProfileItem {
uint64_t tick_start; uint64_t tick_start;
uint64_t tick_accum; uint64_t tick_accum;
uint64_t sample_count; uint64_t sample_count;
uint64_t worst_tick;
} value; } value;
} *profiles; } *profiles;
@ -320,40 +319,21 @@ void profile_start(char profile[const static 1]) {
void profile_end(char profile[const static 1]) { void profile_end(char profile[const static 1]) {
struct ProfileItem *p = shgetp_null(profiles, profile); struct ProfileItem *p = shgetp_null(profiles, profile);
if (!p) { if (!p) {
log_warn("Profile %s wasn't started!", profile); log_warn("profile %s wasn't started!", profile);
return; return;
} }
uint64_t took = SDL_GetPerformanceCounter() - p->value.tick_start;
p->value.tick_accum += took;
p->value.sample_count++; p->value.sample_count++;
p->value.tick_accum += SDL_GetPerformanceCounter() - p->value.tick_start;
if (p->value.worst_tick < took)
p->value.worst_tick = took;
} }
void profile_list_stats(void) { void profile_list_stats(void) {
for (size_t i = 0; i < shlenu(profiles); ++i) { for (size_t i = 0; i < shlenu(profiles); ++i) {
if (profiles[i].value.sample_count == 0) { log_info("profile '%s' on average took: %f seconds",
log_warn("Profile %s was started, but not once finished.", profiles[i].key);
}
else if (profiles[i].value.sample_count == 1) {
log_info("Profile '%s' took: %f seconds",
profiles[i].key,
(double)profiles[i].value.tick_accum / (double)(SDL_GetPerformanceFrequency()));
}
else if (profiles[i].value.sample_count > 1) {
log_info("Profile '%s' on average took: %f seconds, worst case: %f, sample count: %llu",
profiles[i].key, profiles[i].key,
(double)profiles[i].value.tick_accum / (double)profiles[i].value.tick_accum /
(double)profiles[i].value.sample_count / (double)profiles[i].value.sample_count /
(double)(SDL_GetPerformanceFrequency()), (double)(SDL_GetPerformanceFrequency()));
(double)profiles[i].value.worst_tick / (double)(SDL_GetPerformanceFrequency()),
profiles[i].value.sample_count);
}
} }
} }