-Subproject commit 06492c598158cf825a18aececaf7511d7fd04f48
+Subproject commit eef4d3c86a653f91b7221c80809ba8ab56f94cf1
|IMP| | vg_ui(void)
| | | |
| | '----'
-'___'
-
- .-.
-| ? |
-| | .-------------------------------------.
-|API| | vg_fatal_exit_loop( const char *err ) |
-| | '-------------------------------------'
-| | |
-| | .------+.
-| | | |
-| | | v
-|IMP| '- vg_framebuffer_resize(void)
'___'
*/
#include "vg_m.h"
#include "vg_io.h"
#include "vg_log.h"
- #include "vg_async.h"
#include "vg_steam.h"
+ #include <setjmp.h>
//#define VG_SYNC_DEBUG
#ifdef VG_SYNC_DEBUG
SDL_Window *window;
SDL_GLContext gl_context;
- SDL_SpinLock sl_context;
- SDL_sem *sem_allow_exec,
- *sem_exec_finished,
- *sem_loader;
+ SDL_sem *sem_loader; /* allows only one loader at a time */
- SDL_threadID thread_id_main,
- thread_id_loader,
- thread_id_with_opengl_context;
- int context_ownership_depth;
+ jmp_buf env_loader_exit;
- int exec_context;
+ SDL_threadID thread_id_main,
+ thread_id_loader;
+ SDL_SpinLock sl_status;
enum engine_status
{
k_engine_status_none,
+ k_engine_status_load_internal,
k_engine_status_running,
k_engine_status_crashed
}
engine_status;
- const char *str_const_engine_err;
- int is_loaded;
/* Window information */
int window_x,
k_thread_purpose_loader
};
-VG_STATIC void vg_fatal_exit_loop( const char *error );
+#include "vg_async.h"
-/*
- * Checks if the engine is running
- */
-VG_STATIC void _vg_ensure_engine_running(void)
+VG_STATIC enum engine_status _vg_engine_status(void)
{
- /* Check if the engine is no longer running */
- SDL_AtomicLock( &vg.sl_context );
+ SDL_AtomicLock( &vg.sl_status );
enum engine_status status = vg.engine_status;
- SDL_AtomicUnlock( &vg.sl_context );
+ SDL_AtomicUnlock( &vg.sl_status );
- if( status != k_engine_status_running ){
- while(1) {
- VG_SYNC_LOG( "[%d] No longer running...\n");
- SDL_Delay(1000);
- }
- }
+ return status;
}
VG_STATIC enum vg_thread_purpose vg_thread_purpose(void)
{
- SDL_AtomicLock( &vg.sl_context );
+ SDL_AtomicLock( &vg.sl_status );
if( vg.thread_id_main == SDL_GetThreadID(NULL) ){
- SDL_AtomicUnlock( &vg.sl_context );
+ SDL_AtomicUnlock( &vg.sl_status );
return k_thread_purpose_main;
}
else{
- SDL_AtomicUnlock( &vg.sl_context );
+ SDL_AtomicUnlock( &vg.sl_status );
return k_thread_purpose_loader;
}
}
-/*
- * Sync execution so that the OpenGL context is switched onto this thread.
- * Anything after this call will be in a valid context.
- */
-VG_STATIC void vg_acquire_thread_sync(void)
-{
- /* We dont want to do anything if this is the main thread */
-
- if( vg_thread_purpose() == k_thread_purpose_loader ){
- VG_SYNC_LOG( "[%d] vg_acquire_thread_sync()\n" );
- _vg_ensure_engine_running();
-
- SDL_AtomicLock( &vg.sl_context );
- if( vg.context_ownership_depth == 0 ){
- vg.context_ownership_depth ++;
- vg.exec_context = 1;
- SDL_AtomicUnlock( &vg.sl_context );
-
- /* wait until told we can go */
- VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n" );
- SDL_SemWait( vg.sem_allow_exec );
-
- _vg_ensure_engine_running();
-
- SDL_GL_MakeCurrent( vg.window, vg.gl_context );
- VG_SYNC_LOG( "[%d] granted\n" );
- }
- else{
- vg.context_ownership_depth ++;
- VG_SYNC_LOG( "[%d] granted\n" );
- SDL_AtomicUnlock( &vg.sl_context );
- }
- }
-}
-
-/*
- * Signify that we are done with the OpenGL context in this thread.
- * Anything after this call will be in an undefined context.
- */
-VG_STATIC void vg_release_thread_sync(void)
-{
- if( vg_thread_purpose() == k_thread_purpose_loader ){
- VG_SYNC_LOG( "[%d] vg_release_thread_sync()\n" );
-
- SDL_AtomicLock( &vg.sl_context );
- vg.context_ownership_depth --;
-
- if( vg.context_ownership_depth == 0 ){
- SDL_AtomicUnlock( &vg.sl_context );
- VG_SYNC_LOG( "[%d] Releasing context.\n" );
- SDL_GL_MakeCurrent( NULL, NULL );
- SDL_SemPost( vg.sem_exec_finished );
- }
- else
- SDL_AtomicUnlock( &vg.sl_context );
- }
-}
-
-VG_STATIC void _vg_run_synced(void)
-{
- SDL_AtomicLock( &vg.sl_context );
-
- if( vg.exec_context != 0 ){
- VG_SYNC_LOG( "[%d] _vg_run_synced() (%d).\n", vg.exec_context );
- vg.exec_context = 0;
- SDL_AtomicUnlock( &vg.sl_context );
-
- /* allow operations to go */
- SDL_GL_MakeCurrent( NULL, NULL );
- SDL_SemPost( vg.sem_allow_exec );
-
- /* wait for operations to complete */
- VG_SYNC_LOG( "[%d] Waiting for content.\n" );
- SDL_SemWait( vg.sem_exec_finished );
-
- /* check if we killed the engine */
- _vg_ensure_engine_running();
-
- /* re-engage main thread */
- VG_SYNC_LOG( "[%d] Re-engaging.\n" );
- SDL_GL_MakeCurrent( vg.window, vg.gl_context );
- }
- else{
- VG_SYNC_LOG( "[%d] Nothing to do.\n" );
- SDL_AtomicUnlock( &vg.sl_context );
- }
-}
-
VG_STATIC void _vg_opengl_sync_init(void)
{
- vg.sem_allow_exec = SDL_CreateSemaphore(0);
- vg.sem_exec_finished = SDL_CreateSemaphore(0);
vg.sem_loader = SDL_CreateSemaphore(1);
}
}
if( fail )
- vg_fatal_exit_loop( "OpenGL Error" );
+ vg_fatal_error( "OpenGL Error" );
+}
+
+VG_STATIC void async_vg_bake_shaders( void *payload, u32 size )
+{
+ vg_shaders_compile();
}
VG_STATIC void vg_bake_shaders(void)
{
- vg_acquire_thread_sync();
vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL );
- vg_shaders_compile();
- vg_release_thread_sync();
+ vg_async_item *call = vg_async_alloc(0);
+ vg_async_dispatch( call, async_vg_bake_shaders );
}
-void test_async_runner( void *payload, u32 size )
+void async_internal_complete( void *payload, u32 size )
{
- vg_success( "Async call test (%p, %u)\n", payload, size );
+ vg_success( "Internal async setup complete\n" );
+ SDL_AtomicLock( &vg.sl_status );
+
+ if( vg.engine_status == k_engine_status_crashed ){
+ SDL_AtomicUnlock( &vg.sl_status );
+ return;
+ }
+ else{
+ vg.engine_status = k_engine_status_running;
+ }
+
+ SDL_AtomicUnlock( &vg.sl_status );
}
VG_STATIC void _vg_load_full(void)
vg_loader_step( vg_audio_init, vg_audio_free );
vg_loader_step( vg_profiler_init, NULL );
+ vg_async_item *test_async = vg_async_alloc( 0 );
+ vg_async_dispatch( test_async, async_internal_complete );
+
/* client */
vg_load();
-
- vg_async_item *test_async = vg_async_alloc( 0 );
- vg_async_dispatch( test_async, test_async_runner );
}
VG_STATIC void _vg_process_events(void)
{
- /* Update timers */
v2_zero( vg.mouse_wheel );
v2_zero( vg.mouse_delta );
{
vg_profile_begin( &vg_prof_render );
- if( vg.is_loaded ){
- /* render */
- vg.engine_stage = k_engine_stage_rendering;
- vg_render();
+ /* render */
+ vg.engine_stage = k_engine_stage_rendering;
+ vg_render();
- /* ui */
- vg.engine_stage = k_engine_stage_ui;
- {
- ui_begin( vg.window_x, vg.window_y );
+ /* ui */
+ vg.engine_stage = k_engine_stage_ui;
+ {
+ ui_begin( vg.window_x, vg.window_y );
- /* TODO */
- ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
+ /* TODO */
+ ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
- int frame_target = vg.display_refresh_rate;
+ int frame_target = vg.display_refresh_rate;
- if( vg.fps_limit > 0 ){
- frame_target = vg.fps_limit;
- }
+ if( vg.fps_limit > 0 ){
+ frame_target = vg.fps_limit;
+ }
+
+ vg_profile_drawn(
+ (struct vg_profile *[]){
+ &vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3,
+ (1.0f/(float)frame_target)*1000.0f,
+ (ui_rect){ 4, 4, 250, 0 }, 0
+ );
+
+ if( vg_profiler ){
+ char perf[256];
- vg_profile_drawn(
- (struct vg_profile *[]){
- &vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3,
- (1.0f/(float)frame_target)*1000.0f,
- (ui_rect){ 4, 4, 250, 0 }, 0
- );
-
- if( vg_profiler ){
- char perf[256];
-
- snprintf( perf, 255,
- "x: %d y: %d\n"
- "refresh: %d (%.1fms)\n"
- "samples: %d\n"
- "iterations: %d (acc: %.3fms%%)\n"
- "time: real(%.2f) delta(%.2f) rate(%.2f)\n"
+ snprintf( perf, 255,
+ "x: %d y: %d\n"
+ "refresh: %d (%.1fms)\n"
+ "samples: %d\n"
+ "iterations: %d (acc: %.3fms%%)\n"
+ "time: real(%.2f) delta(%.2f) rate(%.2f)\n"
#ifdef _WIN32
- " extrap(%.2f) frame(%.2f) spin( %llu )\n",
+ " extrap(%.2f) frame(%.2f) spin( %llu )\n",
#else
- " extrap(%.2f) frame(%.2f) spin( %lu )\n",
+ " extrap(%.2f) frame(%.2f) spin( %lu )\n",
#endif
- vg.window_x, vg.window_y,
- frame_target, (1.0f/(float)frame_target)*1000.0f,
- vg.samples,
- vg.fixed_iterations,
- (vg.time_fixed_accumulator/VG_TIMESTEP_FIXED)*100.0f,
- vg.time, vg.time_delta, vg.time_rate,
- vg.time_fixed_extrapolate, vg.time_frame_delta,
- vg.time_spinning );
-
- ui_text( (ui_rect){258, 4+24+12+12,0,0},perf, 1,0);
- }
-
- /* FIXME */
- audio_debug_ui( vg.pv );
- vg_ui();
- _vg_console_draw();
-
- ui_resolve();
- ui_draw( NULL );
+ vg.window_x, vg.window_y,
+ frame_target, (1.0f/(float)frame_target)*1000.0f,
+ vg.samples,
+ vg.fixed_iterations,
+ (vg.time_fixed_accumulator/VG_TIMESTEP_FIXED)*100.0f,
+ vg.time, vg.time_delta, vg.time_rate,
+ vg.time_fixed_extrapolate, vg.time_frame_delta,
+ vg.time_spinning );
+
+ ui_text( (ui_rect){258, 4+24+12+12,0,0},perf, 1,0);
}
+
+ /* FIXME */
+ audio_debug_ui( vg.pv );
+ vg_ui();
+ _vg_console_draw();
+
+ ui_resolve();
+ ui_draw( NULL );
}
vg_profile_end( &vg_prof_render );
return 0;
}
+VG_STATIC int _vg_crashscreen(void)
+{
+ if( vg.window_should_close )
+ return 1;
+
+ if( vg_getkey( SDLK_ESCAPE ) )
+ return 1;
+
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+ glEnable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
+ glBlendEquation(GL_FUNC_ADD);
+
+ glClearColor( 0.15f + sinf(vg.time)*0.1f, 0.0f, 0.0f,1.0f );
+ glClear( GL_COLOR_BUFFER_BIT );
+ glViewport( 0,0, vg.window_x, vg.window_y );
+
+ _vg_render_log();
+
+ return 0;
+}
+
VG_STATIC void _vg_gameloop(void)
{
//vg.time_fixed_accumulator = 0.75f * (1.0f/60.0f);
vg_profile_begin( &vg_prof_swap );
SDL_GL_SwapWindow( vg.window );
- _vg_run_synced();
vg_profile_end( &vg_prof_swap );
- vg.time_delta = vg.time_frame_delta * vg.time_rate;
- vg.time += vg.time_delta;
-
- vg_run_async_checked();
- _vg_process_events();
+ enum engine_status status = _vg_engine_status();
- if( vg.window_should_close )
- break;
-
- if( vg.is_loaded ){
- if( !post_start ){
- vg_start();
- post_start = 1;
- }
+ if( status == k_engine_status_crashed ){
+ if( _vg_crashscreen() )
+ break;
}
else{
- _vg_loader_render();
- }
+ vg.time_delta = vg.time_frame_delta * vg.time_rate;
+ vg.time += vg.time_delta;
- _vg_gameloop_update();
- _vg_gameloop_render();
+ vg_run_async_checked();
+ _vg_process_events();
+
+ if( vg.window_should_close )
+ break;
+
+ if( status == k_engine_status_running ){
+ _vg_gameloop_update();
+ _vg_gameloop_render();
+ }
+ else{
+ _vg_loader_render();
+ }
+ }
vg.time_frame_delta = 0.0;
vg.time_spinning = 0;
#endif
}
+VG_STATIC void _vg_terminate(void)
+{
+ /* Shutdown */
+ _vg_console_write_persistent();
+
+ SDL_AtomicLock( &vg.sl_status );
+ vg.engine_status = k_engine_status_none;
+ SDL_AtomicUnlock( &vg.sl_status );
+
+ _vg_loader_free();
+
+ vg_success( "If you see this it means everything went.. \"well\".....\n" );
+
+ SDL_GL_DeleteContext( vg.gl_context );
+ SDL_Quit();
+ exit(0);
+}
+
VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
{
_vg_process_launch_opts_internal( argc, argv );
_vg_ui_init();
_vg_loader_init();
- vg.engine_status = k_engine_status_running;
+ vg.engine_status = k_engine_status_load_internal;
_vg_opengl_sync_init();
vg_loader_start( _vg_load_full );
_vg_gameloop();
-
- /* Shutdown */
- _vg_console_write_persistent();
-
- SDL_AtomicLock( &vg.sl_context );
- vg.engine_status = k_engine_status_none;
- SDL_AtomicUnlock( &vg.sl_context );
-
- _vg_loader_free();
-
- vg_success( "If you see this it means everything went.. \"well\".....\n" );
-
- SDL_GL_DeleteContext( vg.gl_context );
- SDL_Quit();
+ _vg_terminate();
}
-/*
- * Immediately transfer away from calling thread into a safe loop, signal for
- * others to shutdown, then free everything once the user closes the window.
- *
- * FIXME(bug): glfwWindowShouldClose() never returns 1 in windows via wine, ONLY
- * when calling the program from outside its normal directory.
- */
-VG_STATIC void vg_fatal_exit_loop( const char *error )
+VG_STATIC void vg_fatal_error( const char *fmt, ... )
{
- /*
- * https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
- * thanks gnu <3
- *
- * TODO: this on windows?
- */
+ va_list args;
+ va_start( args, fmt );
+ _vg_log_write( stderr, KRED " fatal" KWHT "| " KRED, fmt, args );
+ va_end( args );
vg_print_backtrace();
- vg_error( "Fatal error: %s\n", error );
- SDL_AtomicLock( &vg.sl_context );
- if( vg.engine_status == k_engine_status_none ){
- SDL_AtomicUnlock( &vg.sl_context );
+ SDL_AtomicLock( &vg.sl_status );
+ vg.engine_status = k_engine_status_crashed;
+ SDL_AtomicUnlock( &vg.sl_status );
- /* TODO: Correct shutdown before other systems */
- exit(0);
+ if( vg_thread_purpose() == k_thread_purpose_loader ){
+ longjmp( vg.env_loader_exit, 1 );
}
else{
- SDL_AtomicUnlock( &vg.sl_context );
-
- vg_acquire_thread_sync();
-
- SDL_AtomicLock( &vg.sl_context );
- vg.engine_status = k_engine_status_crashed;
- vg.str_const_engine_err = error;
- SDL_AtomicUnlock( &vg.sl_context );
-
- /* Notify other thread for curtosey */
- if( vg_thread_purpose() == k_thread_purpose_main ){
- SDL_AtomicLock( &vg.sl_context );
-
- if( vg.exec_context != 0 )
- SDL_SemPost( vg.sem_allow_exec );
-
- SDL_AtomicUnlock( &vg.sl_context );
- }
-
- vg_audio_free();
-
- while(1){
- _vg_process_events();
- if( vg.window_should_close )
- break;
-
- if( vg_getkey( SDLK_ESCAPE ) )
- break;
-
- glBindFramebuffer( GL_FRAMEBUFFER, 0 );
- glEnable(GL_BLEND);
- glDisable(GL_DEPTH_TEST);
- glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
- glBlendEquation(GL_FUNC_ADD);
-
- glClearColor( 0.15f + sinf(vg.time)*0.1f, 0.0f, 0.0f,1.0f );
- glClear( GL_COLOR_BUFFER_BIT );
- glViewport( 0,0, vg.window_x, vg.window_y );
-
- _vg_render_log();
- SDL_GL_SwapWindow( vg.window );
- }
-
- /* Can now shutdown and EXIT */
- _vg_loader_free();
- SDL_GL_DeleteContext( vg.gl_context );
- SDL_Quit();
- exit(0);
+ vg_error( "There is no jump to the error runner thing yet! bai bai\n" );
+ _vg_terminate();
}
}
#else /* VG_GAME */
-VG_STATIC void vg_fatal_exit_loop( const char *error )
+#include "vg_log.h"
+VG_STATIC void vg_fatal_error( const char *fmt, ... )
{
- vg_error( "Fatal error: %s\n", error );
+ va_list args;
+ va_start( args, fmt );
+ _vg_log_write( stderr, KRED " fatal" KWHT "| " KRED, fmt, args );
+ va_end( args );
exit(0);
}
#ifndef VG_ASYNC_H
#define VG_ASYNC_H
+#define VG_GAME
#include "vg/vg.h"
typedef struct vg_async_item vg_async_item;
}
static vg_async;
+VG_STATIC enum vg_thread_purpose vg_thread_purpose(void);
+VG_STATIC enum engine_status _vg_engine_status(void);
+
/*
* Allocate an asynchronous call with a bit of memory
*/
VG_STATIC vg_async_item *vg_async_alloc( u32 size )
{
+ /* ditch out here if engine crashed. this serves as the 'quit checking' */
+
+ if( _vg_engine_status() == k_engine_status_crashed ){
+ assert( vg_thread_purpose() == k_thread_purpose_loader );
+ longjmp( vg.env_loader_exit, 1 );
+ }
+
SDL_AtomicLock( &vg_async.sl_index );
u32 total_allocation = vg_align8(size) + vg_align8(sizeof(vg_async_item)),
vg_error( "Requested: %umb. Buffer size: %umb\n",
(total_allocation/1024)/1024,
(capacity/1024)/1024 );
- vg_fatal_exit_loop( "async alloc invalid size\n" );
+
+ vg_fatal_error( "async alloc invalid size\n" );
}
if( total_allocation > remaining ){
vg_async_item *entry = block;
entry->next = NULL;
- entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item));
+
+ if( size ) entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item));
+ else entry->payload = NULL;
+
entry->size = size;
entry->fn_runner = NULL;
return entry;
}
+VG_STATIC void vg_async_stall(void)
+{
+ vg_info( "async_stall: %d\n", SDL_SemValue( vg_async.sem_wait_for_flush ) );
+ SDL_SemWait( vg_async.sem_wait_for_flush );
+}
+
/*
* Mark the call as being filled and ready to go
*/
VG_STATIC void vg_async_dispatch( vg_async_item *item,
void (*runner)( void *payload, u32 size ) )
{
+ if( SDL_SemValue(vg_async.sem_wait_for_flush) )
+ SDL_SemWait(vg_async.sem_wait_for_flush);
+
SDL_AtomicLock( &vg_async.sl_index );
item->fn_runner = runner;
SDL_AtomicUnlock( &vg_async.sl_index );
}
}
else{
- break;
+ SDL_AtomicUnlock( &vg_async.sl_index );
+ return;
}
/* TODO: if exceed max frametime.... */
}
+ if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){
+ SDL_SemPost( vg_async.sem_wait_for_flush );
+ }
+
SDL_AtomicUnlock( &vg_async.sl_index );
}
{
vg_async.sem_wait_for_flush = SDL_CreateSemaphore(0);
vg_async.buffer = vg_create_linear_allocator( NULL, 50*1024*1024,
- VG_MEMORY_REALTIME );
+ VG_MEMORY_SYSTEM );
}
#endif /* VG_ASYNC_H */
SDL_PauseAudioDevice( vg_audio.sdl_output_device, 0 );
}
else{
- vg_fatal_exit_loop(
+ vg_fatal_error(
"SDL_OpenAudioDevice failed. Your default audio device must support:\n"
" Frequency: 44100 hz\n"
" Buffer size: 512\n"
}
}
- if( !vg_validf( framevol_l ) ) vg_fatal_exit_loop( "NaN left channel" );
- if( !vg_validf( framevol_r ) ) vg_fatal_exit_loop( "NaN right channel" );
+ if( !vg_validf( framevol_l ) ) vg_fatal_error( "NaN left channel" );
+ if( !vg_validf( framevol_r ) ) vg_fatal_error( "NaN right channel" );
if( !vg_validf( frame_samplerate ) )
- vg_fatal_exit_loop( "NaN sample rate" );
+ vg_fatal_error( "NaN sample rate" );
}
u32 buffer_length = AUDIO_MIX_FRAME_SIZE;
if( format == k_audio_format_vorbis ){
if( !clip->path ){
- vg_fatal_exit_loop( "No path specified, embeded vorbis unsupported" );
+ vg_fatal_error( "No path specified, embeded vorbis unsupported" );
}
audio_lock();
audio_unlock();
if( !clip->data )
- vg_fatal_exit_loop( "Audio failed to load" );
+ vg_fatal_error( "Audio failed to load" );
float mb = (float)(clip->size) / (1024.0f*1024.0f);
vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
}
else if( format == k_audio_format_stereo ){
- vg_fatal_exit_loop( "Unsupported format (Stereo uncompressed)" );
+ vg_fatal_error( "Unsupported format (Stereo uncompressed)" );
}
else if( format == k_audio_format_bird ){
if( !clip->data ){
- vg_fatal_exit_loop( "No data, external birdsynth unsupported" );
+ vg_fatal_error( "No data, external birdsynth unsupported" );
}
u32 total_size = clip->size + sizeof(struct synth_bird);
total_size = vg_align8( total_size );
if( total_size > AUDIO_DECODE_SIZE )
- vg_fatal_exit_loop( "Bird coding too long\n" );
+ vg_fatal_error( "Bird coding too long\n" );
struct synth_bird *bird = vg_linear_alloc( lin_alloc, total_size );
memcpy( &bird->settings, clip->data, clip->size );
}
else{
if( !clip->path ){
- vg_fatal_exit_loop( "No path specified, embeded mono unsupported" );
+ vg_fatal_error( "No path specified, embeded mono unsupported" );
}
vg_linear_clear( vg_mem.scratch );
if( !decoder ){
vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
clip->path, err );
- vg_fatal_exit_loop( "Vorbis decode error" );
+ vg_fatal_error( "Vorbis decode error" );
}
/* only mono is supported in uncompressed */
decoder, clip->data, length_samples );
if( read_samples != length_samples )
- vg_fatal_exit_loop( "Decode error" );
+ vg_fatal_error( "Decode error" );
float mb = (float)(data_size) / (1024.0f*1024.0f);
vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb,
return;
audio_unlock();
- vg_fatal_exit_loop( "Must load audio clip before playing! \n" );
+ vg_fatal_error( "Must load audio clip before playing! \n" );
}
/*
samples = vg_align4( samples );
if( vg_dsp.allocations + samples > (1024*1024)/4 )
- vg_fatal_exit_loop( "too much dsp" );
+ vg_fatal_error( "too much dsp" );
float *buf = &vg_dsp.buffer[ vg_dsp.allocations ];
vg_dsp.allocations += samples;
static struct dsp_lpf __echos_lpf[8];
static struct dsp_schroeder __diffusion_chain[8];
-static void vg_dsp_init( void )
+static void async_vg_dsp_alloc_texture( void *payload, u32 size )
{
- vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 );
- vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 );
-
- vg_acquire_thread_sync();
glGenTextures( 1, &vg_dsp.view_texture );
glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
GL_RGBA, GL_UNSIGNED_BYTE, vg_dsp.view_texture_buffer );
- vg_tex2d_nearest();
- vg_release_thread_sync();
-
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+}
- /* temporary global design */
+static void vg_dsp_init( void )
+{
+ vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 );
+ vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 );
+ vg_async_item *call = vg_async_alloc(0);
+ vg_async_dispatch( call, async_vg_dsp_alloc_texture );
+ /* temporary global design */
dsp_init_lpf( &__lpf_mud_free, 125.0f );
float sizes[] =
u32 flags )
{
if( vg_console.var_count > vg_list_size(vg_console.vars) )
- vg_fatal_exit_loop( "Too many vars registered" );
+ vg_fatal_error( "Too many vars registered" );
vg_var *var = &vg_console.vars[ vg_console.var_count ++ ];
var->name = alias;
void (*poll_suggest)(int argc, const char *argv[]) )
{
if( vg_console.function_count > vg_list_size(vg_console.functions) )
- vg_fatal_exit_loop( "Too many functions registered" );
+ vg_fatal_error( "Too many functions registered" );
vg_cmd *cmd = &vg_console.functions[ vg_console.function_count ++ ];
int _test_break( int argc, const char *argv[] )
{
- vg_fatal_exit_loop( "Test crash from main, after loading (console)" );
+ vg_fatal_error( "Test crash from main, after loading (console)" );
return 0;
}
return 0;
}
-VG_STATIC void vg_input_init(void)
+VG_STATIC void async_vg_input_init( void *payload, u32 size )
{
- vg_acquire_thread_sync();
-
vg_console_reg_cmd( "bind", vg_rebind_input_cmd, vg_rebind_input_cmd_poll );
VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
+}
- vg_release_thread_sync();
+VG_STATIC void vg_input_init(void)
+{
+ vg_async_item *call = vg_async_alloc(0);
+ vg_async_dispatch( call, async_vg_input_init );
}
VG_STATIC void vg_input_free(void)
else{
if( ferror( f ) ){
fclose(f);
- vg_fatal_exit_loop( "read error" );
+ vg_fatal_error( "read error" );
}
else{
fclose(f);
- vg_fatal_exit_loop( "unknown error codition" );
+ vg_fatal_error( "unknown error codition" );
}
}
}
}
static vg_lines;
+VG_STATIC void async_vg_lines_init( void *payload, u32 payload_size )
+{
+ glGenVertexArrays( 1, &vg_lines.vao );
+ glGenBuffers( 1, &vg_lines.vbo );
+ glBindVertexArray( vg_lines.vao );
+ glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo );
+
+ u32 size = 50000 * sizeof( struct vg_lines_vert );
+
+ vg_lines.vertex_buffer =
+ vg_create_linear_allocator(vg_mem.rtmemory, size, VG_MEMORY_REALTIME);
+
+ glBufferData( GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW );
+ glBindVertexArray( vg_lines.vao );
+ VG_CHECK_GL_ERR();
+
+ /* Pointers */
+ glVertexAttribPointer(
+ 0,
+ 3,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof( struct vg_lines_vert ),
+ (void *)0
+ );
+ glEnableVertexAttribArray( 0 );
+
+ glVertexAttribPointer(
+ 1,
+ 4,
+ GL_UNSIGNED_BYTE,
+ GL_TRUE,
+ sizeof( struct vg_lines_vert ),
+ (void*)(offsetof( struct vg_lines_vert, colour ))
+ );
+ glEnableVertexAttribArray( 1 );
+
+ VG_CHECK_GL_ERR();
+ vg_success( "done\n" );
+
+ vg_lines.allow_input = 1;
+}
+
VG_STATIC void vg_lines_init(void)
{
- vg_info( "vg_lines_init\n" );
+ vg_async_item *call = vg_async_alloc(0);
+ vg_async_dispatch( call, async_vg_lines_init );
vg_console_reg_var( "vg_lines", &vg_lines.draw, k_var_dtype_i32,
VG_VAR_CHEAT );
vg_shader_register( &_shader_lines );
- vg_acquire_thread_sync();
- {
- glGenVertexArrays( 1, &vg_lines.vao );
- glGenBuffers( 1, &vg_lines.vbo );
- glBindVertexArray( vg_lines.vao );
- glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo );
-
- u32 size = 50000 * sizeof( struct vg_lines_vert );
-
- vg_lines.vertex_buffer =
- vg_create_linear_allocator(vg_mem.rtmemory, size, VG_MEMORY_REALTIME);
-
- glBufferData( GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW );
- glBindVertexArray( vg_lines.vao );
- VG_CHECK_GL_ERR();
-
- /* Pointers */
- glVertexAttribPointer(
- 0,
- 3,
- GL_FLOAT,
- GL_FALSE,
- sizeof( struct vg_lines_vert ),
- (void *)0
- );
- glEnableVertexAttribArray( 0 );
-
- glVertexAttribPointer(
- 1,
- 4,
- GL_UNSIGNED_BYTE,
- GL_TRUE,
- sizeof( struct vg_lines_vert ),
- (void*)(offsetof( struct vg_lines_vert, colour ))
- );
- glEnableVertexAttribArray( 1 );
-
- VG_CHECK_GL_ERR();
- vg_success( "done\n" );
- }
-
- vg_release_thread_sync();
- vg_lines.allow_input = 1;
}
VG_STATIC void vg_lines_drawall( void )
VG_CHECK_GL_ERR();
if( !vg_shader_compile( &_shader_loader ) )
- vg_fatal_exit_loop( "failed to compile shader" );
+ vg_fatal_error( "failed to compile shader" );
}
VG_STATIC void _vg_loader_free(void)
VG_STATIC int _vg_loader_thread(void *pfn)
{
- SDL_AtomicLock( &vg.sl_context );
- vg.thread_id_loader = SDL_GetThreadID(NULL);
- VG_SYNC_LOG( "[%d] Loader thread begins\n" );
- SDL_AtomicUnlock( &vg.sl_context );
+ if( setjmp( vg.env_loader_exit ) )
+ return 0;
/* Run client loader */
+ vg_info( "Starting client loader thread @%p\n", pfn );
void (*call_func)(void) = pfn;
call_func();
SDL_SemPost( vg.sem_loader );
vg.thread_id_loader = 0;
- vg_acquire_thread_sync();
- vg.is_loaded = 1;
- vg_release_thread_sync();
-
return 0;
}
VG_STATIC void vg_loader_start( void(*pfn)(void) )
{
- vg.is_loaded = 0;
SDL_SemWait( vg.sem_loader );
SDL_CreateThread( _vg_loader_thread, "Loader thread", pfn );
}
if( fn_load )
fn_load();
- if( fn_free )
- {
+ if( fn_free ){
struct loader_free_step step;
step.fn_free = fn_free;
if( vg_loader.step_count == vg_list_size(vg_loader.step_buffer) )
- vg_fatal_exit_loop( "Too many free steps" );
+ vg_fatal_error( "Too many free steps" );
vg_loader.step_buffer[ vg_loader.step_count ++ ] = step;
}
- _vg_ensure_engine_running();
+ /* TODO: There was a quit checker here, re-add this? */
}
#endif /* VG_LOADER_H */
};
#pragma pack(pop)
-VG_STATIC void vg_fatal_exit_loop( const char *error );
+VG_STATIC void vg_fatal_error( const char *fmt, ... );
VG_STATIC void vg_error(const char *fmt, ...);
VG_STATIC void vg_info(const char *fmt, ...);
#else
if( ((u64)buffer) % 8 ){
#endif
- vg_error( "buffer: %p\n", buffer );
- vg_fatal_exit_loop( "unaligned buffer" );
+ vg_fatal_error( "unaligned buffer (%p)", buffer );
}
vg_linear_allocator *alloc = vg_linear_header( buffer );
if( (alloc->cur + size) > alloc->size ){
- vg_error( "%u + %u > %u\n", alloc->cur, size, alloc->size );
- vg_fatal_exit_loop( "linear allocator overflow" );
+ vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
+ alloc->cur, size, alloc->size );
}
if( alloc->flags & VG_MEMORY_SYSTEM )
if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
- vg_fatal_exit_loop( "Max linear allocations reached" );
+ vg_fatal_error( "Max linear allocations reached" );
void *data;
u8 *bytes = data;
for( u32 i=0; i<size; i++ ){
- bytes[i] = 0xae;
+ bytes[i] = 0xfe;
}
alloc->allocation_count ++;
#else
if( ((u64)data) % 8 ){
#endif
- vg_fatal_exit_loop( "unaligned" );
+ vg_fatal_error( "unaligned" );
}
return data;
}
if( alloc->last_alloc != data )
- vg_fatal_exit_loop( "This block has been fixed!" );
+ vg_fatal_error( "This block has been fixed!" );
if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
- vg_fatal_exit_loop( "Cannot resize, overflow" );
+ vg_fatal_error( "Cannot resize, overflow" );
alloc->cur -= alloc->last_alloc_size;
alloc->cur += newsize;
if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
data = realloc( data, newsize );
if( !data )
- vg_fatal_exit_loop( "realloc failed" );
+ vg_fatal_error( "realloc failed" );
alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
alloc->last_alloc = data;
vg_linear_allocator *alloc = vg_linear_header( buffer );
if( alloc->last_alloc != data )
- vg_fatal_exit_loop( "This block has been fixed!" );
+ vg_fatal_error( "This block has been fixed!" );
if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
if( meta->type == k_allocation_type_linear )
- vg_fatal_exit_loop( "Cannot free a linear allocator in this conext" );
+ vg_fatal_error( "Cannot free a linear allocator in this conext" );
free( data );
}
vg_linear_allocator *alloc = vg_linear_header( buffer );
if( alloc->last_alloc != data )
- vg_fatal_exit_loop( "This block has been fixed!" );
+ vg_fatal_error( "This block has been fixed!" );
u32 new_size = alloc->last_alloc_size + extra;
return vg_linear_resize( buffer, data, new_size );
vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
if( alloc->cur + block_size > alloc->size )
- vg_fatal_exit_loop( "Out of memory" );
+ vg_fatal_error( "Out of memory" );
if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
- vg_fatal_exit_loop( "Max allocations in linear allocator" );
+ vg_fatal_error( "Max allocations in linear allocator" );
if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
- vg_fatal_exit_loop( "Cannot declare realtime allocator inside systems"
+ vg_fatal_error( "Cannot declare realtime allocator inside systems"
" allocator" );
if( vg_mem.use_libc_malloc ){
vg_shader *shader = vg_shaders.shaders[i];
if( !vg_shader_compile( shader ) )
- vg_fatal_exit_loop( "Failed to compile shader" );
+ vg_fatal_error( "Failed to compile shader" );
}
}
VG_STATIC void vg_shader_register( struct vg_shader *shader )
{
if( vg_shaders.count == vg_list_size(vg_shaders.shaders) )
- vg_fatal_exit_loop( "Too many shaders" );
+ vg_fatal_error( "Too many shaders" );
shader->compiled = 0;
shader->id = 0; /* TODO: make this an error shader */
#include "vg/vg.h"
#include "vg/vg_log.h"
-#define VG_TEXTURE_NO_MIP 0x1
-#define VG_TEXTURE_REPEAT 0x2
-#define VG_TEXTURE_CLAMP 0x4
-#define VG_TEXTURE_NEAREST 0x8
-#define VG_TEXTURE_ALLOCATED_INTERNAL 0x10
-
-VG_STATIC void *vg_qoi_malloc( size_t size )
-{
- return vg_linear_alloc( vg_mem.scratch, size );
-}
-
-VG_STATIC void vg_qoi_free( void *ptr )
-{
-
-}
-
-#define QOI_IMPLEMENTATION
-#define QOI_NO_STDIO
-#define QOI_MALLOC(sz) vg_qoi_malloc( sz )
-#define QOI_FREE(p) vg_qoi_free( p )
-
-#include "submodules/qoi/qoi.h"
-
-struct vg_tex2d
-{
- const char *path;
- u32 flags;
- GLuint name;
-};
-
struct vg_sprite
{
v4f uv_xywh;
};
-VG_STATIC void vg_tex2d_bind( vg_tex2d *tex, u32 id )
-{
- if( !(tex->flags & VG_TEXTURE_ALLOCATED_INTERNAL) )
- {
- vg_error( "Tried to use '%s' while unloaded!\n", tex->path );
- return;
- }
+#define VG_TEX2D_LINEAR 0x1
+#define VG_TEX2D_NEAREST 0x2
+#define VG_TEX2D_REPEAT 0x4
+#define VG_TEX2D_CLAMP 0x8
+#define VG_TEX2D_NOMIP 0x10
- glActiveTexture( GL_TEXTURE0 + id );
- glBindTexture( GL_TEXTURE_2D, tex->name );
-}
+static u8 const_vg_tex2d_err[] =
+{
+ 0xff, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0xff
+};
-static inline void vg_tex2d_mipmap(void)
-{
- glGenerateMipmap( GL_TEXTURE_2D );
+#define QOI_SRGB 0
+#define QOI_LINEAR 1
+
+typedef struct {
+ unsigned int width;
+ unsigned int height;
+ unsigned char channels;
+ unsigned char colorspace;
+} qoi_desc;
+
+#ifndef QOI_ZEROARR
+ #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
+#endif
+
+#define QOI_OP_INDEX 0x00 /* 00xxxxxx */
+#define QOI_OP_DIFF 0x40 /* 01xxxxxx */
+#define QOI_OP_LUMA 0x80 /* 10xxxxxx */
+#define QOI_OP_RUN 0xc0 /* 11xxxxxx */
+#define QOI_OP_RGB 0xfe /* 11111110 */
+#define QOI_OP_RGBA 0xff /* 11111111 */
+
+#define QOI_MASK_2 0xc0 /* 11000000 */
+
+#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
+#define QOI_MAGIC \
+ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
+ ((unsigned int)'i') << 8 | ((unsigned int)'f'))
+#define QOI_HEADER_SIZE 14
+
+/* 2GB is the max file size that this implementation can safely handle. We guard
+against anything larger than that, assuming the worst case with 5 bytes per
+pixel, rounded down to a nice clean value. 400 million pixels ought to be
+enough for anybody. */
+#define QOI_PIXELS_MAX ((unsigned int)400000000)
+
+typedef union {
+ struct { unsigned char r, g, b, a; } rgba;
+ unsigned int v;
+} qoi_rgba_t;
+
+static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
+static u32 qoi_read_32( const u8 *bytes, int *p ) {
+ u32 a = bytes[(*p)++];
+ u32 b = bytes[(*p)++];
+ u32 c = bytes[(*p)++];
+ u32 d = bytes[(*p)++];
+ return a << 24 | b << 16 | c << 8 | d;
}
-static inline void vg_tex2d_linear(void)
-{
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-}
+struct texture_load_info{
+ GLuint *dest;
+ u32 width, height, flags;
+ u8 *rgba;
+};
-static inline void vg_tex2d_nearest(void)
+VG_STATIC void async_vg_tex2d_upload( void *payload, u32 size )
{
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
-}
+ struct texture_load_info *info = payload;
-static inline void vg_tex2d_linear_mipmap(void)
-{
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- GL_LINEAR_MIPMAP_LINEAR );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-}
+ glGenTextures( 1, info->dest );
+ glBindTexture( GL_TEXTURE_2D, *info->dest );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info->width, info->height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba );
-static inline void vg_tex2d_repeat(void)
-{
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
-}
+ if( !(info->flags & VG_TEX2D_NOMIP) ){
+ glGenerateMipmap( GL_TEXTURE_2D );
+ }
-static inline void vg_tex2d_clamp(void)
-{
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-}
+ if( info->flags & VG_TEX2D_LINEAR ){
+ if( info->flags & VG_TEX2D_NOMIP ){
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ }
+ else{
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR );
+ }
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ }
-VG_STATIC GLuint vg_tex2d_new(void)
-{
- GLuint texture_name;
- glGenTextures( 1, &texture_name );
- glBindTexture( GL_TEXTURE_2D, texture_name );
+ if( info->flags & VG_TEX2D_NEAREST ){
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ }
- return texture_name;
-}
+ if( info->flags & VG_TEX2D_CLAMP ){
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ }
-VG_STATIC void vg_tex2d_set_error(void)
-{
- u32 tex_err[4] =
- {
- 0xffff00ff,
- 0xff000000,
- 0xff000000,
- 0xffff00ff
- };
-
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 2, 2,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_err );
+ if( info->flags & VG_TEX2D_REPEAT ){
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+ }
}
-VG_STATIC void vg_tex2d_qoi( void *mem, u32 size, const char *name )
+VG_STATIC void vg_tex2d_replace_with_error( GLuint *dest )
{
- qoi_desc info;
- u8 *tex_buffer = qoi_decode( mem, size, &info, 4 );
+ u32 hdr_size = vg_align8(sizeof(struct texture_load_info));
- if( tex_buffer )
- {
- vg_info( "Texture decoded: [%u %u] %s\n",
- info.width, info.height, name );
-
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_buffer );
+ vg_async_item *call = vg_async_alloc( hdr_size );
+ struct texture_load_info *info = call->payload;
+
+ info->dest = dest;
+ info->flags = VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP;
+ info->width = 2;
+ info->height = 2;
+ info->rgba = const_vg_tex2d_err;
- QOI_FREE(tex_buffer);
- }
- else
- {
- vg_error( "File size: %u\n", size );
- vg_tex2d_set_error();
- }
+ vg_async_dispatch( call, async_vg_tex2d_upload );
}
-/*
- * TODO: This blocks while we read from file
- */
-VG_STATIC GLuint vg_tex2d_rgba( const char *path )
+VG_STATIC
+void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size,
+ u32 flags, GLuint *dest )
{
- GLuint texture_name = vg_tex2d_new();
+ u32 header_magic;
+ qoi_rgba_t index[64];
+ qoi_rgba_t px;
+ int px_len, chunks_len, px_pos;
+ int p = 0, run = 0;
+
+ u32 channels = 4; /* TODO */
+
+ qoi_desc desc;
+
+ if (
+ bytes == NULL ||
+ (channels != 0 && channels != 3 && channels != 4) ||
+ size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
+ ) {
+ vg_error( "Error while decoding qoi file: illegal parameters\n" );
+ vg_tex2d_replace_with_error( dest );
+ return;
+ }
- vg_linear_clear( vg_mem.scratch );
- u32 size;
- void *file = vg_file_read( vg_mem.scratch, path, &size );
+ header_magic = qoi_read_32(bytes, &p);
+ desc.width = qoi_read_32(bytes, &p);
+ desc.height = qoi_read_32(bytes, &p);
+ desc.channels = bytes[p++];
+ desc.colorspace = bytes[p++];
+
+ if (
+ desc.width == 0 || desc.height == 0 ||
+ desc.channels < 3 || desc.channels > 4 ||
+ desc.colorspace > 1 ||
+ header_magic != QOI_MAGIC ||
+ desc.height >= QOI_PIXELS_MAX / desc.width
+ ) {
+ vg_error( "Error while decoding qoi file: invalid file\n" );
+ vg_tex2d_replace_with_error( dest );
+ return;
+ }
- if( file )
- {
- vg_tex2d_qoi( file, size, path );
- }
- else
- {
- vg_error( "Loading texture failed (%s)\n", path );
- vg_tex2d_set_error();
- }
+ if (channels == 0) {
+ channels = desc.channels;
+ }
- return texture_name;
-}
+ px_len = desc.width * desc.height * channels;
-VG_STATIC void vg_tex2d_init( vg_tex2d *textures[], int num )
-{
- for( int i=0; i<num; i ++ )
- {
- vg_tex2d *tex = textures[i];
- tex->name = vg_tex2d_rgba( tex->path );
- if( !(tex->flags & VG_TEXTURE_NO_MIP) )
- vg_tex2d_mipmap();
-
- if( tex->flags & VG_TEXTURE_NEAREST )
- {
- if( tex->flags & VG_TEXTURE_NO_MIP )
- vg_error( "Invalid texture settings\n" );
- else
- vg_tex2d_nearest();
+ /* allocate async call
+ * --------------------------
+ */
+ u32 hdr_size = vg_align8(sizeof(struct texture_load_info)),
+ tex_size = vg_align8(px_len);
+
+ vg_async_item *call = vg_async_alloc( hdr_size + tex_size );
+ struct texture_load_info *info = call->payload;
+
+ info->dest = dest;
+ info->flags = flags;
+ info->width = desc.width;
+ info->height = desc.height;
+ info->rgba = ((u8*)call->payload) + hdr_size;
+
+ /*
+ * Decode
+ * ----------------------------
+ */
+
+ u8 *pixels = info->rgba;
+
+ QOI_ZEROARR(index);
+ px.rgba.r = 0;
+ px.rgba.g = 0;
+ px.rgba.b = 0;
+ px.rgba.a = 255;
+
+ chunks_len = size - (int)sizeof(qoi_padding);
+ for (px_pos = 0; px_pos < px_len; px_pos += channels) {
+ if (run > 0) {
+ run--;
}
- else
- {
- if( tex->flags & VG_TEXTURE_NO_MIP )
- vg_tex2d_linear();
- else
- vg_tex2d_linear_mipmap();
+ else if (p < chunks_len) {
+ int b1 = bytes[p++];
+
+ if (b1 == QOI_OP_RGB) {
+ px.rgba.r = bytes[p++];
+ px.rgba.g = bytes[p++];
+ px.rgba.b = bytes[p++];
+ }
+ else if (b1 == QOI_OP_RGBA) {
+ px.rgba.r = bytes[p++];
+ px.rgba.g = bytes[p++];
+ px.rgba.b = bytes[p++];
+ px.rgba.a = bytes[p++];
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
+ px = index[b1];
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
+ px.rgba.r += ((b1 >> 4) & 0x03) - 2;
+ px.rgba.g += ((b1 >> 2) & 0x03) - 2;
+ px.rgba.b += ( b1 & 0x03) - 2;
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
+ int b2 = bytes[p++];
+ int vg = (b1 & 0x3f) - 32;
+ px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
+ px.rgba.g += vg;
+ px.rgba.b += vg - 8 + (b2 & 0x0f);
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
+ run = (b1 & 0x3f);
+ }
+
+ index[QOI_COLOR_HASH(px) % 64] = px;
}
-
- if( tex->flags & VG_TEXTURE_CLAMP )
- vg_tex2d_clamp();
- else
- vg_tex2d_repeat();
- tex->flags |= VG_TEXTURE_ALLOCATED_INTERNAL;
+ pixels[px_pos + 0] = px.rgba.r;
+ pixels[px_pos + 1] = px.rgba.g;
+ pixels[px_pos + 2] = px.rgba.b;
+
+ if (channels == 4) {
+ pixels[px_pos + 3] = px.rgba.a;
+ }
}
+
+ /*
+ * Complete the call
+ * --------------------------
+ */
+
+ vg_async_dispatch( call, async_vg_tex2d_upload );
}
-VG_STATIC void vg_tex2d_free( vg_tex2d *textures[], int num )
+VG_STATIC
+void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest )
{
- for( int i = 0; i < num; i ++ )
- {
- glDeleteTextures( 1, &textures[i]->name );
- }
+ vg_linear_clear( vg_mem.scratch );
+
+ u32 size;
+ const void *data = vg_file_read( vg_mem.scratch, path, &size );
+ vg_tex2d_load_qoi_async( data, size, flags, dest );
}
#endif /* VG_TEX_H */
{
if( !vg_shader_compile( &_shader_ui ) ||
!vg_shader_compile( &_shader_ui_image ) )
- vg_fatal_exit_loop( "Failed to compile ui shader" );
+ vg_fatal_error( "Failed to compile ui shader" );
/*
* Vertex buffer
GL_RED, GL_UNSIGNED_BYTE, image );
VG_CHECK_GL_ERR();
- vg_tex2d_clamp();
- vg_tex2d_nearest();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
static struct ui_vert *ui_fill_rect_uv( ui_rect rect, u32 colour, ui_px uv[4] );