From: hgn Date: Mon, 11 Aug 2025 21:15:37 +0000 (+0000) Subject: commpleted wrecking vg4 X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=99714f9842deb9b418dc9f498ff45f3738bd8713;p=vg.git commpleted wrecking vg4 --- diff --git a/build-compat/workaround-27653.c b/build-compat/workaround-27653.c new file mode 100644 index 0000000..e625b2c --- /dev/null +++ b/build-compat/workaround-27653.c @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright (C) 2021 Mathieu Desnoyers + * + * Workaround for malloc interceptors hang caused by glibc i18n lookup deadlock. + * Bypass i18n lookup when nested in glibc's intl code. + * + * https://sourceware.org/bugzilla/show_bug.cgi?id=27653 + * + * Build with: + * + * gcc -shared -fPIC -o workaround-27653.so workaround-27653.c + * + * Then use with (e.g.): + * + * LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/11/libasan.so:./workaround-27653.so free + */ + +#define _GNU_SOURCE +#include +#include +#include + +static __thread int textdomain_nesting __attribute__((tls_model("initial-exec"))); + +static char *(*textdomain_fct)(const char *domainname); +static char *(*bindtextdomain_fct)(const char *domainname, const char *dirname); +static char *(*bind_textdomain_codeset_fct)(const char *domainname, const char *codeset); +static char *(*__dcgettext_fct)(const char *msgid); + +char *textdomain(const char *domainname) +{ + char *str; + + textdomain_nesting++; + if (!textdomain_fct) + textdomain_fct = dlsym(RTLD_NEXT, "textdomain"); + if (!textdomain_fct) + abort(); + str = textdomain_fct(domainname); + textdomain_nesting--; + return str; +} + +char *bindtextdomain(const char *domainname, const char *dirname) +{ + printf( "FUCKYOU\n""FUCKYOU\n""FUCKYOU\n""FUCKYOU\n""FUCKYOU\n""FUCKYOU\n" ); + char *str; + + textdomain_nesting++; + if (!bindtextdomain_fct) + bindtextdomain_fct = dlsym(RTLD_NEXT, "bindtextdomain"); + if (!bindtextdomain_fct) + abort(); + str = bindtextdomain_fct(domainname, dirname); + textdomain_nesting--; + return str; +} + +char *bind_textdomain_codeset(const char *domainname, const char *codeset) +{ + char *str; + + textdomain_nesting++; + if (!bind_textdomain_codeset_fct) + bind_textdomain_codeset_fct = dlsym(RTLD_NEXT, "bind_textdomain_codeset"); + if (!bind_textdomain_codeset_fct) + abort(); + str = bind_textdomain_codeset_fct(domainname, codeset); + textdomain_nesting--; + return str; +} + +char *__dcgettext(const char *msgid) +{ + if (textdomain_nesting) + return (char *) msgid; + + if (!__dcgettext_fct) + __dcgettext_fct = dlsym(RTLD_NEXT, "__dcgettext"); + if (!__dcgettext_fct) + abort(); + return __dcgettext_fct(msgid); +} diff --git a/shaders/debug_lines.fs b/shaders/debug_lines.fs new file mode 100644 index 0000000..1fcc7ad --- /dev/null +++ b/shaders/debug_lines.fs @@ -0,0 +1,8 @@ +out vec4 FragColor; + +in vec4 s_colour; + +void main() +{ + FragColor = s_colour; +} diff --git a/shaders/debug_lines.vs b/shaders/debug_lines.vs new file mode 100644 index 0000000..2fa61e0 --- /dev/null +++ b/shaders/debug_lines.vs @@ -0,0 +1,12 @@ +uniform mat4 uPv; +layout (location=0) in vec3 a_co; +layout (location=1) in vec4 a_colour; + +out vec4 s_colour; + +void main() +{ + vec4 vert_pos = uPv * vec4( a_co, 1.0 ); + s_colour = a_colour; + gl_Position = vert_pos; +} diff --git a/shaders/loader.fs b/shaders/loader.fs new file mode 100644 index 0000000..dc265dc --- /dev/null +++ b/shaders/loader.fs @@ -0,0 +1,34 @@ +out vec4 FragColor; +uniform float uTime; +uniform float uRatio; +uniform float uOpacity; +in vec2 aUv; + +float eval_zero( vec2 uv ) +{ + vec4 vsines = sin( (uTime+uv.y*80.0) * vec4(1.1,2.0234,3.73,2.444) ); + float gradient = min( uv.y, 0.0 ); + float offset = vsines.x*vsines.y*vsines.z*vsines.w*gradient; + + vec2 vpos = uv + vec2( offset, 0.0 ); + float dist = dot( vpos, vpos ); + + float fring = step(0.1*0.1,dist) * step(dist,0.15*0.15); + return max( 0.0, fring * 1.0+gradient*6.0 ); +} + +void main() +{ + vec3 col = 0.5+0.5*sin( uTime + aUv.xyx + vec3(0.0,2.0,4.0) ); + + vec2 uvx = aUv - vec2( 0.5 ); + uvx.x *= uRatio; + uvx.y *= 0.75; + + float zero = eval_zero( uvx ); + + float dither=fract(dot(vec2(171.0,231.0),gl_FragCoord.xy)/71.0)-0.5; + float fmt1 = step( 0.5, zero*zero + dither )*0.8+0.2; + + FragColor = vec4(vec3(fmt1),uOpacity); +} diff --git a/shaders/rigidbody_view.fs b/shaders/rigidbody_view.fs new file mode 100644 index 0000000..a47829b --- /dev/null +++ b/shaders/rigidbody_view.fs @@ -0,0 +1,48 @@ +out vec4 FragColor; +uniform vec4 uColour; + +in vec3 aNorm; +in vec3 aCo; +// The MIT License +// Copyright © 2017 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the Software), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. THE SOFTWARE IS +// PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// Info: https://iquilezles.org/articles/filterableprocedurals +// +// More filtered patterns: https://www.shadertoy.com/playlist/l3KXR1 + +vec3 tri( in vec3 x ) +{ + return 1.0-abs(2.0*fract(x/2.0)-1.0); +} + +float checkersTextureGrad( in vec3 p, in vec3 ddx, in vec3 ddy ) +{ + vec3 w = max(abs(ddx), abs(ddy)) + 0.0001; // filter kernel + vec3 i = (tri(p+0.5*w)-tri(p-0.5*w))/w; // analytical integral (box filter) + return 0.5 - 0.5*i.x*i.y*i.z; // xor pattern +} + +void main() +{ + vec3 uvw = aCo; + vec3 ddx_uvw = dFdx( uvw ); + vec3 ddy_uvw = dFdy( uvw ); + float diffuse = checkersTextureGrad( uvw, ddx_uvw, ddy_uvw )*0.5+0.4; + float light = dot( vec3(0.8017,0.5345,-0.2672), aNorm )*0.5 + 0.5; + FragColor = light * diffuse * uColour; +} diff --git a/shaders/rigidbody_view.vs b/shaders/rigidbody_view.vs new file mode 100644 index 0000000..08fc744 --- /dev/null +++ b/shaders/rigidbody_view.vs @@ -0,0 +1,20 @@ +uniform mat4 uPv; +uniform mat4x3 uMdl; +uniform mat4x3 uMdl1; +layout (location=0) in vec4 a_co; +layout (location=1) in vec3 a_norm; +out vec3 aNorm; +out vec3 aCo; + +void main() +{ + vec3 world_pos0 = uMdl * vec4( a_co.xyz, 1.0 ); + vec3 world_pos1 = uMdl1 * vec4( a_co.xyz, 1.0 ); + vec3 co = mix( world_pos0, world_pos1, a_co.w ); + vec4 vert_pos = uPv * vec4( co, 1.0 ); + + gl_Position = vert_pos; + vec3 l = vec3(length(uMdl[0]),length(uMdl[1]),length(uMdl[2])); + aNorm = (mat3(uMdl) * a_norm)/l; + aCo = a_co.xyz*l; +} diff --git a/shaders/ui.fs b/shaders/ui.fs new file mode 100644 index 0000000..bf8525d --- /dev/null +++ b/shaders/ui.fs @@ -0,0 +1,48 @@ +uniform sampler2D uTexGlyphs; +uniform sampler2D uTexBG; +uniform vec4 uColour; +uniform float uSpread; +out vec4 FragColor; + +in vec4 aTexCoords; +in vec4 aColour; + +vec2 rand_hash22( vec2 p ) +{ + vec3 p3 = fract(vec3(p.xyx) * 213.8976123); + p3 += dot(p3, p3.yzx+19.19); + return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); +} + +void main() +{ + vec4 diffuse = aColour; + + vec4 avg = vec4(0.0); + + if( aColour.a == 0.0 ) + { + avg = aColour; + avg.a = texture( uTexGlyphs, aTexCoords.xy ).r; + } + else + { + if( uSpread > 0.0001 ) + { + for( int i=0; i<4; i ++ ) + { + vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i))); + avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread ); + } + avg *= 0.25; + avg.a = 1.0; + avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a ); + } + else + { + avg = aColour; + } + } + + FragColor = avg * uColour; +} diff --git a/shaders/ui.vs b/shaders/ui.vs new file mode 100644 index 0000000..37f8b3b --- /dev/null +++ b/shaders/ui.vs @@ -0,0 +1,17 @@ +layout (location=0) in vec2 a_co; +layout (location=1) in vec2 a_uv; +layout (location=2) in vec4 a_colour; +uniform mat3 uPv; +uniform vec2 uBGInverseRatio; +uniform vec2 uInverseFontSheet; + +out vec4 aTexCoords; +out vec4 aColour; + +void main() +{ + vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 ); + gl_Position = proj_pos; + aTexCoords = vec4( a_uv * uInverseFontSheet, (proj_pos.xy*0.5+0.5) * uBGInverseRatio ); + aColour = a_colour; +} diff --git a/shaders/ui_image.fs b/shaders/ui_image.fs new file mode 100644 index 0000000..5d45c1d --- /dev/null +++ b/shaders/ui_image.fs @@ -0,0 +1,13 @@ +uniform sampler2D uTexImage; +uniform vec4 uColour; +out vec4 FragColor; + +in vec2 aTexCoords; +in vec4 aColour; +in vec2 aWsp; + +void main() +{ + vec4 colour = texture( uTexImage, aTexCoords ); + FragColor = colour * aColour * uColour; +} diff --git a/shaders/ui_image.vs b/shaders/ui_image.vs new file mode 100644 index 0000000..47f1704 --- /dev/null +++ b/shaders/ui_image.vs @@ -0,0 +1,17 @@ +layout (location=0) in vec2 a_co; +layout (location=1) in vec2 a_uv; +layout (location=2) in vec4 a_colour; +uniform mat3 uPv; + +out vec2 aTexCoords; +out vec4 aColour; +out vec2 aWsp; + +void main() +{ + gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 ); + aTexCoords = a_uv * 0.00390625; + aColour = a_colour; + + aWsp = a_co; +} diff --git a/shaders/ui_image_grad.fs b/shaders/ui_image_grad.fs new file mode 100644 index 0000000..3201908 --- /dev/null +++ b/shaders/ui_image_grad.fs @@ -0,0 +1,22 @@ +uniform sampler2D uTexImage; +uniform vec4 uColour; +uniform int uLog; +uniform float uScale; + +out vec4 FragColor; + +in vec2 aTexCoords; +in vec4 aColour; +in vec2 aWsp; + +void main() +{ + vec4 colour = texture( uTexImage, aTexCoords ); + float v = colour.r; + if( uLog == 1 ) + { + v = log(v); + } + v *= uScale; + FragColor = vec4(vec3(v)*uColour.rgb,1.0); +} diff --git a/shaders/ui_image_hsv.fs b/shaders/ui_image_hsv.fs new file mode 100644 index 0000000..e98838c --- /dev/null +++ b/shaders/ui_image_hsv.fs @@ -0,0 +1,15 @@ +uniform float uHue; +out vec4 FragColor; + +in vec2 aTexCoords; +in vec4 aColour; +in vec2 aWsp; + +void main() +{ + vec3 c = vec3( uHue, aTexCoords ); + vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0); + vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www); + vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y); + FragColor = vec4( colour, 1.0 ); +} diff --git a/unit_engine.c b/unit_engine.c new file mode 100644 index 0000000..1c01be4 --- /dev/null +++ b/unit_engine.c @@ -0,0 +1,5 @@ +#include "vg/vg.hconf" + +#define VG_IMPLEMENTATION +#include "vg/vg.hconf" +#undef VG_IMPLEMENTATION diff --git a/unit_thirdparty.c b/unit_thirdparty.c new file mode 100644 index 0000000..17ff8c0 --- /dev/null +++ b/unit_thirdparty.c @@ -0,0 +1,2 @@ +#define VG_THIRDPARTY +#include "vg/vg.hconf" diff --git a/vg.hconf b/vg.hconf new file mode 100644 index 0000000..f11f84b --- /dev/null +++ b/vg.hconf @@ -0,0 +1,188 @@ +/* Dependence (Standard) + * ------------------------------------------------------------------------------------------------------------------ */ + +#if !defined( VG_THIRDPARTY ) +# if defined(_WIN32) +# include +# include +# include +# include +# else +# include +# include +# include +# endif +# include +# include + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# if defined( VG_MULTITHREAD ) +# if !defined( VG_ENGINE ) +# include +# include +# endif +# endif +#endif + +/* Dependence (Third party) + * ------------------------------------------------------------------------------------------------------------------ */ + +#if defined( VG_THIRDPARTY ) +# include "vg/submodules/anyascii/impl/c/anyascii.c" +#else +# include "vg/submodules/anyascii/impl/c/anyascii.h" +#endif + +#if defined( VG_ENGINE ) || defined( VG_BUILD_TOOLS ) +# if defined( VG_THIRDPARTY ) +# define STB_IMAGE_IMPLEMENTATION +# define STB_IMAGE_WRITE_IMPLEMENTATION +# endif +# define STBI_NO_THREAD_LOCALS +# include "vg/submodules/stb/stb_image.h" +# include "vg/submodules/stb/stb_image_write.h" +#endif + +#if defined( VG_BUILD_TOOLS ) || defined( VG_ENGINE ) +# if defined( VG_THIRDPARTY ) +# define STB_INCLUDE_IMPLEMENTATION +# endif +# define STB_INCLUDE_LINE_GLSL +# include "vg/submodules/stb/stb_include.h" +#endif + +#if defined( VG_ENGINE ) +# define STB_VORBIS_MAX_CHANNELS 2 +# if defined( VG_THIRDPARTY ) +# undef STB_VORBIS_HEADER_ONLY +# include "vg/submodules/stb/stb_vorbis.c" +# include "vg/vg_vorbis.c" +# undef L +# undef R +# undef C +# else +# define STB_VORBIS_HEADER_ONLY +# include "vg/submodules/stb/stb_vorbis.c" +# include "vg/vg_vorbis.h" +# endif +# define SDL_MAIN_HANDLED +# include "vg/dep/sdl2-devel/include/SDL.h" +# if defined( VG_THIRDPARTY ) +# include "vg/submodules/rax/rax.c" +# else +# include "vg/submodules/rax/rax.h" +# endif +# if defined( VG_THIRDPARTY ) +# include "vg/dep/glad/glad.c" +# else +# include "vg/dep/glad/glad.h" +# endif +#endif + + +/* Tier 0 + * ------------------------------------------------------------------------------------------------------------------ */ +#if !defined( VG_THIRDPARTY ) +# include "vg/vg_platform.h" +# include "vg/vg_mutex.h" +# include "vg/vg_log.h" +# include "vg/vg_mem.h" +# include "vg/vg_mem_pool.h" +# include "vg/vg_mem_queue.h" +# include "vg/vg_string.h" + +/* Tier 1 + * ------------------------------------------------------------------------------------------------------------------ */ +# include "vg/vg_io.h" +# include "vg/vg_kv.h" +# if defined( VG_MSG_LEGACY ) || defined( VG_MSG_TO_KVS ) +# include "vg/vg_msg.h" +# endif +# if defined( VG_MULTITHREAD ) +# include "vg/vg_async2.h" +# endif +# if defined( VG_CONSOLE ) +# include "vg/vg_console.h" +# endif +# include "vg/vg_opt.h" +# include "vg/vg_binstr.h" + +/* Maths + * ------------------------------------------------------------------------------------------------------------------ */ +# if defined( VG_MATH ) +# include "vg/vg_m.h" +# include "vg/vg_perlin.h" +# endif + +/* Database + * ------------------------------------------------------------------------------------------------------------------ */ +# if defined( VG_DB ) +# include "vg/vg_db.h" +# endif + +/* Build tools + * ------------------------------------------------------------------------------------------------------------------ */ +# if defined( VG_BUILD_TOOLS ) +# include "vg/vg_font.h" +# include "vg/vg_build_font.h" +# include "vg/vg_build.h" +# include "vg/vg_build_utils_shader.h" +# if !defined( VG_IMPLEMENTATION ) +# include "vg/vg_tex.h" +# endif +# endif + +/* Game Engine + * ------------------------------------------------------------------------------------------------------------------ */ +# if defined( VG_ENGINE ) +# include "vg/vg_window.h" +# include "vg/vg_shader.h" +# include "src.generated/vg.shaders.h" +# include "vg/vg_tex.h" +# include "vg/vg_font.h" +# include "vg/vg_ui/imgui.h" +# include "vg/vg_ui/imgui_impl_opengl.h" +# include "vg/vg_ui/filebrowser.h" +# include "vg/vg_ui/console.h" +# include "vg/vg_magi.h" +# include "vg/vg_settings.h" + +# include "vg/vg_framebuffer.h" +# include "vg/vg_camera.h" +# include "vg/vg_loader.h" +# include "vg/vg_render.h" + +# include "vg/vg_audio.h" +# include "vg/vg_audio_dsp.h" +# include "vg/vg_audio_synth_bird.h" + +# include "vg/vg_bvh.h" +# include "vg/vg_rigidbody.h" +# include "vg/vg_rigidbody_collision.h" +# include "vg/vg_rigidbody_constraints.h" +# include "vg/vg_rigidbody_view.h" + +# include "vg/vg_input.h" + +# include "vg/vg_profiler.h" +# include "vg/vg_lines.h" +# include "vg/vg_mem_view.h" + +# include "vg/vg_steam2.h" +# include "vg/vg_engine.h" +# if defined( VG_IMPLEMENTATION ) +# include "vg/laptop_gpu.c" +# endif +# endif +#endif diff --git a/vg_async2.c b/vg_async2.c index fedaf33..cf6260b 100644 --- a/vg_async2.c +++ b/vg_async2.c @@ -1,4 +1,62 @@ -#include "vg/vg_async2.h" +struct _vg_async +{ + i16 group_counts[ 16 ]; + vg_mutex count_lock; +} +_vg_async; + +struct vg_async_task +{ + const c8 *alloc_debug_info; + vg_async_fn fn; + u32 buffer_size; + u16 groups, unused0; + + union + { + u64 _force_8byte_align[]; + u8 buffer[]; + }; +}; + +VG_API void _vg_async_init( void ) +{ + VG_ASSERT( VG_MUTEX_INIT( _vg_async.count_lock ) ); +} + +static void _vg_async_group_increment( u16 groups, i16 dir ) +{ + if( !groups ) + return; + + VG_MUTEX_LOCK( _vg_async.count_lock ); + for( u16 i=0; i<16; i ++ ) + { + if( (groups >> i) & 0x1 ) + { + _vg_async.group_counts[i] += dir; + + VG_ASSERT( _vg_async.group_counts[i] >= 0 ); + VG_ASSERT( _vg_async.group_counts[i] <= 2048 ); + + vg_warn( "The task count for group %d has %s to %d\n", + i, dir>0? "increased": "decreased", _vg_async.group_counts[i] ); + } + } + VG_MUTEX_UNLOCK( _vg_async.count_lock ); +} + +VG_API i16 _vg_async_group_count( u16 group ) +{ + VG_ASSERT( group ); + u32 index = __builtin_ctz( (u32)group ); + + VG_MUTEX_LOCK( _vg_async.count_lock ); + i16 count = _vg_async.group_counts[ index ]; + VG_MUTEX_UNLOCK( _vg_async.count_lock ); + + return count; +} bool vg_init_async_queue( vg_async_queue *queue ) { @@ -40,9 +98,10 @@ bool vg_async_checksize( vg_async_queue *queue, u32 bytes ) return total_size <= queue->queue.size; } -vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking, const char *debug_info ) +VG_TIER_1 vg_async_task *vg_create_task( vg_async_queue *queue, u32 buffer_size, u32 async_flags, const c8 *debug_info ) { - u32 total_size = sizeof(vg_async_task) + bytes; + vg_queue *ring = &queue->queue; + u32 total_size = sizeof(vg_async_task) + buffer_size; VG_ASSERT( total_size <= queue->queue.size ); VG_MUTEX_LOCK( queue->data_lock ); if( queue->allocating_task ) @@ -52,36 +111,56 @@ vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool b " Overlapping call at: %s\n", queue->allocating_task->alloc_debug_info, debug_info ); } VG_MUTEX_LOCK( queue->lock ); - vg_queue *ring = &queue->queue; - vg_async_task *task = vg_queue_alloc( ring, total_size, NULL ); - while( blocking && !task ) + vg_async_task *task = vg_queue_alloc( ring, total_size ); + while( (async_flags & (VG_ASYNC_CRIT|VG_ASYNC_BLOCKING)) && !task ) { VG_MUTEX_UNLOCK( queue->lock ); + + if( async_flags & VG_ASYNC_CRIT ) + vg_fatal_error( "Too much tasks (critical)\n" ); + VG_SEMAPHORE_WAIT( queue->blocking_signal ); VG_MUTEX_LOCK( queue->lock ); - task = vg_queue_alloc( ring, total_size, NULL ); + task = vg_queue_alloc( ring, total_size ); } if( task ) { - task->handler = NULL; - task->queue = queue; - task->alloc_debug_info = debug_info; queue->allocating_task = task; + task->alloc_debug_info = debug_info; + task->fn = NULL; + task->buffer_size = buffer_size; + task->groups = _vg_async_context_get_groups(); + _vg_async_group_increment( task->groups, +1 ); + VG_MUTEX_UNLOCK( queue->lock ); + return task; } + else + { + VG_MUTEX_UNLOCK( queue->lock ); + return NULL; + } +} - VG_MUTEX_UNLOCK( queue->lock ); - return task; +void *vg_task_buffer( vg_async_queue *queue, vg_async_task *task ) +{ + VG_ASSERT( task ); + VG_ASSERT( queue->allocating_task == task ); + return task->buffer; } -void _vg_async_task_dispatch( vg_async_task *task, void (*handler)( vg_async_task *task ), const char *debug_info ) +void vg_task_send( vg_async_queue *queue, vg_async_task *task, vg_async_fn fn ) { - task->queue->allocating_task = NULL; - VG_MUTEX_UNLOCK( task->queue->data_lock ); - task->handler = handler; - task->alloc_debug_info = debug_info; - VG_SEMAPHORE_POST( task->queue->work_semaphore ); + VG_ASSERT( task ); + VG_ASSERT( queue->allocating_task == task ); + + if( fn ) task->fn = fn; + else _vg_async_group_increment( task->groups, -1 ); + queue->allocating_task = NULL; + + VG_MUTEX_UNLOCK( queue->data_lock ); + VG_SEMAPHORE_POST( queue->work_semaphore ); } bool vg_async_has_work( vg_async_queue *queue ) @@ -111,10 +190,20 @@ bool vg_async_process_next_task( vg_async_queue *queue ) if( task ) { - if( !task->handler ) - vg_fatal_error( "NO HANDLER ALLOCATED FROM %s\n", task->alloc_debug_info ); + /* task can be NULL if it was cancelled (so this is a NOP). Makes code easier if we do this instead of + * reverting the queue to cancel. */ + if( task->fn ) + { + vg_async_info info = + { + .buffer_size = task->buffer_size, + }; - task->handler( task ); + _vg_async_context_push_groups( task->groups ); + task->fn( (void *)task->buffer, &info ); + _vg_async_context_pop_groups(); + _vg_async_group_increment( task->groups, -1 ); + } VG_MUTEX_LOCK( queue->lock ); vg_queue_pop( &queue->queue ); @@ -134,121 +223,3 @@ void vg_async_queue_end( vg_async_queue *queue, enum async_quit quit ) VG_MUTEX_UNLOCK( queue->lock ); VG_SEMAPHORE_POST( queue->work_semaphore ); } - -/* - * simple functions - * ------------------------------------------------------------------------------------------------------------------ */ -struct simple_function_info -{ - void (*fn)(void *userdata); - void *userdata; -}; - -static void simple_function_call( vg_async_task *task ) -{ - struct simple_function_info *info = (void *)task->data; - info->fn( info->userdata ); -} - -void vg_async_call( vg_async_queue *queue, void(*fn)(void *userdata), void *userdata ) -{ - vg_async_task *task = vg_allocate_async_task( queue, sizeof(struct simple_function_info), 1 ); - struct simple_function_info *info = (void *)task->data; - info->fn = fn; - info->userdata = userdata; - vg_async_task_dispatch( task, simple_function_call ); -} - -/* - * Coroutines - * ------------------------------------------------------------------------------------------------------------------ */ - -bool co_begin( vg_coroutine *co ) -{ - co->i = 0; - if( co->state == k_coroutine_state_none ) - { - co->state = k_coroutine_state_init; - return 1; - } - else - { - co->state = k_coroutine_state_run; - return 0; - } -} - -void co_thread( vg_coroutine *co, i32 thread_id, vg_async_queue *queue ) -{ - co->worker_queues[thread_id] = queue; -} - -void *co_storage( vg_coroutine *co, u16 size ) -{ - co->data_size = size; - co->task = vg_allocate_async_task( co->worker_queues[0], sizeof(vg_coroutine) + size, 1 ); - vg_coroutine *next_co = (void *)co->task->data; - memcpy( next_co, co, sizeof(vg_coroutine) ); - memset( next_co->data, 0, size ); - return next_co->data; -} - -static void co_internal_call( vg_async_task *task ) -{ - vg_coroutine *co = (void *)task->data; - co->userdata = NULL; - co->task = NULL; - co->fn( co ); -} - -bool co_step( vg_coroutine *co, i32 thread_id ) -{ - if( co->state == k_coroutine_state_run ) - { - if( co->i == co->step ) - { - if( thread_id == co->thread ) - { - co->step ++; - co->i ++; - return 1; - } - else - { - co->thread = thread_id; - u32 size = sizeof(vg_coroutine) + co->data_size; - co->task = vg_allocate_async_task( co->worker_queues[ thread_id ], size, 1 ); - vg_coroutine *co_copy = (void *)co->task->data; - memcpy( co_copy, co, size ); - co->state = k_coroutine_state_orphan; - return 0; - } - } - else - co->i ++; - } - - return 0; -} - -void co_end( vg_coroutine *co ) -{ - if( co->state == k_coroutine_state_init ) - { - if( !co->task ) - co_storage( co, 0 ); - } - - if( co->task ) - vg_async_task_dispatch( co->task, co_internal_call ); -} - -void co_run( void(*fn)(vg_coroutine *co), void *userdata ) -{ - vg_coroutine bootstrap = {0}; - bootstrap.userdata = userdata; - bootstrap.state = k_coroutine_state_none; - bootstrap.thread = 0xffff; - bootstrap.fn = fn; - fn( &bootstrap ); -} diff --git a/vg_async2.h b/vg_async2.h index 8cf2d2d..77d9f60 100644 --- a/vg_async2.h +++ b/vg_async2.h @@ -1,11 +1,23 @@ -#pragma once -#include "vg/vg_mem.h" -#include "vg/vg_mem_queue.h" -#include "vg/vg_mutex.h" +/* VG Async 2 + * Type: Library + * Depends on: vg_platform, vg_mem, vg_mutex + */ + +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_async2.c" +#else typedef struct vg_async_queue vg_async_queue; typedef struct vg_async_task vg_async_task; -typedef struct vg_coroutine vg_coroutine; +typedef struct vg_async_data vg_async_data; +typedef struct vg_async_info vg_async_info; + +typedef void (*vg_async_fn)( void *user, vg_async_info *info ); + +struct vg_async_info +{ + u32 buffer_size; +}; struct vg_async_queue { @@ -28,55 +40,60 @@ struct vg_async_queue quit; }; -struct vg_async_task -{ - vg_async_queue *queue; - const char *alloc_debug_info; - void (*handler)( vg_async_task *task ); - u8 data[]; -}; +VG_API void _vg_async_init( void ); -struct vg_coroutine -{ - void *userdata; - vg_async_task *task; - vg_async_queue *worker_queues[2]; - void (*fn)(vg_coroutine *co); +VG_API u32 _vg_async_push_grouping( u32 group_ids ); +VG_API void _vg_async_pop_grouping(void); +VG_API i16 _vg_async_group_count( u16 group ); - u16 step, i, thread; +bool vg_init_async_queue( vg_async_queue *queue ); +void vg_free_async_queue( vg_async_queue *queue ); - enum - { - k_coroutine_state_none, - k_coroutine_state_init, - k_coroutine_state_run, - k_coroutine_state_orphan - } - state; +#define VG_ASYNC_CANCEL 0 +#define VG_ASYNC_OK 1 - u16 data_size; - u8 data[]; -}; +/* If task would exceed queue capacity, crash the program */ +#define VG_ASYNC_CRIT 0x4 -void *co_storage( vg_coroutine *co, u16 size ); -bool co_begin( vg_coroutine *co ); -void co_thread( vg_coroutine *co, i32 thread_id, vg_async_queue *queue ); -bool co_step( vg_coroutine *co, i32 thread_id ); -void co_run( void(*fn)(vg_coroutine *co), void *userdata ); -void co_end( vg_coroutine *co ); +/* If task currently would exceeed queue capacity, block until there is space */ +#define VG_ASYNC_BLOCKING 0x2 -bool vg_init_async_queue( vg_async_queue *queue ); -void vg_free_async_queue( vg_async_queue *queue ); +/* If queue is full, immediately return and don't conduct task */ +#define VG_ASYNC_NONBLOCK 0x1 -/* returns NULL if out of memory, or if blocking is set, wait for memory to become availible */ -#define vg_allocate_async_task( Q,B,BL ) _vg_allocate_async_task( Q,B,BL, VG_LOG_WHERE ) -vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking, const char *debug_info ); +/* Any asynchronous flag. NOTE: if you try to create a task that exceeds the queue capacity, regardless of which + * strategy you chose, the program will crash. */ +#define VG_ASYNC (VG_ASYNC_CRIT|VG_ASYNC_BLOCKING|VG_ASYNC_NONBLOCK) + +/* Used for clarity in other functions that you DONT wan't it to run asynchronously */ +#define VG_SYNC 0x0 + +#define VG_ASYNC_GROUP_OPENGL 0x1 +#define VG_ASYNC_GROUP_INIT 0x2 +#define VG_ASYNC_GROUP_RESERVED2 0x4 +#define VG_ASYNC_GROUP_RESERVED3 0x8 +#define VG_ASYNC_GROUP_RESERVED4 0x10 +#define VG_ASYNC_GROUP_RESERVED5 0x20 +#define VG_ASYNC_GROUP_RESERVED6 0x40 +#define VG_ASYNC_GROUP_RESERVED7 0x80 + +#define VG_ASYNC_GROUP_CLIENT0 0x100 +#define VG_ASYNC_GROUP_CLIENT1 0x200 +#define VG_ASYNC_GROUP_CLIENT2 0x400 +#define VG_ASYNC_GROUP_CLIENT3 0x800 +#define VG_ASYNC_GROUP_CLIENT4 0x1000 +#define VG_ASYNC_GROUP_CLIENT5 0x2000 +#define VG_ASYNC_GROUP_CLIENT6 0x4000 +#define VG_ASYNC_GROUP_CLIENT7 0x8000 + +VG_TIER_1 vg_async_task *vg_create_task( vg_async_queue *queue, u32 buffer_size, u32 async_flags, const c8 *debug_info ); +void *vg_task_buffer( vg_async_queue *queue, vg_async_task *task ); +void vg_task_send( vg_async_queue *queue, vg_async_task *task, vg_async_fn fn ); -#define vg_async_task_dispatch( T,H ) _vg_async_task_dispatch( T,H, VG_LOG_WHERE ) -void _vg_async_task_dispatch( vg_async_task *task, void (*handler)( vg_async_task *task ), const char *debug_info ); void vg_async_queue_end( vg_async_queue *queue, enum async_quit quit ); bool vg_async_has_work( vg_async_queue *queue ); bool vg_async_process_next_task( vg_async_queue *queue ); -void vg_async_call( vg_async_queue *queue, void(*fn)(void *userdata), void *userdata ); bool vg_async_checksize( vg_async_queue *queue, u32 bytes ); + +#endif diff --git a/vg_audio.c b/vg_audio.c index 41f9136..56aee24 100644 --- a/vg_audio.c +++ b/vg_audio.c @@ -1,47 +1,9 @@ -#include "vg_audio.h" -#include "vg_audio_dsp.h" -#include "vg_platform.h" -#include "vg_io.h" -#include "vg_m.h" -#include "vg_console.h" -#include "vg_profiler.h" -#include "vg_audio_synth_bird.h" -#include "vg_vorbis.h" -#include - struct vg_audio _vg_audio = { .master_volume_ui = 1.0f, .dsp_enabled_ui = 1 }; -static struct vg_profile - _vg_prof_audio_decode = { .mode = k_profile_mode_accum, .name = "[T2] audio_decode()" }, - _vg_prof_audio_mix = { .mode = k_profile_mode_accum, .name = "[T2] audio_mix()" }, - _vg_prof_dsp = { .mode = k_profile_mode_accum, .name = "[T2] dsp_process()" }, - _vg_prof_audio_decode_ui, - _vg_prof_audio_mix_ui, - _vg_prof_audio_dsp_ui; - -static f64 _vg_audio_budget() -{ - vg_audio_lock(); - f64 ms = ((double)_vg_audio.samples_written_last_audio_frame / 44100.0) * 1000.0; - vg_audio_unlock(); - return ms; -} - -struct vg_profile_set static _vg_prof_audio = -{ - .name = "audio", - .count = 3, - .get_budget = _vg_audio_budget, - .list = - { - &_vg_prof_audio_decode_ui, &_vg_prof_audio_mix_ui, &_vg_prof_audio_dsp_ui - } -}; - _Thread_local static bool _vg_audio_thread_has_lock = 0; void vg_audio_lock(void) @@ -68,114 +30,105 @@ static void vg_audio_assert_lock(void) /* clip loading from disk * ------------------------------------------------------------------------------- */ -void audio_clip_load( audio_clip *clip, vg_stack_allocator *stack ) +VG_TIER_2 bool vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack ) { - THREAD_1; - - if( stack == NULL ) - stack = _vg_audio.permanent_stack; - - if( _vg_audio.always_keep_clips_compressed ) - { - if( (clip->flags & AUDIO_FLAG_FORMAT) != k_audio_format_bird ) - { - clip->flags &= ~AUDIO_FLAG_FORMAT; - clip->flags |= k_audio_format_vorbis; - } - } + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); /* load in directly */ u32 format = clip->flags & AUDIO_FLAG_FORMAT; - - /* TODO: This contains audio_lock() and unlock, but i don't know why - * can probably remove them. Low priority to check this */ - - /* TODO: packed files for vorbis etc, should take from data if its not not - * NULL when we get the clip - */ - if( format == k_audio_format_vorbis ) { - if( !clip->path ) + if( clip->path ) + { + clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 ); + if( clip->any_data ) + { + float mb = (float)(clip->size) / (1024.0f*1024.0f); + vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); + return 1; + } + else + { + vg_error( "Audio failed to load '%s'\n", clip->path ); + return 0; + } + } + else + { vg_error( "No path specified, embeded vorbis unsupported\n" ); - - vg_audio_lock(); - clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 ); - vg_audio_unlock(); - - if( !clip->any_data ) - vg_error( "Audio failed to load\n" ); - - float mb = (float)(clip->size) / (1024.0f*1024.0f); - vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); + return 0; + } } else if( format == k_audio_format_stereo ) { vg_error( "Unsupported format (Stereo uncompressed)\n" ); + return 0; } else if( format == k_audio_format_bird ) { if( !clip->any_data ) { vg_error( "No data, external birdsynth unsupported\n" ); + return 0; } - u32 total_size = clip->size + sizeof(struct synth_bird); - total_size -= sizeof(struct synth_bird_settings); - total_size = vg_align8( total_size ); - - if( total_size > AUDIO_DECODE_SIZE ) - vg_error( "Bird coding too long, and exceeds maximum decode size\n" ); - - struct synth_bird *bird = vg_stack_allocate( stack, total_size, 8, NULL ); - memcpy( &bird->settings, clip->any_data, clip->size ); - + struct synth_bird *bird = vg_stack_allocate( stack, sizeof(struct synth_bird), 8, NULL ); + bird->settings = clip->any_data; clip->any_data = bird; - clip->size = total_size; - - vg_info( "Loaded bird synthesis pattern (%u bytes)\n", total_size ); + vg_info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size ); + return 1; } else { if( !clip->path ) + { vg_error( "No path specified, embeded mono unsupported\n" ); + return 0; + } - vg_stack_clear( &vg.scratch ); - u32 fsize; - - stb_vorbis_alloc alloc = { - .alloc_buffer = vg_stack_allocate( &vg.scratch, AUDIO_DECODE_SIZE, 8, NULL ), + u32 temp_frame = _vg_start_temp_frame(); + stb_vorbis_alloc alloc = + { + .alloc_buffer = vg_stack_allocate( _vg_temp_stack(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ), .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE }; - void *filedata = vg_file_read( &vg.scratch, clip->path, &fsize, 0 ); - + u32 fsize; + void *filedata = vg_file_read( _vg_temp_stack(), clip->path, &fsize, 0 ); int err; stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc ); - if( !decoder ) - vg_fatal_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err ); + { + vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err ); + _vg_end_temp_frame( temp_frame ); + return 0; + } /* only mono is supported in uncompressed */ u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ), data_size = length_samples * sizeof(i16); - vg_audio_lock(); clip->any_data = vg_stack_allocate( stack, data_size, 8, NULL ); clip->size = length_samples; - vg_audio_unlock(); - int read_samples = stb_vorbis_get_samples_i16_downmixed( decoder, clip->any_data, length_samples ); + _vg_end_temp_frame( temp_frame ); - if( read_samples != length_samples ) + if( read_samples == length_samples ) + return 1; + else + { vg_error( "Decode error, read_samples did not match length_samples\n" ); + return 0; + } } } -void audio_clip_loadn( audio_clip *arr, int count, vg_stack_allocator *stack ) +VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack ) { - for( int i=0; igroup ) { vg_strcat( &str, " grp" ); - vg_strcati32r( &str, channel->group, 6, ' ' ); + vg_strcati64r( &str, channel->group, 10, 6, ' ' ); } else vg_strcat( &str, " " ); @@ -1285,7 +1227,7 @@ static void cb_vg_audio_view( ui_context *ctx, ui_rect rect, struct vg_magi_pane vg_strcat( &str, " " ); f32 volume = (f32)channel->ui_volume / (f32)AUDIO_VOLUME_100; - vg_strcati32r( &str, volume * 100.0f, 3, ' ' ); + vg_strcati64r( &str, volume * 100.0f, 10, 3, ' ' ); vg_strcatch( &str, '%' ); vg_strcat( &str, " " ); @@ -1424,7 +1366,7 @@ static int cmd_vg_audio( int argc, const char *argv[] ) return 1; } -void vg_audio_register(void) +VG_API void _vg_audio_register(void) { vg_console_reg_cmd( "vg_audio", cmd_vg_audio, NULL ); vg_console_reg_var( "volume", &_vg_audio.master_volume_ui, k_var_dtype_f32, VG_VAR_PERSISTENT ); @@ -1469,15 +1411,21 @@ void vg_audio_device_init(void) } } -void vg_audio_init(void) +static void vg_audio_free(void) { - _vg_profile_reg_set( &_vg_prof_audio ); - _vg_audio.permanent_stack = vg_stack_make_substack( &vg.rtmem, VG_MB(32), "Permanent audio data" ); - vg_stack_set_flags( _vg_audio.permanent_stack, VG_STACK_ALLOCATOR_METADATA ); + SDL_CloseAudioDevice( _vg_audio.sdl_output_device ); + _vg_audio.sdl_output_device = 0; +} + +VG_API void _vg_audio_init(void) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) ); + + _vg_audio.profiler = _vg_profiler_create( "vg.audio", 1000.0f/20.0f ); /* fixed */ u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS; - _vg_audio.decoding_buffer = vg_stack_allocate( &vg.rtmem, decode_size, 8, "Decoding buffers" ); + _vg_audio.decoding_buffer = vg_stack_allocate( NULL, decode_size, 8, "Decoding buffers" ); struct audio_master_controls *master_controls = &_vg_audio.controls; master_controls->dsp_enabled = _vg_audio.dsp_enabled_ui; @@ -1485,21 +1433,14 @@ void vg_audio_init(void) v3_copy( (v3f){1,0,0}, master_controls->listener_right_ear_direction ); v3_zero( master_controls->listener_velocity ); v3_zero( master_controls->listener_position ); - vg_dsp_init(); -} + _vg_dsp_init(); -void vg_audio_begin(void) -{ _vg_audio.mutex = SDL_CreateMutex(); if( _vg_audio.mutex == NULL ) vg_fatal_error( "SDL2: %s\n", SDL_GetError() ); vg_audio_device_init(); -} -void vg_audio_free(void) -{ - SDL_CloseAudioDevice( _vg_audio.sdl_output_device ); - _vg_audio.sdl_output_device = 0; + _vg_add_exit_function( vg_audio_free ); } void vg_audio_preupdate(void) diff --git a/vg_audio.h b/vg_audio.h index 8be4c69..ef0acf9 100644 --- a/vg_audio.h +++ b/vg_audio.h @@ -1,12 +1,6 @@ -/* Copyright (C) 2021-2025 Mt.Zero Software - All Rights Reserved */ - -#pragma once - -#include "vg_platform.h" -#include "vg_engine.h" -#include "vg_string.h" -#include "vg_vorbis.h" - +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_audio.c" +#else #define AUDIO_FRAME_SIZE 512 #define AUDIO_MIX_FRAME_SIZE 256 @@ -182,7 +176,6 @@ struct vg_audio SDL_AudioDeviceID sdl_output_device; vg_str device_choice; /* buffer is null? use default from OS */ - vg_stack_allocator *permanent_stack; void *decoding_buffer; SDL_mutex *mutex; @@ -210,23 +203,24 @@ struct vg_audio i32 volume, volume_at_frame_start; } state; - bool always_keep_clips_compressed; f32 master_volume_ui; i32 dsp_enabled_ui; bool working; + + u32 profiler; } extern _vg_audio; -void vg_audio_register(void); +VG_API void _vg_audio_register(void); +VG_API void _vg_audio_init(void); + void vg_audio_device_init(void); void vg_audio_begin(void); -void vg_audio_init(void); -void vg_audio_free(void); -void audio_clip_load( audio_clip *clip, vg_stack_allocator *stack ); -void audio_clip_loadn( audio_clip *arr, int count, vg_stack_allocator *stack ); +VG_TIER_2 bool vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack ); +VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack ); void vg_audio_lock(void); void vg_audio_unlock(void); @@ -265,3 +259,4 @@ void vg_audio_fadeout_flagged_audio( u32 flag, f32 length ); bool vg_audio_flagged_stopped( u32 flag ); bool vg_audio_is_channel_using_clip( audio_channel_id id, audio_clip *clip ); void vg_audio_set_flagged_pause( u32 flag, bool pause ); +#endif diff --git a/vg_audio_dsp.c b/vg_audio_dsp.c index 7d6bd1a..e30a431 100644 --- a/vg_audio_dsp.c +++ b/vg_audio_dsp.c @@ -1,6 +1,3 @@ -#include "vg_audio_dsp.h" -#include "vg_mem.h" - struct vg_dsp vg_dsp; float *dsp_allocate( u32 samples ) @@ -130,10 +127,10 @@ static struct dsp_lpf __echos_lpf[8]; #endif static struct dsp_schroeder __diffusion_chain[8]; -void vg_dsp_init( void ) +VG_API void _vg_dsp_init(void) { vg_rand_seed( &vg_dsp.rand, 461 ); - vg_dsp.buffer = vg_stack_allocate( &vg.rtmem, VG_MB(1), 8, "Audio DSP Buffer" ); + vg_dsp.buffer = vg_stack_allocate( NULL, VG_MB(1), 8, "Audio DSP Buffer" ); /* temporary global design */ dsp_init_lpf( &__lpf_mud_free, 125.0f ); diff --git a/vg_audio_dsp.h b/vg_audio_dsp.h index 4a1e9ae..219b971 100644 --- a/vg_audio_dsp.h +++ b/vg_audio_dsp.h @@ -1,11 +1,9 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_audio_dsp.c" +#else //#define VG_ECHO_LPF_BUTTERWORTH -#include "vg_platform.h" -#include "vg_opengl.h" -#include "vg_m.h" - struct vg_dsp { float *buffer; @@ -44,8 +42,7 @@ struct dsp_biquad xnz1, xnz2, ynz1, ynz2, offset; }; -void vg_dsp_init( void ); -void vg_dsp_free( void ); +VG_API void _vg_dsp_init(void); void dsp_update_tunings(void); void vg_dsp_process( float *stereo_in, float *stereo_out ); @@ -59,5 +56,6 @@ void dsp_init_lpf( struct dsp_lpf *lpf, float freq ); void dsp_write_lpf( struct dsp_lpf *lpf, float *s ); void dsp_read_lpf( struct dsp_lpf *lpf, float *s ); void dsp_init_schroeder( struct dsp_schroeder *sch, float length, float gain ); -void dsp_process_schroeder( struct dsp_schroeder *sch, - float *input, float *output ); +void dsp_process_schroeder( struct dsp_schroeder *sch, float *input, float *output ); + +#endif diff --git a/vg_audio_synth_bird.c b/vg_audio_synth_bird.c index eecad3b..71d8519 100644 --- a/vg_audio_synth_bird.c +++ b/vg_audio_synth_bird.c @@ -1,6 +1,3 @@ -#include "vg_audio_synth_bird.h" -#include "vg_binstr.h" - #define DEFAULT_VOL 1.0f,0.5f,0.2f,0.125f #define DEFAULT_TONES { {1,1}, {6,5}, {8,7}, {13,12} } #define DEFAULT_RISE 0.00090702947f @@ -151,9 +148,9 @@ u32 synth_bird_get_length_in_samples( struct synth_bird *bird ) { u32 total = 0; - for( int i=0; isettings.pattern_length; i ++ ){ - struct synth_bird_signature *sig = &bird->settings.pattern[i]; - + for( int i=0; isettings->pattern_length; i ++ ) + { + struct synth_bird_signature *sig = &bird->settings->pattern[i]; u32 l = sig->length * (float)BIRD_SAMPLE_RATE, p = sig->pause * (float)BIRD_SAMPLE_RATE; @@ -178,20 +175,20 @@ void synth_bird_reset( struct synth_bird *bird ) bird->rt.fundamental = 0.0f; bird->rt.x = 0; - bird->rt.length = bird->settings.pattern[0].length * (float)BIRD_SAMPLE_RATE; + bird->rt.length = bird->settings->pattern[0].length * (float)BIRD_SAMPLE_RATE; bird->rt.gate = 1; bird->rt.adsr = 0; bird->rt.frame = 0; bird->rt.lfo_hz = 0; bird->rt.fm_depth = 0.0f; - bird->rt.adsr_rise = bird->settings.adsr_rise * (float)BIRD_SAMPLE_RATE; - bird->rt.adsr_fall = bird->settings.adsr_fall * (float)BIRD_SAMPLE_RATE; + bird->rt.adsr_rise = bird->settings->adsr_rise * (float)BIRD_SAMPLE_RATE; + bird->rt.adsr_fall = bird->settings->adsr_fall * (float)BIRD_SAMPLE_RATE; } static u32 synth_bird_save_size( struct synth_bird *bird ) { - return sizeof(struct synth_bird_signature) * bird->settings.pattern_length + + return sizeof(struct synth_bird_signature) * bird->settings->pattern_length + sizeof(struct synth_bird_settings); } @@ -232,8 +229,8 @@ static struct synth_bird *synth_bird_create( bird->settings = *settings; - memcpy( bird->settings.pattern, pattern, pattern_size ); - bird->settings.pattern_length = pattern_length; + memcpy( bird->settings->pattern, pattern, pattern_size ); + bird->settings->pattern_length = pattern_length; synth_bird_reset( bird ); return bird; @@ -243,7 +240,7 @@ static struct synth_bird *synth_bird_create( static void synth_bird_think( struct synth_bird *bird ) { - struct synth_bird_signature *sig = &bird->settings.pattern[ bird->rt.frame ]; + struct synth_bird_signature *sig = &bird->settings->pattern[ bird->rt.frame ]; bird->rt.x ++; if( bird->rt.x >= bird->rt.length ) @@ -257,10 +254,10 @@ static void synth_bird_think( struct synth_bird *bird ) { bird->rt.frame ++; - if( bird->rt.frame >= bird->settings.pattern_length ) + if( bird->rt.frame >= bird->settings->pattern_length ) bird->rt.frame = 0; - sig = &bird->settings.pattern[ bird->rt.frame ]; + sig = &bird->settings->pattern[ bird->rt.frame ]; bird->rt.gate = 1; bird->rt.length = sig->length * (float)BIRD_SAMPLE_RATE; @@ -323,10 +320,10 @@ void synth_bird_generate_samples( struct synth_bird *bird, int freq = bird->rt.fundamental + fm; int hz[4] = { - (freq * bird->settings.tones[0][0]) / bird->settings.tones[0][1], - (freq * bird->settings.tones[1][0]) / bird->settings.tones[1][1], - (freq * bird->settings.tones[2][0]) / bird->settings.tones[2][1], - (freq * bird->settings.tones[3][0]) / bird->settings.tones[3][1], + (freq * bird->settings->tones[0][0]) / bird->settings->tones[0][1], + (freq * bird->settings->tones[1][0]) / bird->settings->tones[1][1], + (freq * bird->settings->tones[2][0]) / bird->settings->tones[2][1], + (freq * bird->settings->tones[3][0]) / bird->settings->tones[3][1], }; int_add_mod( bird->rt.osc_main + 0, hz[0] ); @@ -348,8 +345,6 @@ void synth_bird_generate_samples( struct synth_bird *bird, } #ifdef SYNTH_BIRD_STDLIB -#include "stdio.h" - #define KNRM "\x1B[00m" #define KRED "\x1B[31m" #define KGRN "\x1B[32m" diff --git a/vg_audio_synth_bird.h b/vg_audio_synth_bird.h index 0a2f418..ed1f973 100644 --- a/vg_audio_synth_bird.h +++ b/vg_audio_synth_bird.h @@ -1,4 +1,6 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_audio_synth_bird.c" +#else #ifndef BIRD_SAMPLE_RATE #define BIRD_SAMPLE_RATE 44100 @@ -16,7 +18,8 @@ struct synth_bird_signature struct synth_bird { - struct{ + struct + { int osc_main[4], osc_lfo; float volume[4]; @@ -34,7 +37,8 @@ struct synth_bird } rt; - struct synth_bird_settings{ + struct synth_bird_settings + { int tones[4][2]; /* fraction of the fundemental tone for each oscillator */ float adsr_rise, /* rise/fall in seconds */ @@ -49,10 +53,11 @@ struct synth_bird int pattern_length; struct synth_bird_signature pattern[]; } - settings; + *settings; }; void synth_bird_reset( struct synth_bird *bird ); u32 synth_bird_get_length_in_samples( struct synth_bird *bird ); -void synth_bird_generate_samples( struct synth_bird *bird, - float *stereo_buffer, int samples ); +void synth_bird_generate_samples( struct synth_bird *bird, float *stereo_buffer, int samples ); + +#endif diff --git a/vg_binstr.c b/vg_binstr.c index 7d9ac83..7047f15 100644 --- a/vg_binstr.c +++ b/vg_binstr.c @@ -1,5 +1,3 @@ -#include "vg/vg_binstr.h" - void vg_str_bin( const void *txt, void *bin, int size ) { const u8 *src = txt; diff --git a/vg_binstr.h b/vg_binstr.h index 6e662fe..5a2ec1e 100644 --- a/vg_binstr.h +++ b/vg_binstr.h @@ -1,10 +1,11 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_binstr.c" +#else /* dead simple.. 4 bits/character encoding */ - -#include "vg_platform.h" - #define VG_BINSTR_BASECHAR 0x41 void vg_str_bin( const void *txt, void *bin, int size ); void vg_bin_str( const void *bin, void *txt, u32 size ); + +#endif diff --git a/vg_build.h b/vg_build.h index ccf93e9..86705fc 100644 --- a/vg_build.h +++ b/vg_build.h @@ -1,16 +1,6 @@ -#include -#include -#include -#include -#include - -#include "vg_opt.h" -#include "vg_log.h" -#include "vg_string.h" -#include "vg_build_font.h" - +#if defined( VG_IMPLEMENTATION ) /* we dont free dynamic vg_strs in this program. so, we dont care.. */ -const char *__asan_default_options() { return "detect_leaks=0"; } +const c8 *__asan_default_options() { return "detect_leaks=0"; } struct vg_project { @@ -55,23 +45,13 @@ struct vg_compiler_env } libc; } -vg_test_env = +_vg_common_env = { .optimization = 0, .debug_asan = 1, .thread_san = 0, .platform = k_platform_linux, .arch = k_architecture_x86_64, - .compiler = k_compiler_clang, - .libc = k_libc_version_native -}, -vg_release_env = -{ - .optimization = 3, - .debug_asan = 0, - .thread_san = 0, - .platform = k_platform_anyplatform, - .arch = k_architecture_x86_64, .compiler = k_compiler_zigcc, .libc = k_libc_version_2_23 }; @@ -97,28 +77,28 @@ enum obj_type * string tables * -------------------------------------------------------------------------- */ -static const char *platform_names[] = +static const c8 *platform_names[] = { [k_platform_anyplatform] = "anyplatform", [k_platform_windows] = "windows", [k_platform_linux] = "linux", }; -static const char *architecture_names[] = +static const c8 *architecture_names[] = { [k_architecture_anyarch] = "anyarch", [k_architecture_i386] = "i386", [k_architecture_x86_64] = "x86_64", }; -static const char *compiler_names[] = +static const c8 *compiler_names[] = { [k_compiler_blob] = "blob", [k_compiler_clang] = "clang", [k_compiler_zigcc] = "zig-cc", }; -static const char *libc_names[] = +static const c8 *libc_names[] = { [k_libc_version_native] = "", [k_libc_version_2_23] = ".2.23" @@ -128,7 +108,7 @@ static const char *libc_names[] = * OS & file tools * -------------------------------------------------------------------------- */ -void vg_syscall( const char *fmt, ... ) +void vg_syscall( const c8 *fmt, ... ) { va_list args; va_start( args, fmt ); @@ -142,13 +122,12 @@ void vg_syscall( const char *fmt, ... ) exit(1); } -void vg_add_blob( struct vg_project *proj, const char *blob, const char *dest ) +void vg_add_blob( struct vg_project *proj, const c8 *blob, const c8 *dest ) { vg_syscall( "cp %s %s/%s", blob, proj->bin_folder.buffer, dest ); } -void vg_symlink( struct vg_project *proj, - const char *folder, const char *bin_name ) +void vg_symlink( struct vg_project *proj, const c8 *folder, const c8 *bin_name ) { char dest[512]; snprintf( dest, 512, "%s/%s", proj->bin_folder.buffer, bin_name ); @@ -159,14 +138,7 @@ void vg_symlink( struct vg_project *proj, void vg_tarball_project( struct vg_project *proj ) { - vg_syscall( "tar -chzvf dist/%s-%u.tar.gz %s/", - proj->uid.buffer, time(NULL), proj->bin_folder.buffer ); -} - -bool vg_platform_posix( enum platform p ) -{ - if( p == k_platform_linux ) return 1; - else return 0; + vg_syscall( "tar -chzvf dist/%s-%u.tar.gz %s/", proj->uid.buffer, time(NULL), proj->bin_folder.buffer ); } /* @@ -178,8 +150,8 @@ bool vg_platform_posix( enum platform p ) * CLEAR IF fresh */ void vg_project_init( struct vg_project *proj, - const char *folder, - const char *name, + const c8 *folder, + const c8 *name, struct vg_compiler_env *env, bool fresh ) { @@ -217,14 +189,21 @@ struct compile_result rel_path; }; +bool compiler_supports_sanitizers( enum compiler compiler ) +{ + if( compiler == k_compiler_clang ) return 1; + if( compiler == k_compiler_zigcc ) return 1; + return 0; +} + /* run a compiler.. return compiled object relative to project folder */ struct compile_result vg_compiler_run( struct vg_project *project, struct vg_compiler_env *env, struct vg_compiler_conf *conf, - const char *sources, - const char *target_name, + const c8 *sources, + const c8 *target_name, enum obj_type type ) { /* check for problems in configuration */ @@ -295,16 +274,16 @@ vg_compiler_run( struct vg_project *project, vg_strcat( &cmd, "\\\n" ); } - if( (env->compiler == k_compiler_clang) && env->debug_asan ) - vg_strcat( &cmd, " -rdynamic -fsanitize=undefined -fsanitize=address -fPIE -fstack-protector-strong " ); + if( compiler_supports_sanitizers(env->compiler) && env->debug_asan ) + vg_strcat( &cmd, " -lasan -rdynamic -fsanitize=address -fPIE -fstack-protector-strong " ); - if( (env->compiler == k_compiler_clang) && env->thread_san ) - vg_strcat( &cmd, " -rdynamic -fsanitize=thread -fPIE -fstack-protector-strong " ); + if( compiler_supports_sanitizers(env->compiler) && env->thread_san ) + vg_strcat( &cmd, " -lasan -rdynamic -fsanitize=thread -fPIE -fstack-protector-strong " ); vg_strcat( &cmd, conf->no_lto? " -fno-lto \\\n": " -flto \\\n" ); /* want a lot of warnings but not useless ones */ - vg_strcat( &cmd, " -Wall -ferror-limit=10\\\n" + vg_strcat( &cmd, " -Wall -ferror-limit=5000\\\n" " -Wno-unused-function -Wno-unused-variable\\\n" " -Wno-unused-command-line-argument -Wno-unused-but-set-variable\\\n" ); @@ -355,7 +334,7 @@ vg_compiler_run( struct vg_project *project, vg_strcat( &res.rel_path, ".obj" ); } - if( vg_platform_posix( env->platform ) ) + if( env->platform == k_platform_linux ) { if( type == k_obj_type_shared ) vg_strcat( &res.rel_path, ".so" ); @@ -396,8 +375,8 @@ struct vg_engine_config log_source_info, steam_api, custom_game_settings, - custom_shaders, - multiplayer; + multiplayer, + release_mode; i32 fixed_update_hz; } vg_engine_default_config = @@ -408,8 +387,8 @@ vg_engine_default_config = .log_source_info = 1, .steam_api = 0, .custom_game_settings = 0, - .custom_shaders = 0, - .multiplayer = 0 + .multiplayer = 0, + .release_mode = 0 }; struct compile_result @@ -417,8 +396,8 @@ vg_make_app( struct vg_project *proj, struct vg_engine_config *vg_conf, struct vg_compiler_env *env, struct vg_compiler_conf *conf, - const char *sources, - const char *appname ) + const c8 *sources, + const c8 *appname ) { struct vg_project vg_proj; vg_project_init( &vg_proj, "bin", ".vg", env, 0 ); @@ -428,12 +407,13 @@ vg_make_app( struct vg_project *proj, /* add config defines to compiler config */ if( !vg_conf ) vg_conf = &vg_engine_default_config; + vg_strcat( &conf->defines, vg_conf->use_3d? "-DVG_3D \\\n": "-DVG_2D \\\n" ); - vg_strcatf( &conf->defines, "-DVG_TIMESTEP_FIXED=\"(1.0/%d.0)\" \\\n", - vg_conf->fixed_update_hz ); + vg_strcatf( &conf->defines, "-DVG_TIMESTEP_FIXED=\"(1.0/%d.0)\" \\\n", vg_conf->fixed_update_hz ); + vg_strcat( &conf->defines, "-DVG_MULTITHREAD -DVG_CONSOLE -DVG_MATH \\\n" ); if( vg_conf->legacy_support_vg_msg1 ) - vg_strcat( &conf->defines, "-DVG_MSG_V1_SUPPORT \\\n" ); + vg_strcat( &conf->defines, "-DVG_MSG_TO_KVS \\\n" ); if( vg_conf->log_source_info ) vg_strcat( &conf->defines, "-DVG_LOG_SOURCE_INFO \\\n" ); @@ -441,17 +421,20 @@ vg_make_app( struct vg_project *proj, if( vg_conf->custom_game_settings ) vg_strcat( &conf->defines, "-DVG_GAME_SETTINGS \\\n" ); - if( vg_conf->custom_shaders ) - vg_strcat( &conf->defines, "-DVG_CUSTOM_SHADERS \\\n" ); - if( vg_conf->multiplayer ) vg_strcat( &conf->defines, "-DVG_MULTIPLAYER \\\n" ); + if( vg_conf->release_mode ) + vg_strcat( &conf->defines, "-DVG_RELEASE_MODE \\\n" ); + vg_strcat( &conf->defines, "-DVG_ENGINE \\\n" ); vg_strcat( &conf->defines, "\\\n" ); vg_strcat( &conf->include, "-I. -I./vg -I./vg/dep " ); + vg_strcat( &conf->library, "-L. -L/usr/lib/x86_64-linux-gnu " ); + vg_strcat( &conf->link, "-lm " ); + /* compile all the components * ----------------------------------------------------------------------- */ vg_str components = {0}; @@ -461,25 +444,18 @@ vg_make_app( struct vg_project *proj, //denv.optimization = 3; /* external dependencies */ - struct compile_result depencies = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_depencies.c", - "vg_deps", k_obj_type_obj ); + struct compile_result depencies = vg_compiler_run( &vg_proj, &denv, conf, "vg/unit_thirdparty.c", "vg_deps", k_obj_type_obj ); vg_strcatf( &components, "%s ", depencies.path ); - /* glad */ - struct compile_result glad = vg_compiler_run( &vg_proj, &denv, conf, "vg/dep/glad/glad.c", - "vg_glad", k_obj_type_obj ); - vg_strcatf( &components, "%s ", glad.path ); - /* core engine */ - struct compile_result vg = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_engine.c", - "vg_engine_core", k_obj_type_obj ); + struct compile_result vg = vg_compiler_run( &vg_proj, &denv, conf, "vg/unit_engine.c", "vg_engine_core", k_obj_type_obj ); vg_strcatf( &components, "%s ", vg.path ); /* steamworks */ if( vg_conf->steam_api ) { - struct compile_result steam = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_steam2.c", "vg_steam", k_obj_type_obj ); - vg_strcatf( &components, "%s ", steam.path ); + //struct compile_result steam = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_steam2.c", "vg_steam", k_obj_type_obj ); + //vg_strcatf( &components, "%s ", steam.path ); if( env->platform == k_platform_linux ) { @@ -496,8 +472,8 @@ vg_make_app( struct vg_project *proj, } /* link */ - vg_strcat( &conf->library, "-L. -L/usr/lib " ); - vg_strcat( &conf->link, "-lm " ); + /* TODO: Could be /usr/lib/x86_64-linux-gnu or /usr/lib + */ if( env->platform == k_platform_linux ) { @@ -525,3 +501,33 @@ void vg_add_controller_database( struct vg_project *proj ) { vg_add_blob( proj, "vg/submodules/SDL_GameControllerDB/gamecontrollerdb.txt", "" ); } + +void vg_build_scripts(void); +int main( int argc, const c8 *argv[] ) +{ + _vg_log_pre_init(); + _vg_opt_init( argc, argv ); + + if( vg_long_opt( "windows", "Build for windows" ) ) + _vg_common_env.platform = k_platform_windows; + + if( vg_long_opt( "tsan", "Use Thread Sanitizer instead of Adress Sanitizer" ) ) + { + _vg_common_env.debug_asan = 0; + _vg_common_env.thread_san = 1; + } + + if( vg_opt( 'O', "Build with optimisations" ) ) + { + _vg_common_env.optimization = 3; + _vg_common_env.debug_asan = 0; + _vg_common_env.thread_san = 0; + } + + vg_build_scripts(); + + if( !_vg_opt_check() ) + return 0; +} + +#endif diff --git a/vg_build_font.h b/vg_build_font.h index 84af448..6f27360 100644 --- a/vg_build_font.h +++ b/vg_build_font.h @@ -1,10 +1,5 @@ -#include "vg_font.h" - -#define STB_IMAGE_IMPLEMENTATION -#include "submodules/stb/stb_image.h" - -void vg_build_font_face_run( vg_font_face *face, - char first, char last, i16 x, i16 y ) +#if defined( VG_IMPLEMENTATION ) +void vg_build_font_face_run( vg_font_face *face, char first, char last, i16 x, i16 y ) { u32 uf = *((u8 *)&first), ul = *((u8 *)&last); @@ -69,7 +64,7 @@ void vg_build_font_sheet( FILE *fp, char *name, const char *source ) u32 buff = 0; for( int b = 31; b >= 0; b-- ) { - buff |= data[pixel*4]>128?0x1< 128)? (0x1u<<(u32)b): 0; pixel++; if( pixel >= pixel_max ) @@ -158,3 +153,4 @@ void vg_build_default_font(void) fclose( fp ); } +#endif diff --git a/vg_build_utils_shader.h b/vg_build_utils_shader.h index 8376490..dc452fb 100644 --- a/vg_build_utils_shader.h +++ b/vg_build_utils_shader.h @@ -1,307 +1,359 @@ -#define STB_INCLUDE_IMPLEMENTATION -#define STB_INCLUDE_LINE_GLSL -#include "submodules/stb/stb_include.h" +#if defined( VG_IMPLEMENTATION ) struct { - struct uniform - { - char name[32]; - char type[20]; - char uniform_code_id[128]; + vg_str c_structs, + c_funcs, + c_enum; - int array; + struct multi_string + { + vg_str str; + u32 ghost_count; } - uniform_buffer[100]; - - int uniform_count, - uniform_uid; - char shader_dir[ 256 ]; - char current_shader_name[ 128 ]; + names, + infos, + glsl; - vg_str code_register, - code_link, - code_function_body; + u32 name_count, shader_count; bool init; - const char *preprocessed_dir; -} -static vg_shaderbuild; - -static void vg_shader_set_include_dir( char *dir ) -{ - strcpy( vg_shaderbuild.shader_dir, dir ); + char shader_dir[ 256 ]; } +_shadercomp; -static void parse_uniform_name( char *start, struct uniform *uf ) +static void _shadercomp_pstr( struct multi_string *ms, vg_str *ref_place, const c8 *str, u32 length ) { - uf->array = 0; - int type_set = 0; - - for( int i=0;; i++ ) + if( ref_place ) { - if( start[i] == '\0' ) - break; - - if( start[i] == ';' ) - { - start[i] = '\0'; - vg_strncpy( start, uf->name, sizeof(uf->name), - k_strncpy_always_add_null ); - } - - if( start[i] == '[' ) - { - start[i] = '\0'; - vg_strncpy( start, uf->name, sizeof(uf->name), - k_strncpy_always_add_null ); - uf->array = 1; - } - - if( start[i] == ' ' ) - { - start[i] = '\0'; - - if( !type_set ) - { - vg_strncpy( start, uf->type, sizeof(uf->type), - k_strncpy_always_add_null ); - type_set = 1; - } - start = start+i+1; - i=0; - } + vg_strcatu64( ref_place, ms->str.i - ms->ghost_count, 10 ); + vg_strcat( ref_place, "," ); } - snprintf( uf->uniform_code_id, 64, "_uniform_%s_%s", vg_shaderbuild.current_shader_name, uf->name ); + if( str ) + { + vg_strcat_limit( &ms->str, str, length ); + vg_strcat( &ms->str, "\\0" ); + ms->ghost_count ++; + } } -static int compile_subshader( vg_str *str, char *name, const char *path_write_full ) +static int compile_subshader( c8 *file, c8 *shader_name ) { char error[256]; - char *full = stb_include_file( name, "", vg_shaderbuild.shader_dir, error ); + char *full = stb_include_file( file, "", _shadercomp.shader_dir, error ); if( !full ) { - fprintf( stderr, "stb_include_file error:\n%s\n", error ); + vg_error( "stb_include_file error:\n%s\n", error ); return 0; } - else + + enum + { + k_nothing, + k_type, + k_name, + k_maybe_uniform_block + } + state = k_nothing; + + bool uniform = 0; + u32 t_length = 0, t_start = 0; + u32 type_index = 0; + c8 comment = 0; + + // glsl types have to be ordered by string length!!!!!!!! + const c8 *glsl_defs[][6] = + { + { "int", "int b", "glUniform1i(", ",b);" }, + { "vec2", "v2f v", "glUniform2fv(", ",1,v);" }, + { "vec3", "v3f v", "glUniform3fv(", ",1,v);" }, + { "vec4", "v4f v", "glUniform4fv(", ",1,v);" }, + { "bool", "int b", "glUniform1i(", ",b);" }, + { "mat2", "m2x2f m", "glUniformMatrix2fv(", ",1,GL_FALSE,(f32*)m);" }, + { "mat3", "m3x3f m", "glUniformMatrix3fv(", ",1,GL_FALSE,(f32*)m);" }, + { "mat4", "m4x4f m", "glUniformMatrix4fv(", ",1,GL_FALSE,(f32*)m);" }, + { "float", "f32 f", "glUniform1f(", ",f);" }, + { "mat4x3", "m4x3f m", "glUniformMatrix4x3fv(", ",1,GL_FALSE,(f32*)m);", + "m4x3f *m, u32 count", ",count,GL_FALSE,(f32*)m);"}, // 1,3 -> 4,5 + { "sampler2D", "int i", "glUniform1i(", ",i);" }, + { "usampler3D", "int i", "glUniform1i(", ",i);" }, + { "samplerCube", "int i", "glUniform1i(", ",i);" }, + { "samplerBuffer","int i", "glUniform1i(", ",i);" }, + }; + + _shadercomp_pstr( &_shadercomp.glsl, &_shadercomp.c_structs, NULL, 0 ); + vg_strcatch( &_shadercomp.glsl.str, '"' ); + _shadercomp.glsl.ghost_count += 1; + + for( u32 i=0; 1; i ++ ) { - if( path_write_full ) + c8 c = full[i]; + if( !c ) + break; + + if( comment == '/' ) + { + if( c == '\n' ) comment = 0; + continue; + } + if( comment == '*' ) { - FILE *fp = fopen( path_write_full, "w" ); - if( !fp ) + if( (c == '*') && (full[i+1] == '/') ) { - free( full ); - fprintf( stderr, "Cant open %s\n", path_write_full ); - return 0; + comment = 0; + i ++; } - fputs( "#version 330 core\n", fp ); - fputs( full, fp ); - fclose( fp ); + continue; + } + if( (c == '/') && ((full[i+1] == '/') || (full[i+1] == '*')) ) + { + comment = full[i+1]; + i ++; + continue; } - vg_strcatf( str, "{\n" - ".orig_file = \"%s\",\n" - ".static_src = \n", - name ); + if( c == '\n' ) + { + vg_strcat( &_shadercomp.glsl.str, "\\n\"\n\"" ); + _shadercomp.glsl.ghost_count += 4; + } + else + vg_strcatch( &_shadercomp.glsl.str, c ); - char *cur = full, *start = full; - while( 1 ) + if( c==' '||c=='\t'||c=='\r'||c=='\n'||c=='{'||c=='}'||c==';'||c=='[' ) { - char c = *cur; - if( c == '\n' || c == '\0' ) + if( t_length ) { - *cur = '\0'; - vg_strcatf( str, "\"" ); - vg_strcatf( str, start ); - - if( !strncmp(start,"uniform",7) ) + if( state == k_nothing ) { - start += 8; - struct uniform *uf = &vg_shaderbuild.uniform_buffer[ vg_shaderbuild.uniform_count ++ ]; - parse_uniform_name( start, uf ); + if( !strncmp( full+t_start, "uniform", t_length ) ) + { + state = k_type; + } } - - if( c == '\0' ) + else if( state == k_type ) { - vg_strcatf( str, "\"" ); - break; + // check type + type_index = 9999; + + for( u32 j=0; juniform_code_id ); - vg_strcatf( c_body, "GLuint %s;\n", uf->uniform_code_id ); - } - - struct type_info - { - const char *glsl_type, - *args, - *gl_call_pattern; - } - types[] = - { - { "float", "f32 f", "glUniform1f(%s,f);" }, - { "bool", "int b", "glUniform1i(%s,b);" }, - { "int", "int b", "glUniform1i(%s,b);" }, - { "vec2", "v2f v", "glUniform2fv(%s,1,v);" }, - { "vec3", "v3f v", "glUniform3fv(%s,1,v);" }, - { "vec4", "v4f v", "glUniform4fv(%s,1,v);" }, - { "sampler2D", "int i", "glUniform1i(%s,i);" }, - { "samplerCube", "int i", "glUniform1i(%s,i);" }, - { "mat2", "m2x2f m", "glUniformMatrix2fv(%s,1,GL_FALSE,(f32*)m);" }, - { "mat4x3", "m4x3f m", "glUniformMatrix4x3fv(%s,1,GL_FALSE,(f32*)m);" }, - { "mat3", "m3x3f m", "glUniformMatrix3fv(%s,1,GL_FALSE,(f32*)m);" }, - { "mat4", "m4x4f m", "glUniformMatrix4fv(%s,1,GL_FALSE,(f32*)m);" }, - }; - - for( int i=0; iarray ) - continue; + /* [shader] *uniform* ( ... ) */ + vg_strcat( &_shadercomp.c_structs, "{" ); + _shadercomp_pstr( &_shadercomp.infos, &_shadercomp.c_structs, name, 0 ); + + /* vs */ + vg_strcat( &_shadercomp.c_structs, "{" ); + if( !compile_subshader( src_vert, name ) ) {} + _shadercomp_pstr( &_shadercomp.infos, &_shadercomp.c_structs, src_vert, 0 ); + + /* fs */ + vg_strcat( &_shadercomp.c_structs, "},{" ); + if( !compile_subshader( src_frag, name ) ) {} + _shadercomp_pstr( &_shadercomp.infos, &_shadercomp.c_structs, src_frag, 0 ); + + u32 u_end = _shadercomp.name_count; + vg_strcat( &_shadercomp.c_structs, "}," ); + vg_strcatu64( &_shadercomp.c_structs, u_start, 10 ); + vg_strcat( &_shadercomp.c_structs, "," ); + vg_strcatu64( &_shadercomp.c_structs, u_offset, 10 ); + vg_strcat( &_shadercomp.c_structs, "," ); + vg_strcatu64( &_shadercomp.c_structs, (u_end - u_start)-1, 10 ); + vg_strcat( &_shadercomp.c_structs, "},\n" ); + + _shadercomp.shader_count ++; + return 1; +} - for( int j=0; jglsl_type, uf->type ) ) - { - fprintf( header, "static inline void shader_%s_%s(%s)\n{\n", - name, uf->name, inf->args ); - fprintf( header, " " ); - fprintf( header, inf->gl_call_pattern, uf->uniform_code_id ); - fprintf( header, "\n}\n" ); - } - } - } +void vg_build_shader_impl(void) +{ + vg_syscall( "mkdir -p src.generated" ); - vg_strcatf( c_reg, " vg_shader_register( &_shader_%s );\n", name ); - fprintf( header, "static inline void shader_%s_use(void);\n", name ); - fprintf( header, "static inline void shader_%s_use(void)\n{\n", name ); - fprintf( header, " glUseProgram(_shader_%s.id);\n}\n", name ); + const c8 *h_path = "src.generated/vg.shaders.h"; - for( int i=0; iname, - name, uf->name ); + /* IMPLEMENTATION ----------------------------------------------------------------- */ + vg_str names_def; + vg_strnull( &names_def, NULL, 0 ); + vg_strcat( &names_def, "GLuint _vg_shader_names[ " ); + vg_strcatu64( &names_def, _shadercomp.name_count, 10 ); + vg_strcat( &names_def, " ];\n" ); + + vg_str shaders_def; + vg_strnull( &shaders_def, NULL, 0 ); + vg_strcat( &shaders_def, "struct vg_shader _vg_shaders[ " ); + vg_strcatu64( &shaders_def, _shadercomp.shader_count, 10 ); + vg_strcat( &shaders_def, " ]" ); + + fputs( "#ifdef VG_IMPLEMENTATION\n", fp ); + fputs( names_def.buffer, fp ); + + fputs( shaders_def.buffer, fp ); + fputs( " = {", fp ); + if( _shadercomp.c_structs.buffer ) fputs( _shadercomp.c_structs.buffer, fp ); + fputs( "};\n", fp ); + + fputs( "const c8 *_vg_shaders_uniform_names = \"", fp ); + if( _shadercomp.names.str.buffer ) fputs( _shadercomp.names.str.buffer, fp ); + fputs( "\";\n", fp ); + + fputs( "const c8 *_vg_shaders_infos = \"", fp ); + if( _shadercomp.infos.str.buffer ) fputs( _shadercomp.infos.str.buffer, fp ); + fputs( "\";\n", fp ); + + fputs( "const c8 *_vg_shaders_glsl = ", fp ); + if( _shadercomp.glsl.str.buffer ) fputs( _shadercomp.glsl.str.buffer, fp ); + fputs( ";\n", fp ); + + fputs( "#else\n", fp ); /* HEADER ----------------------------------------------------------------- */ + + fputs( "extern const c8 *_vg_shaders_uniform_names;\n", fp ); + fputs( "extern const c8 *_vg_shaders_infos;\n", fp ); + fputs( "extern const c8 *_vg_shaders_glsl;\n", fp ); + fputs( "extern ", fp ); + fputs( names_def.buffer, fp ); + + fputs( "extern ", fp ); + fputs( shaders_def.buffer, fp ); + fputs( ";\n", fp ); + + fputs( "enum e_shader_gl_name\n{\n", fp ); + if( _shadercomp.c_enum.buffer ) fputs( _shadercomp.c_enum.buffer, fp ); + fputs( "};\n", fp ); + + if( _shadercomp.c_funcs.buffer ) fputs( _shadercomp.c_funcs.buffer, fp ); + + fputs( "#endif\n", fp ); + fclose( fp ); } - - fclose( header ); - return 1; + else vg_fatal_error( "Cant open '%s'!\n", h_path ); } -void vg_build_postprocess_shaders(void) -{ - _S( "blit", "../vg/shaders/blit.vs", "../vg/shaders/blit_tex.fs" ); - _S( "blitblur", "../vg/shaders/blit.vs", "../vg/shaders/blit_blur.fs" ); - _S( "blitcolour","../vg/shaders/blit.vs", "../vg/shaders/blit_colour.fs" ); -} +#endif diff --git a/vg_bvh.c b/vg_bvh.c index 9127be5..9da3fad 100644 --- a/vg_bvh.c +++ b/vg_bvh.c @@ -1,22 +1,17 @@ -#include "vg_bvh.h" -#include "vg_mem.h" -#include "vg_m.h" -#include "vg_lines.h" -#include "vg_log.h" - -void bh_update_bounds( bh_tree *bh, u32 inode ){ +static void bh_update_bounds( bh_tree *bh, u32 inode ) +{ bh_node *node = &bh->nodes[ inode ]; - box_init_inf( node->bbx ); - for( u32 i=0; icount; i++ ){ + for( u32 i=0; icount; i++ ) + { u32 idx = node->start+i; bh->system->expand_bound( bh->user, node->bbx, idx ); } } -void bh_subdivide( bh_tree *bh, u32 inode ){ +static void bh_subdivide( bh_tree *bh, u32 inode ) +{ bh_node *node = &bh->nodes[ inode ]; - if( node->count <= bh->max_per_leaf ) return; @@ -40,26 +35,26 @@ void bh_subdivide( bh_tree *bh, u32 inode ){ i32 i = node->start, j = i + node->count-1; - while( i <= j ){ + while( i <= j ) + { f32 centroid = bh->system->item_centroid( bh->user, i, axis ); - if( centroid < split ) i ++; - else{ + else + { bh->system->item_swap( bh->user, i, j ); j --; } } u32 left_count = i - node->start; - if( left_count == 0 || left_count == node->count ) return; + if( left_count == 0 || left_count == node->count ) + return; u32 il = bh->node_count ++, ir = bh->node_count ++; - bh_node *lnode = &bh->nodes[il], *rnode = &bh->nodes[ir]; - lnode->start = node->start; lnode->count = left_count; rnode->start = i; @@ -75,7 +70,7 @@ void bh_subdivide( bh_tree *bh, u32 inode ){ bh_subdivide( bh, ir ); } -void bh_rebuild( bh_tree *bh, u32 item_count ) +static void bh_rebuild( bh_tree *bh, u32 item_count ) { bh_node *root = &bh->nodes[0]; bh->node_count = 1; @@ -91,31 +86,25 @@ void bh_rebuild( bh_tree *bh, u32 item_count ) bh_subdivide( bh, 0 ); } -bh_tree *bh_create( vg_stack_allocator *stack, bh_system *system, void *user, u32 item_count, u32 max_per_leaf ) +VG_TIER_1 void bh_create( bh_tree *bh, bh_system *system, void *user, u32 item_count, + u32 max_per_leaf, vg_stack_allocator *stack ) { - u32 alloc_count = VG_MAX( 1, item_count ); + vg_zero_mem( bh, sizeof(bh_tree) ); - u32 totsize = sizeof(bh_tree) + sizeof(bh_node)*(alloc_count*2-1); - bh_tree *bh = stack? vg_stack_allocate( stack, totsize, 8, "BVH Tree" ): vg_malloc( totsize ); + u32 alloc_count = VG_MAX( 1, item_count ); + i32 max_size = sizeof(bh_node)*(alloc_count*2-1); + bh->nodes = vg_stack_allocate( stack, max_size, 8, "BVH Tree" ); bh->system = system; bh->user = user; bh->max_per_leaf = max_per_leaf; bh_rebuild( bh, item_count ); - totsize = sizeof(bh_tree) + sizeof(bh_node) * bh->node_count; - - if( stack ) bh = vg_stack_resize_last( stack, totsize ); - else bh = vg_realloc( bh, totsize ); - + i32 used_size = sizeof(bh_node) * bh->node_count; + vg_stack_extend_last( stack, used_size - max_size ); vg_success( "BVH done, size: %u/%u\n", bh->node_count, (alloc_count*2-1) ); - return bh; } -/* - * Draw items in this leaf node. - * *item_debug() must be set! - */ -void bh_debug_leaf( bh_tree *bh, bh_node *node ) +VG_TIER_0 void bh_debug_leaf( bh_tree *bh, bh_node *node ) { vg_line_boxf( node->bbx, 0xff00ff00 ); @@ -132,27 +121,25 @@ void bh_debug_leaf( bh_tree *bh, bh_node *node ) /* * Trace the bh tree all the way down to the leaf nodes where pos is inside */ -void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour ) +VG_TIER_0 void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour ) { bh_node *node = &bh->nodes[ inode ]; - if( (pos[0] >= node->bbx[0][0] && pos[0] <= node->bbx[1][0]) && (pos[2] >= node->bbx[0][2] && pos[2] <= node->bbx[1][2]) ) { - if( !node->count ){ + if( !node->count ) + { vg_line_boxf( node->bbx, colour ); - bh_debug_trace( bh, node->il, pos, colour ); bh_debug_trace( bh, node->ir, pos, colour ); } - else{ + else if( bh->system->item_debug ) bh_debug_leaf( bh, node ); - } } } -void bh_iter_init_generic( i32 root, bh_iter *it ) +VG_TIER_0 void bh_iter_init_generic( i32 root, bh_iter *it ) { it->stack[0].id = root; it->stack[0].depth = 0; @@ -160,25 +147,23 @@ void bh_iter_init_generic( i32 root, bh_iter *it ) it->i = 0; } -void bh_iter_init_box( i32 root, bh_iter *it, boxf box ) +VG_TIER_0 void bh_iter_init_box( i32 root, bh_iter *it, boxf box ) { bh_iter_init_generic( root, it ); it->query = k_bh_query_box; - box_copy( box, it->box.box ); } -void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist ) +VG_TIER_0 void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist ) { bh_iter_init_generic( root, it ); it->query = k_bh_query_ray; - v3_div( (v3f){1.0f,1.0f,1.0f}, dir, it->ray.inv_dir ); v3_copy( co, it->ray.co ); it->ray.max_dist = max_dist; } -void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range ) +VG_TIER_0 void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range ) { bh_iter_init_generic( root, it ); it->query = k_bh_query_range; @@ -187,14 +172,10 @@ void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range ) it->range.dist_sqr = range*range; } -/* NOTE: does not compute anything beyond the leaf level. element level tests - * should be implemented by the users code. - * - * this is like a 'broad phase only' deal. - */ -i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ) +VG_TIER_0 i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ) { - while( it->depth >= 0 ){ + while( it->depth >= 0 ) + { bh_node *inode = &bh->nodes[ it->stack[it->depth].id ]; /* Only process overlapping nodes */ @@ -202,39 +183,45 @@ i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ) if( it->i ) /* already checked */ q = 1; - else{ + else + { if( it->query == k_bh_query_box ) q = box_overlap( inode->bbx, it->box.box ); else if( it->query == k_bh_query_ray ) - q = ray_aabb1( inode->bbx, it->ray.co, - it->ray.inv_dir, it->ray.max_dist ); - else { + q = ray_aabb1( inode->bbx, it->ray.co, it->ray.inv_dir, it->ray.max_dist ); + else + { v3f nearest; closest_point_aabb( it->range.co, inode->bbx, nearest ); - if( v3_dist2( nearest, it->range.co ) <= it->range.dist_sqr ) q = 1; } } - if( !q ){ + if( !q ) + { it->depth --; continue; } - if( inode->count ){ - if( it->i < inode->count ){ + if( inode->count ) + { + if( it->i < inode->count ) + { *em = inode->start+it->i; it->i ++; return 1; } - else{ + else + { it->depth --; it->i = 0; } } - else{ - if( it->depth+1 >= VG_ARRAY_LEN(it->stack) ){ + else + { + if( it->depth+1 >= VG_ARRAY_LEN(it->stack) ) + { vg_error( "Maximum stack reached!\n" ); return 0; } @@ -245,39 +232,43 @@ i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ) it->i = 0; } } - return 0; } -int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist ) +VG_TIER_0 i32 bh_closest_point( bh_tree *bh, v3f pos, v3f closest, f32 max_dist ) { if( bh->node_count < 2 ) return -1; max_dist = max_dist*max_dist; - int queue[ 128 ], + i32 queue[ 128 ], depth = 0, best_item = -1; queue[0] = 0; - while( depth >= 0 ){ + while( depth >= 0 ) + { bh_node *inode = &bh->nodes[ queue[depth] ]; v3f p1; closest_point_aabb( pos, inode->bbx, p1 ); /* branch into node if its closer than current best */ - float node_dist = v3_dist2( pos, p1 ); - if( node_dist < max_dist ){ - if( inode->count ){ - for( int i=0; icount; i++ ){ + f32 node_dist = v3_dist2( pos, p1 ); + if( node_dist < max_dist ) + { + if( inode->count ) + { + for( i32 i=0; icount; i++ ) + { v3f p2; bh->system->item_closest( bh->user, inode->start+i, pos, p2 ); - float item_dist = v3_dist2( pos, p2 ); - if( item_dist < max_dist ){ + f32 item_dist = v3_dist2( pos, p2 ); + if( item_dist < max_dist ) + { max_dist = item_dist; v3_copy( p2, closest ); best_item = inode->start+i; @@ -286,10 +277,10 @@ int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist ) depth --; } - else{ + else + { queue[depth] = inode->il; queue[depth+1] = inode->ir; - depth ++; } } diff --git a/vg_bvh.h b/vg_bvh.h index 3592bc6..ec1bed3 100644 --- a/vg_bvh.h +++ b/vg_bvh.h @@ -1,68 +1,51 @@ -#pragma once -#include "vg_platform.h" -#include "vg_mem.h" -#include "vg_m.h" -#include "vg_lines.h" - -/* - * Usage: - * - * create a bh_system with functions filled out for expand, centroid, and swap. - * optionally include item_debug and cast_ray functions if needed, otherwise, - * set them to null - * - * create a bh_tree struct with: - * user: a pointer back the base of the data you are ordering - * system: the system we created above which will deal with the data - * - * call bh_create( bh_tree *bh, u32 item_count ) - * static int bh_ray( bh_tree *bh, u32 inode, v3f co, v3f dir, ray_hit *hit ) - * static int bh_select( bh_tree *bh, boxf box, u32 *buffer, int len ) - */ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_bvh.c" +#else typedef struct bh_node bh_node; typedef struct bh_tree bh_tree; typedef struct bh_system bh_system; typedef struct ray_hit ray_hit; -struct ray_hit{ - float dist; +struct ray_hit +{ + f32 dist; u32 *tri; v3f pos, normal; }; -struct bh_tree{ +struct bh_tree +{ u32 node_count; bh_system *system; void *user; u32 max_per_leaf; - struct bh_node{ + struct bh_node + { boxf bbx; /* if il is 0, this is a leaf */ - int il, count; - union{ int ir, start; }; + i32 il, count; + union{ i32 ir, start; }; } - nodes[]; + *nodes; }; -/* - * TODO: This might get ugly, but it would make sense to do codegen here - * - * eg.. #define BH_SYSTEM( FN_EXPAND, FN_CENTROID, FN_CLOSEST ... ) - * - * which macros out variants of the functions so we can give the compier - * a more solid thing to work with without function pointers inside - * hot loops. - */ +typedef void (*bh_expand_fn) ( void *user, boxf bound, u32 item_index ); +typedef f32 (*bh_centroid_fn)( void *user, u32 item_index, i32 axis ); +typedef void (*bh_closest_fn) ( void *user, u32 item_index, v3f point, v3f closest ); +typedef void (*bh_swap_fn) ( void *user, u32 ia, u32 ib ); +typedef void (*bh_debug_fn) ( void *user, u32 item_index ); +typedef void (*bh_raycast_fn) ( void *user, u32 index, v3f co, v3f dir, ray_hit *hit ); -struct bh_system{ - void (*expand_bound)( void *user, boxf bound, u32 item_index ); - float (*item_centroid)( void *user, u32 item_index, int axis ); - void (*item_closest)( void *user, u32 item_index, v3f point, v3f closest ); - void (*item_swap)( void *user, u32 ia, u32 ib ); +struct bh_system +{ + bh_expand_fn expand_bound; + bh_centroid_fn item_centroid; + bh_closest_fn item_closest; + bh_swap_fn item_swap; /* * Optional: @@ -70,52 +53,53 @@ struct bh_system{ * cast_ray - shoot a ray against the object, if this is not set, * raycasts will simply return the hit on the bvh node */ - - void (*item_debug)( void *user, u32 item_index ); - int (*cast_ray)( void *user, u32 index, v3f co, v3f dir, ray_hit *hit ); + bh_debug_fn item_debug; + bh_raycast_fn cast_ray; }; -void bh_update_bounds( bh_tree *bh, u32 inode ); -void bh_subdivide( bh_tree *bh, u32 inode ); -void bh_rebuild( bh_tree *bh, u32 item_count ); - -bh_tree *bh_create( vg_stack_allocator *stack, bh_system *system, void *user, u32 item_count, u32 max_per_leaf ); - - -void bh_debug_leaf( bh_tree *bh, bh_node *node ); +VG_TIER_1 void bh_create( bh_tree *bh, bh_system *system, void *user, u32 item_count, + u32 max_per_leaf, vg_stack_allocator *stack ); +VG_TIER_0 void bh_debug_leaf( bh_tree *bh, bh_node *node ); /* * Trace the bh tree all the way down to the leaf nodes where pos is inside */ -void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour ); +VG_TIER_0 void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour ); typedef struct bh_iter bh_iter; -struct bh_iter{ - struct { +struct bh_iter +{ + struct + { i32 id, depth; } stack[64]; - enum bh_query_type{ + enum bh_query_type + { k_bh_query_box, k_bh_query_ray, k_bh_query_range } query; - union{ - struct{ + union + { + struct + { boxf box; } box; - struct{ + struct + { v3f co, inv_dir; f32 max_dist; } ray; - struct { + struct + { v3f co; f32 dist_sqr; } @@ -125,9 +109,11 @@ struct bh_iter{ i32 depth, i; }; -void bh_iter_init_generic( i32 root, bh_iter *it ); -void bh_iter_init_box( i32 root, bh_iter *it, boxf box ); -void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist ); -void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range ); -i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ); -int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist ); +VG_TIER_0 void bh_iter_init_generic( i32 root, bh_iter *it ); +VG_TIER_0 void bh_iter_init_box( i32 root, bh_iter *it, boxf box ); +VG_TIER_0 void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist ); +VG_TIER_0 void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range ); +VG_TIER_0 i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ); +VG_TIER_0 int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist ); + +#endif diff --git a/vg_camera.c b/vg_camera.c index cb44a56..5bb432f 100644 --- a/vg_camera.c +++ b/vg_camera.c @@ -1,7 +1,3 @@ -#include "vg_camera.h" -#include "vg_m.h" -#include "vg_engine.h" - void vg_camera_lerp_angles( v3f a, v3f b, f32 t, v3f d ) { d[0] = vg_alerpf( a[0], b[0], t ); diff --git a/vg_camera.h b/vg_camera.h index a0846b0..c4f88f6 100644 --- a/vg_camera.h +++ b/vg_camera.h @@ -1,6 +1,6 @@ -#pragma once - -#include "vg_m.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_camera.c" +#else typedef struct vg_camera vg_camera; struct vg_camera @@ -49,3 +49,5 @@ void vg_camera_update_projection( vg_camera *cam, f32 vw, f32 vh ); * 4) [projection matrix, view matrix] -> previous pv, new pv */ void vg_camera_finalize( vg_camera *cam ); + +#endif diff --git a/vg_console.c b/vg_console.c index cd7d07c..4454630 100644 --- a/vg_console.c +++ b/vg_console.c @@ -1,20 +1,10 @@ -#ifdef VG_ENGINE -#include "vg_engine.h" -#include "vg_ui/imgui.h" -#endif - -#include "vg_console.h" -#include "vg_log.h" -#include "vg_string.h" -#include - struct vg_console vg_console; void vg_console_reg_var( const char *alias, void *ptr, enum vg_var_dtype type, u32 flags ) { -#ifdef VG_ENGINE +#if defined( VG_ENGINE ) VG_ASSERT( vg_console.registration_blocked==0 ); - THREAD_1; + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) ); #endif VG_ASSERT( vg_console.var_count < VG_ARRAY_LEN(vg_console.vars) ); @@ -23,15 +13,14 @@ void vg_console_reg_var( const char *alias, void *ptr, enum vg_var_dtype type, u var->data = ptr; var->data_type = type; var->flags = flags; - - vg_info( "Console variable '%s' registered\n", alias ); } void vg_console_reg_cmd( const char *alias, int (*function)(int argc, const char *argv[]), void (*poll_suggest)(int argc, const char *argv[]) ) { -#ifdef VG_ENGINE +#if defined( VG_ENGINE ) VG_ASSERT( vg_console.registration_blocked==0 ); + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) ); #endif VG_ASSERT( vg_console.function_count < VG_ARRAY_LEN(vg_console.functions) ); @@ -40,8 +29,6 @@ void vg_console_reg_cmd( const char *alias, int (*function)(int argc, const char cmd->function = function; cmd->poll_suggest = poll_suggest; cmd->name = alias; - - vg_info( "Console function '%s' registered\n", alias ); } static int _vg_console_list( int argc, char const *argv[] ) @@ -61,17 +48,10 @@ static int _vg_console_list( int argc, char const *argv[] ) return 0; } +#if defined( VG_ENGINE ) static void vg_console_write_persistent(void) { -#ifdef VG_ENGINE - enum engine_status status = SDL_AtomicGet( &vg.engine_status ); - if( status < k_engine_status_running ) - { - vg_low( "Not writing auto.conf due to early exit.\n" ); - return; - } -#endif - + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) ); FILE *fp = fopen( "cfg/auto.conf", "w" ); if( !fp ) { @@ -102,12 +82,6 @@ static void vg_console_write_persistent(void) fclose( fp ); } - -#ifdef VG_ENGINE -static void _vg_console_free(void) -{ - vg_console_write_persistent(); -} #endif /* @@ -286,338 +260,6 @@ void vg_execute_console_input( const char *cmd, bool silent, bool cheat_override vg_error( "No command/var named '%s'. Use 'list' to view all\n",args[0]); } -#ifdef VG_ENGINE -u32 str_lev_distance( const char *s1, const char *s2 ) -{ - u32 m = strlen( s1 ), - n = strlen( s2 ); - - if( m==0 ) return n; - if( n==0 ) return m; - - u32 costs[ 256 ]; - - for( u32 k=0; k<=n; k++ ) - costs[k] = k; - - u32 i = 0; - for( u32 i=0; i=0; j -- ) - if( score > vg_console.suggestions[j].lev_score ) - best_pos = j; - - /* insert if good score */ - if( best_pos < VG_ARRAY_LEN( vg_console.suggestions ) ) - { - int start = VG_MIN( vg_console.suggestion_count, VG_ARRAY_LEN( vg_console.suggestions )-1 ); - for( int j=start; j>best_pos; j -- ) - vg_console.suggestions[j] = vg_console.suggestions[j-1]; - - vg_console.suggestions[ best_pos ].str = str; - vg_console.suggestions[ best_pos ].len = strlen( str ); - vg_console.suggestions[ best_pos ].lev_score = score; - - if( vg_console.suggestion_count < VG_ARRAY_LEN( vg_console.suggestions ) ) - vg_console.suggestion_count ++; - } -} - -static void console_update_suggestions( ui_context *ctx ) -{ - if( ctx->focused_control_type != k_ui_control_textbox || - ctx->textbuf != vg_console.input ) - return; - - vg_console.suggestion_count = 0; - vg_console.suggestion_select = -1; - vg_console.suggestion_maxlen = 0; - - /* - * - must be typing something - * - must be at the end - * - prev char must not be a whitespace - * - cursors should match - */ - - if( ctx->textbox.cursor_pos == 0 ) return; - if( ctx->textbox.cursor_pos != ctx->textbox.cursor_user ) return; - if( vg_console.input[ ctx->textbox.cursor_pos ] != '\0' ) return; - - if( (vg_console.input[ ctx->textbox.cursor_pos -1 ] == ' ') || - (vg_console.input[ ctx->textbox.cursor_pos -1 ] == '\t') ) - return; - - char temp[128]; - const char *args[32]; - - int token_count = vg_console_tokenize( vg_console.input, temp, args ); - if( !token_count ) return; - vg_console.suggestion_pastepos = args[token_count-1]-temp; - - /* Score all our commands and cvars */ - if( token_count == 1 ) - { - for( int i=0; iname, args[0], 1 ); - } - - for( int i=0; iname, args[0], 1 ); - } - } - else - { - vg_cmd *cmd = vg_console_match_cmd( args[0] ); - vg_var *var = vg_console_match_var( args[0] ); - - if( cmd ) - if( cmd->poll_suggest ) - cmd->poll_suggest( token_count-1, &args[1] ); - } - - /* some post processing */ - for( int i=0; itextbox.cursor_user, - &ctx->textbox.cursor_pos, 10000, 1 ); - } - else - { - strncpy( target, - vg_console.suggestions[ vg_console.suggestion_select ].str, - VG_ARRAY_LEN( vg_console.input )-1 ); - - _ui_textbox_move_cursor( ctx, - &ctx->textbox.cursor_user, - &ctx->textbox.cursor_pos, 10000, 1 ); - _ui_textbox_put_char( ctx, ' ' ); - } -} - -static void _console_suggest_store_normal(void) -{ - if( vg_console.suggestion_select == -1 ) - { - char *target = &vg_console.input[ vg_console.suggestion_pastepos ]; - strcpy( vg_console.input_copy, target ); - } -} - -void console_suggest_next( ui_context *ctx ) -{ - if( vg_console.suggestion_count ) - { - _console_suggest_store_normal(); - - vg_console.suggestion_select ++; - - if( vg_console.suggestion_select >= vg_console.suggestion_count ) - vg_console.suggestion_select = -1; - - _console_fetch_suggestion( ctx ); - } -} - -void console_suggest_prev( ui_context *ctx ) -{ - if( vg_console.suggestion_count ) - { - _console_suggest_store_normal(); - - vg_console.suggestion_select --; - - if( vg_console.suggestion_select < -1 ) - vg_console.suggestion_select = vg_console.suggestion_count-1; - - _console_fetch_suggestion( ctx ); - } -} - -static void _vg_console_on_update( ui_context *ctx, char *buf, u32 len, void *userdata ) -{ - if( buf == vg_console.input ) - { - console_update_suggestions( ctx ); - } -} - -static void console_history_get( char* buf, int entry_num ) -{ - if( !vg_console.history_count ) - return; - - int offset = VG_MIN( entry_num, vg_console.history_count -1 ), - pick = (vg_console.history_last - offset) % - VG_ARRAY_LEN( vg_console.history ); - strcpy( buf, vg_console.history[ pick ] ); -} - -static void _vg_console_on_up( ui_context *ctx, char *buf, u32 len, void *userdata ) -{ - if( buf == vg_console.input ) - { - vg_console.history_pos = - VG_MAX - ( - 0, - VG_MIN - ( - vg_console.history_pos+1, - VG_MIN - ( - VG_ARRAY_LEN( vg_console.history ), - vg_console.history_count - 1 - ) - ) - ); - - console_history_get( vg_console.input, vg_console.history_pos ); - _ui_textbox_move_cursor( ctx, - &ctx->textbox.cursor_user, - &ctx->textbox.cursor_pos, - VG_ARRAY_LEN(vg_console.input)-1, 1 ); - } -} - -static void _vg_console_on_down( ui_context *ctx, char *buf, u32 len, void *userdata ) -{ - if( buf == vg_console.input ) - { - vg_console.history_pos = VG_MAX( 0, vg_console.history_pos-1 ); - console_history_get( vg_console.input, vg_console.history_pos ); - - _ui_textbox_move_cursor( ctx, - &ctx->textbox.cursor_user, - &ctx->textbox.cursor_pos, - VG_ARRAY_LEN(vg_console.input)-1, 1 ); - } -} - -static void _vg_console_on_enter( ui_context *ctx, char *buf, u32 len, void *userdata ) -{ - if( buf == vg_console.input ) - { - if( !strlen( vg_console.input ) ) - return; - - vg_info( "%s\n", vg_console.input ); - - if( strcmp( vg_console.input, - vg_console.history[ vg_console.history_last ]) ) - { - vg_console.history_last = ( vg_console.history_last + 1) % - VG_ARRAY_LEN(vg_console.history ); - vg_console.history_count = - VG_MIN( VG_ARRAY_LEN( vg_console.history ), - vg_console.history_count + 1 ); - strcpy( vg_console.history[ vg_console.history_last ], - vg_console.input ); - } - - vg_console.history_pos = -1; - vg_execute_console_input( vg_console.input, 0, 0 ); - _ui_textbox_move_cursor( ctx, &ctx->textbox.cursor_user, &ctx->textbox.cursor_pos, -10000, 1 ); - vg_console.input[0] = '\0'; - console_update_suggestions( ctx ); - } - - vg_console.auto_focus = 1; -} -#endif - static int vg_console_exec( int argc, const char *argv[] ) { if( argc < 1 ) @@ -657,112 +299,20 @@ static int vg_console_exec( int argc, const char *argv[] ) } -void vg_console_init(void) +VG_API void _vg_console_register(void) { vg_console_reg_cmd( "list", _vg_console_list, NULL ); vg_console_reg_cmd( "exec", vg_console_exec, NULL ); -#ifdef VG_ENGINE - vg_console_reg_var( "cheats", &vg_console.cheats, k_var_dtype_i32, -#ifdef VG_DEVWINDOW - VG_VAR_PERSISTENT -#else - 0 -#endif - ); +#if defined( VG_ENGINE ) + vg_console_reg_var( "cheats", &vg_console.cheats, k_var_dtype_i32, 0 ); #endif } -#ifdef VG_ENGINE -void vg_console_load_autos(void) +VG_API void _vg_console_init(void) { +#if defined( VG_ENGINE ) vg_console.registration_blocked = 1; vg_console_exec( 2, (const char *[]){ "auto.conf", "silent" } ); -} - -void vg_console_draw( ui_context *ctx ) -{ - if( !vg_console.enabled ) return; - - if( SDL_LockMutex( vg_log.mutex ) ) - vg_fatal_error( "" ); - - int ptr = vg_log.log_line_current; - int const fh = ctx->font->sy, log_lines = 32; - int console_lines = VG_MIN( log_lines, vg_log.log_line_count ); - - ui_rect rect_log = { 0, 0, vg.window_x, log_lines*fh }, - rect_input = { 0, log_lines*fh + 1, vg.window_x, fh*2 }, - rect_line = { 0, 0, vg.window_x, fh }; - - /* - * log - */ - u32 bg_colour = (ui_colour( ctx, k_ui_bg )&0x00ffffff)|0x9f000000; - - ui_fill( ctx, rect_log, bg_colour ); - rect_line[1] = rect_log[1]+rect_log[3]-fh; - - for( int i=0; ifont->sx*vg_console.suggestion_pastepos; - rect_suggest[1] += rect_input[3]; - rect_suggest[2] = ctx->font->sx * vg_console.suggestion_maxlen; - rect_suggest[3] = vg_console.suggestion_count * fh; - - ui_fill( ctx, rect_suggest, bg_colour ); - - rect_suggest[3] = fh; - - for( int i=0; i -#include - static void vg_db_touch( vg_db *db, u16 cache_id ); /* util diff --git a/vg_db.h b/vg_db.h index 5505610..d4a9ab7 100644 --- a/vg_db.h +++ b/vg_db.h @@ -1,5 +1,6 @@ -#pragma once -#include "vg_m.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_db.c" +#else #define VG_PAGE_BITS 11 #define VG_PAGE_SIZE (0x1lu<flags & flags) == flags; + else + return 0; +} -u32 vg_thread_purpose(void) +VG_API void _vg_thread_set_flags( u32 flags ) { - return *((u32 *)SDL_TLSGet( vg.thread_purpose )); + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + VG_ASSERT( context ); + context->flags |= flags; } -#include -#include "vg/vg_ui/imgui.c" -#include "vg/vg_ui/imgui_impl_opengl.c" -#include "vg/vg_default_font.gc" +VG_API const c8 *_vg_thread_prefix(void) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + if( context ) + return context->log_prefix; + else + return "?"; +} -#include "vg_console.h" -#include "vg_profiler.h" -#include "vg_magi.h" -#include "vg_mem_view.h" -#ifndef VG_NO_AUDIO - #include "vg_audio.h" -#endif -#include "vg_shader.h" -#include "vg_tex.h" -#include "vg_input.h" -#include "vg_framebuffer.h" -#include "vg_render.h" -#include "vg_lines.h" -#include "vg_rigidbody_view.h" -#include "vg_loader.h" -#include "vg_opt.h" -#include "vg/vg_ui/imgui.h" - -/* Diagnostic */ -static struct vg_profile vg_prof_update = {.name="update()"}, - vg_prof_render = {.name="render()"}, - vg_prof_swap = {.name="swap"}; - -static f64 _vg_gameloop_budget() +VG_API void _vg_terminate(void) { - int frame_target = vg.display_refresh_rate; - if( vg.fps_limit > 0 ) frame_target = vg.fps_limit; - return (1.0/(f64)frame_target)*1000.0; + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) ); + vg_low( "vg: exiting\n" ); + + /* Shutdown */ + vg_magi_save(); + vg_async_queue_end( &vg.exit_tasks, k_async_quit_when_empty ); + while( vg_async_process_next_task( &vg.exit_tasks ) ) {} + _vg_steam_shutdown(); + _vg_window_shutdown(); + SDL_Quit(); + exit(0); } -struct vg_profile_set static _vg_prof_gameloop = +static int _vg_async_thread( void *pfn ) { - .name = "gameloop", - .get_budget = _vg_gameloop_budget, - .count = 3, - .list = - { - &vg_prof_update, &vg_prof_render, &vg_prof_swap - } + SDL_TLSSet( vg.thread_tls, &vg.thread_contexts[1], NULL ); + _vg_thread_set_flags( VG_THREAD_ASYNC ); + while( vg_async_process_next_task( &vg.thread_tasks[1] ) ) {} + return 0; +} + +VG_API u32 _vg_start_temp_frame(void) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + VG_ASSERT( context->temp_stack_depth < VG_TEMP_STACK_MAX ); + u32 offset = context->temporary_memory.offset; + context->temp_offsets[ context->temp_stack_depth ++ ] = offset; + return offset; +} + +VG_API void _vg_end_temp_frame( u32 whence ) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + VG_ASSERT( context->temp_stack_depth ); + context->temp_stack_depth --; + VG_ASSERT( context->temp_offsets[ context->temp_stack_depth ] == whence ); + context->temporary_memory.offset = whence; +} + +VG_API vg_stack_allocator *_vg_temp_stack(void) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + return &context->temporary_memory; +} + +/* group control */ +VG_API void _vg_async_context_push_groups( u16 groups ) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + + context->async_group_depth ++; + VG_ASSERT( context->async_group_depth < VG_TEMP_STACK_MAX ); + context->async_groups[ context->async_group_depth ] = groups | context->async_groups[ context->async_group_depth-1 ]; +} + +VG_API void _vg_async_context_pop_groups(void) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + + VG_ASSERT( context->async_group_depth ); + context->async_group_depth --; +} + +VG_API u16 _vg_async_context_get_groups(void) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + return context->async_groups[ context->async_group_depth ]; +} + +VG_API void *_vg_async_alloc ( u32 thread_id_target, u32 bytes ) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + VG_ASSERT( context->async_task == NULL ); + VG_ASSERT( context->async_task_queue == NULL ); + VG_ASSERT( (thread_id_target==0) || (thread_id_target==1) ); + if( thread_id_target==1 ) { VG_ASSERT( !_vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); } + else { VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); } + + context->async_task_queue = &vg.thread_tasks[ thread_id_target ]; + context->async_task = vg_create_task( context->async_task_queue, bytes, VG_ASYNC_BLOCKING, "engine (No info)" ); + return vg_task_buffer( context->async_task_queue, context->async_task ); +} + +VG_API void _vg_async_send( void *check_buffer, vg_async_fn fn ) +{ + struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls ); + VG_ASSERT( context->async_task && context->async_task_queue ); + VG_ASSERT( vg_task_buffer( context->async_task_queue, context->async_task ) == check_buffer ); + vg_task_send( context->async_task_queue, context->async_task, fn ); + context->async_task = NULL; + context->async_task_queue = NULL; +} + +struct exit_task +{ + void (*fn)(void); }; +static void vg_call_exit( struct exit_task *in_args, vg_async_info *async ) +{ + in_args->fn(); +} -void vg_bake_shaders(void) +VG_API void _vg_add_exit_function( void( *fn )( void ) ) { - vg_async_call( &vg.main_tasks, vg_shaders_compile, NULL ); + vg_async_task *task = vg_create_task( &vg.exit_tasks, sizeof(struct exit_task), VG_ASYNC_CRIT, "engine exit (No info)" ); + struct exit_task *out_args = vg_task_buffer( &vg.exit_tasks, task ); + out_args->fn = fn; + vg_task_send( &vg.exit_tasks, task, (vg_async_fn)vg_call_exit ); } -#ifdef VG_CUSTOM_SHADERS -void vg_auto_shader_register(void); /* created from codegen */ +#ifdef _WIN32 +#include +#include +#endif + +#include +static void sync_signal_handler( int signum ) +{ + // We want signals to be caught by debuggers if we're just in the testing builds. +#if defined( VG_RELEASE_MODE ) + if( signum == SIGSEGV ) vg_fatal_exit( "SIGSEGV" ); +# if !defined( _WIN32 ) + if( signum == SIGBUS ) vg_fatal_exit( "SIGBUS" ); +# endif + if( signum == SIGFPE ) vg_fatal_exit( "SIGFPE" ); + if( signum == SIGILL ) vg_fatal_exit( "SIGILL" ); + vg_fatal_exit( "UNKNOWN SIGNAL" ); #endif +} -static void vg_load_co( vg_coroutine *co ) +static int cmd_die( int argc, const char *argv[] ) { - if( co_begin( co ) ) + if( argc ) { - co_thread( co, 0, &vg.main_tasks ); - co_thread( co, 1, &vg.loader_tasks ); + if( !strcmp( argv[0], "segv" ) ) + { + vg_info( "Trying to make a segfault\n" ); + u32 *nothing = (void *)3; + vg_info( "Uhm %u\n", *nothing ); + } } + else + vg_fatal_error( "VG FATAL ASSERT ERROR" ); + return 0; +} + +static void _vg_engine_register(void) +{ + vg_console_reg_var( "vg_fps_limit", &vg.fps_limit, k_var_dtype_i32, VG_VAR_PERSISTENT ); + vg_console_reg_var( "vg_quality", &vg.quality_profile, k_var_dtype_i32, VG_VAR_PERSISTENT ); + vg_console_reg_cmd( "die", cmd_die, NULL ); +} + +static void _vg_engine_init(void) +{ + vg_info(" Copyright . . . -----, ,----- ,---. .---. \n" ); + vg_info(" 2021-2025 |\\ /| | / | | | | /| \n" ); + vg_info(" | \\ / | +-- / +----- +---' | / | \n" ); + vg_info(" | \\ / | | / | | \\ | / | \n" ); + vg_info(" | \\/ | | / | | \\ | / | \n" ); + vg_info(" ' ' '--' [] '----- '----- ' ' '---' " + "SOFTWARE\n" ); - if( co_step( co, 1 ) ) + /* init SDL */ + vg_info( "SDL_INIT_VIDEO\n" ); + if( SDL_Init( SDL_INIT_VIDEO ) != 0 ) { - vg_info(" Copyright . . . -----, ,----- ,---. .---. \n" ); - vg_info(" 2021-2025 |\\ /| | / | | | | /| \n" ); - vg_info(" | \\ / | +-- / +----- +---' | / | \n" ); - vg_info(" | \\ / | | / | | \\ | / | \n" ); - vg_info(" | \\/ | | / | | \\ | / | \n" ); - vg_info(" ' ' '--' [] '----- '----- ' ' '---' " - "SOFTWARE\n" ); - - vg_tex2d_replace_with_error_async( 0, &vg.tex_missing ); + vg_error( "SDL_Init failed: %s\n", SDL_GetError() ); + exit(0); } - if( co_step( co, 0 ) ) +#ifndef VG_NO_AUDIO + vg_info( "SDL_INIT_AUDIO\n" ); + SDL_InitSubSystem( SDL_INIT_AUDIO ); +#endif + vg_info( "SDL_INIT_GAMECONTROLLER\n" ); + SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ); + vg.base_path = SDL_GetBasePath(); + + if( !vg_init_async_queue( &vg.thread_tasks[0] ) ) + vg_fatal_error( "Failed to create main task queue\n" ); + if( !vg_init_async_queue( &vg.thread_tasks[1] ) ) + vg_fatal_error( "Failed to create async task queue\n" ); + if( !vg_init_async_queue( &vg.exit_tasks ) ) + vg_fatal_error( "Failed to create exit task queue\n" ); + + for( u32 i=0; i<3; i ++ ) + vg_stack_init( &vg.thread_contexts[i].temporary_memory, NULL, VG_MB(20), "Temporary memory stack" ); + + SDL_CreateThread( _vg_async_thread, "vg: async", NULL ); + vg.profiler = _vg_profiler_create( "vg.core", 1000.0f/60.0f ); + vg_rand_seed( &vg.rand, 461 ); +} + +VG_API void vg_run( int argc, const char *argv[], vg_event_callback callback_fn ) +{ + /* Pre-init + * -------------------------------------------------------- */ + vg.thread_tls = SDL_TLSCreate(); + SDL_TLSSet( vg.thread_tls, &vg.thread_contexts[0], NULL ); + _vg_thread_set_flags( VG_THREAD_MAIN ); + _vg_log_pre_init(); + + /* launch options + * --------------------------------------------------------------------------------------------------- */ + _vg_opt_init( argc, argv ); + const char *arg; + if( vg_long_opt( "high-performance", "Turn graphics to lowest quality" ) ) + vg.quality_profile = k_quality_profile_low; + + if( (arg = vg_long_opt_arg( "load-step-delay", "Loader step delay (ms)" )) ) + vg.load_step_delay = atoi(arg); + + if( (arg = vg_long_opt_arg( "log", "Log output to text file (without console colours)" )) ) { - vg_ui.tex_bg = vg.tex_missing; + vg_log.plain_output_file = fopen( arg, "w" ); + if( !vg_log.plain_output_file ) + vg_error( "Could not open '%s' for logging.\n", arg ); } - if( co_step( co, 1 ) ) + if( vg_long_opt( "no-steam", "Disable Steam integration (Good idea for pirating)" ) ) + _steam_api.disabled = 1; + + callback_fn( (vg_event_info[]){{ .type = k_vg_event_opts }} ); + + if( !_vg_opt_check() ) { - /* internal */ - vg_loader_step( vg_render_init, NULL ); - vg_loader_step( vg_input_init, vg_input_free ); - vg_loader_step( vg_lines_init, NULL ); - vg_loader_step( vg_rb_view_init, NULL ); - _vg_profile_reg_set( &_vg_prof_gameloop ); + callback_fn( (vg_event_info[]){{ .type = k_vg_event_invalid_options }} ); + exit(0); + } -#ifndef VG_NO_AUDIO - vg_loader_step( vg_audio_init, vg_audio_free ); + /* Crash watcher + * --------------------------------------------------------------------------------------------------- */ +#ifdef _WIN32 + DWORD pid = GetCurrentProcessId(); + + char report_args_buf[ 512 ]; + vg_str report_args; + vg_strnull( &report_args, report_args_buf, sizeof(report_args_buf) ); + vg_strcat( &report_args, "vgcrashreport.exe " ); + vg_strcatu64( &report_args, pid, 16 ); + vg_strcat( &report_args, " " ); + vg_strcat( &report_args, vg_log.crash_path ); + + STARTUPINFO si={0}; + PROCESS_INFORMATION pi={0}; + si.cb = sizeof(si); + + if( CreateProcess( NULL, report_args_buf, NULL, NULL, 0, BELOW_NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi ) == 0 ) + vg_error( "Could not start crash reporter. Reason: %d\n", GetLastError() ); + else + vg_success( "Crash watcher started!\n" ); +#endif + + // We want signals to be caught by debuggers if we're just in the testing builds. +#if defined( VG_RELEASE_MODE ) + signal( SIGSEGV, sync_signal_handler ); +# if !defined( _WIN32 ) + signal( SIGBUS, sync_signal_handler ); +# endif + signal( SIGFPE, sync_signal_handler ); + signal( SIGILL, sync_signal_handler ); #endif - /* client */ -#ifdef VG_CUSTOM_SHADERS - vg_auto_shader_register(); + /* Registration step + * ----------------------------------------------- */ + _vg_console_register(); + _vg_magi_register(); + _vg_settings_register(); + _vg_engine_register(); + _vg_rigidbody_register(); + _vg_audio_register(); + _vg_framebuffer_register(); + _vg_render_register(); + _vg_input_register(); + _vg_lines_register(); + _vg_profiler_register(); + _vg_mem_view_register(); + _vg_shaders_register(); + callback_fn( (vg_event_info[]){{ .type = k_vg_event_register }} ); + + /* Init step + * ---------------------------------------------------- */ + _vg_async_init(); + _vg_profiler_init(); + _vg_engine_init(); + _vg_console_init(); + _vg_window_init(); + _vg_shaders_init(); + _vg_ui_init(); + _vg_loader_init(); + _vg_tex_init(); + _vg_render_init(); + _vg_input_init(); + _vg_lines_init(); + _vg_rb_view_init(); +#if !defined( VG_NO_AUDIO ) + _vg_audio_init(); #endif + _vg_steam_init(); + callback_fn( (vg_event_info[]){{ .type = k_vg_event_init }} ); + + vg_magi_restore(); + + /* Frame pre-filter & timing + * -------------------------------------------- */ +L_new_frame: + _vg_profiler_tick( vg.profiler ); + + vg.time_frame_delta = 0.0; + vg.time_spinning = 0; + +L_filter_frame: + vg.time_hp = SDL_GetPerformanceCounter(); + u64 dt_hp = vg.time_hp - vg.time_hp_last; + vg.time_hp_last = vg.time_hp; + + f64 dt = (f64)dt_hp / (f64)SDL_GetPerformanceFrequency(); + vg.time_frame_delta += dt; + + /* TODO: limit time we can spend here */ + while( vg_async_has_work( &vg.thread_tasks[0] ) ) + { + if( vg_async_process_next_task( &vg.thread_tasks[0] ) == 0 ) + _vg_terminate(); } - if( co_step( co, 0 ) ) + if( vg.fps_limit == 0 ) + vg.fps_limit = _vg_window.monitor_refresh_rate; + if( vg.fps_limit < 24 ) + vg.fps_limit = 24; + if( vg.fps_limit > 300 ) + vg.fps_limit = 300; + + f64 min_frametime = 1.0/(f64)vg.fps_limit; + if( vg.time_frame_delta < min_frametime ) { -#ifndef VG_NO_AUDIO - vg_audio_begin(); -#endif - SDL_AtomicSet( &vg.engine_status, k_engine_status_running ); - _vg_tower_set_flag( vg.sig_engine, 1 ); + /* TODO: we can use high res nanosleep on Linux here */ + f64 sleep_ms = (min_frametime-vg.time_frame_delta) * 1000.0; + u32 ms = (u32)floor( sleep_ms ); + if( ms ) + SDL_Delay(ms); + else + vg.time_spinning ++; + goto L_filter_frame; } - co_end( co ); -} + /* New fame starts here + * --------------------------------------------------------------------------------------------------------------- */ -static void _vg_process_events(void) -{ - v2_zero( vg.mouse_wheel ); - v2_zero( vg.mouse_delta ); + vg.time_real += vg.time_frame_delta; + vg.time_delta = vg.time_frame_delta * vg.time_rate; + vg.time += vg.time_delta; /* SDL event loop */ SDL_Event event; @@ -156,17 +410,11 @@ static void _vg_process_events(void) ui_defocus_all( &vg_ui.ctx ); } else if( (event.key.keysym.mod & KMOD_CTRL) && (event.key.keysym.sym == SDLK_n) ) - { console_suggest_next( &vg_ui.ctx ); - } else if( (event.key.keysym.mod & KMOD_CTRL ) && (event.key.keysym.sym == SDLK_p) ) - { console_suggest_prev( &vg_ui.ctx ); - } else - { vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym ); - } } else { @@ -176,9 +424,7 @@ static void _vg_process_events(void) vg_console.enabled = 1; } else - { vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym ); - } } } else if( event.type == SDL_MOUSEWHEEL ) @@ -187,9 +433,7 @@ static void _vg_process_events(void) vg.mouse_wheel[1] += event.wheel.preciseY; } else if( (event.type == SDL_CONTROLLERDEVICEADDED) || (event.type == SDL_CONTROLLERDEVICEREMOVED) ) - { vg_input_device_event( &event ); - } else if( event.type == SDL_CONTROLLERAXISMOTION || event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP ) @@ -205,1102 +449,105 @@ static void _vg_process_events(void) { if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) { - int w, h; - SDL_GL_GetDrawableSize( vg.window, &w, &h ); - - if( !w || !h ) - { - vg_warn( "Got a invalid framebuffer size: %dx%d... ignoring\n", w, h ); - } - else - { - - i32 delta[2] = { w - vg.window_x, h - vg.window_y }; - _vg_magi_area_change( delta ); - vg.window_x = w; - vg.window_y = h; - - vg_framebuffer_update_sizes(); - vg_framebuffer_resize( vg.window_x, vg.window_y ); - } + _vg_window_size_changed(); + callback_fn( (vg_event_info[]){{ .type = k_vg_event_framebuffer_resize, + .framebuffer = { .w = _vg_window.w, .h = _vg_window.h } }} ); } else if( event.window.event == SDL_WINDOWEVENT_CLOSE ) - { - vg.window_should_close = 1; - } + _vg_terminate(); } else if( event.type == SDL_TEXTINPUT ) - { ui_proc_utf8( &vg_ui.ctx, event.text.text ); - } } - vg.mouse_state = SDL_GetMouseState( &vg.mouse_pos[0], &vg.mouse_pos[1] ); - vg_process_inputs(); + v2_zero( vg.mouse_wheel ); + v2_zero( vg.mouse_delta ); + _vg_window_swap(); + vg_audio_preupdate(); + vg_process_inputs(); vg_steam_frame(); -} - -static void _vg_gameloop_update(void) -{ - vg_profile_begin( &vg_prof_update ); - - vg.engine_stage = k_engine_stage_update; - vg_pre_update(); - - /* Fixed update loop */ - vg.engine_stage = k_engine_stage_update_fixed; - - vg.fixed_iterations = 0; - vg_lines.enabled = vg_lines.render; - vg.time_fixed_accumulator += vg.time_delta; - - while( vg.time_fixed_accumulator >= vg.time_fixed_delta ) + + if( 1 ) { - vg_fixed_update(); - vg_lines.enabled = 0; - vg.time_fixed_accumulator -= vg.time_fixed_delta; - - vg.fixed_iterations ++; - if( vg.fixed_iterations == 8 ) - break; - } - vg_lines.enabled = vg_lines.render; - vg.time_fixed_extrapolate = vg.time_fixed_accumulator / vg.time_fixed_delta; + /* Update loop + * ----------------------------------------------------------------------------------------------------------- */ + { + _vg_profiler_enter_block( vg.profiler, "update" ); + vg.gameloop_stage = k_gameloop_update; + callback_fn( (vg_event_info[]){{ .type = k_vg_event_pre_update }} ); - vg.engine_stage = k_engine_stage_update; - vg_post_update(); - vg_profile_end( &vg_prof_update ); -} + /* Fixed update loop */ + vg.gameloop_stage = k_gameloop_update_fixed; + vg.fixed_iterations = 0; + vg_lines.enabled = vg_lines.render; + vg.time_fixed_accumulator += vg.time_delta; -static void vg_settings_gui( ui_context *ctx ); -static void _vg_gameloop_render(void) -{ - vg_profile_begin( &vg_prof_render ); + while( vg.time_fixed_accumulator >= vg.time_fixed_delta ) + { + callback_fn( (vg_event_info[]){{ .type = k_vg_event_fixed_update }} ); + vg_lines.enabled = 0; + vg.time_fixed_accumulator -= vg.time_fixed_delta; - /* render */ - vg.engine_stage = k_engine_stage_rendering; - vg_render(); + vg.fixed_iterations ++; + if( vg.fixed_iterations == 8 ) + break; + } + vg_lines.enabled = vg_lines.render; + vg.time_fixed_extrapolate = vg.time_fixed_accumulator / vg.time_fixed_delta; - vg_profile_end( &vg_prof_render ); + vg.gameloop_stage = k_gameloop_update; + callback_fn( (vg_event_info[]){{ .type = k_vg_event_post_update }} ); + _vg_profiler_exit_block( vg.profiler ); + } - /* ui */ - vg.engine_stage = k_engine_stage_ui; - { - if( _vg_tower_clearence( _vg_tower_mask(vg.sig_engine)|_vg_tower_mask(vg.sig_client) ) ) + /* Render loop + * ------------------------------------------------------------------------------------------------------------ */ { + /* render -------------------------------------------------- */ + _vg_profiler_enter_block( vg.profiler, "render" ); + vg.gameloop_stage = k_gameloop_rendering; + callback_fn( (vg_event_info[]){{ .type = k_vg_event_render }} ); + _vg_profiler_exit_block( vg.profiler ); + + /* ui --------------------------------------------------- */ + vg.gameloop_stage = k_gameloop_ui; + ui_prerender( &vg_ui.ctx ); - vg_ui_set_screen( vg.window_x, vg.window_y ); + vg_ui_set_screen( _vg_window.w, _vg_window.h ); ui_update_mouse( &vg_ui.ctx, (ui_px[2]){ vg.mouse_pos[0], vg.mouse_pos[1] }, vg.mouse_state ); if( vg_console.enabled ) - { ui_ignore_input_frames( &vg_ui.ctx, 10 ); - vg_gui( &vg_ui.ctx ); + callback_fn( (vg_event_info[]){{ .type = k_vg_event_gui, .gui = { .ctx = &vg_ui.ctx } }} ); + if( vg_console.enabled ) + { ui_ignore_input_frames( &vg_ui.ctx, 0 ); ui_capture_mouse( &vg_ui.ctx, 1 ); vg_console_draw( &vg_ui.ctx ); } - else vg_gui( &vg_ui.ctx ); - if( vg.settings_open ) - vg_settings_gui( &vg_ui.ctx ); - + _vg_settings_gui( &vg_ui.ctx ); vg_framebuffer_ui( &vg_ui.ctx ); _vg_magi_render( &vg_ui.ctx ); - ui_postrender( &vg_ui.ctx, vg.time_frame_delta ); vg_ui_post_update(); } } -} - -static void vg_changevsync(void) -{ - if( vg.vsync && (vg.vsync_feature != k_vsync_feature_error) ) + else { - /* turn on vsync if not enabled */ - - enum vsync_feature requested = k_vsync_feature_enabled; - if( vg.vsync < 0 ) requested = k_vsync_feature_enabled_adaptive; - - if( vg.vsync_feature != requested ) - { - vg_info( "Setting swap interval\n" ); - - int swap_interval = 1; - if( requested == k_vsync_feature_enabled_adaptive ) - swap_interval = -1; - - if( SDL_GL_SetSwapInterval( swap_interval ) == -1 ) - { - if( requested == k_vsync_feature_enabled ) - { - ui_start_modal( &vg_ui.ctx, - "Vsync not supported on this system.\n\n" - "You may be overriding it in your" - " graphics \ncontrol panel.\n", - NULL, UI_MODAL_BAD, NULL ); - } - else - { - ui_start_modal( &vg_ui.ctx, - "Adaptive Vsync is not supported by your system\n\n" - "You may be overriding it in your" - " graphics \ncontrol panel.\n", - NULL, UI_MODAL_BAD, NULL ); - } - - vg.vsync_feature = k_vsync_feature_error; - vg.vsync = 0; - } - else - { - vg_success( "Vsync enabled (%d)\n", requested ); - vg.vsync_feature = requested; - } - } - } - else { - if( vg.vsync_feature != k_vsync_feature_disabled ){ - SDL_GL_SetSwapInterval( 0 ); - vg.vsync_feature = k_vsync_feature_disabled; - } + vg_loader_render(); } -} - -static int vg_framefilter( f64 dt ) -{ - if( vg.fps_limit < 24 ) - vg.fps_limit = 24; - if( vg.fps_limit > 300 ) - vg.fps_limit = 300; - - double min_frametime = 1.0/(double)vg.fps_limit; - if( vg.time_frame_delta < min_frametime ) + if( vg.loader_ring > 0.01f ) { - /* TODO: we can use high res nanosleep on Linux here */ - double sleep_ms = (min_frametime-vg.time_frame_delta) * 1000.0; - u32 ms = (u32)floor( sleep_ms ); + vg_loader_render_ring( vg.loader_ring ); + vg.loader_ring -= vg.time_frame_delta * 0.5f; + } - if( ms ) - SDL_Delay(ms); - else - vg.time_spinning ++; - - return 1; - } - - return 0; -} - -static void _vg_gameloop(void) -{ - vg.time_hp = SDL_GetPerformanceCounter(); - vg.time_hp_last = vg.time_hp; - - int post_start = 0; - while(1) - { - vg.time_hp = SDL_GetPerformanceCounter(); - u64 dt_hp = vg.time_hp - vg.time_hp_last; - vg.time_hp_last = vg.time_hp; - - f64 dt = (f64)dt_hp / (f64)SDL_GetPerformanceFrequency(); - vg.time_frame_delta += dt; - - while( vg_async_has_work( &vg.main_tasks ) ) - { - if( vg_async_process_next_task( &vg.main_tasks ) == 0 ) - return; - } - - if( vg_framefilter( dt ) ) - continue; - - vg_changevsync(); - vg_audio_preupdate(); - - enum engine_status status = SDL_AtomicGet( &vg.engine_status ); - if( status == k_engine_status_running ) - vg_profile_begin( &vg_prof_swap ); - - SDL_GL_SwapWindow( vg.window ); - - if( status == k_engine_status_running ) - vg_profile_end( &vg_prof_swap ); - - vg.time_real += vg.time_frame_delta; - vg.time_delta = vg.time_frame_delta * vg.time_rate; - vg.time += vg.time_delta; - - _vg_process_events(); - - if( vg.window_should_close ) - return; - - if( status == k_engine_status_running ) - { - _vg_gameloop_update(); - _vg_gameloop_render(); - } - else - { - vg_loader_render(); - } - - if( vg.loader_ring > 0.01f ) - { - vg_loader_render_ring( vg.loader_ring ); - vg.loader_ring -= vg.time_frame_delta * 0.5f; - } - - vg.time_frame_delta = 0.0; - vg.time_spinning = 0; - - vg_audio_lock(); - vg_audio_sync_ui_master_controls(); - vg_audio_unlock(); - } -} - -static void _vg_init_window( const char *window_name ) -{ - vg_info( "SDL_INIT\n" ); - - if( SDL_Init( SDL_INIT_VIDEO ) != 0 ) - { - vg_error( "SDL_Init failed: %s\n", SDL_GetError() ); - exit(0); - } - -#ifndef VG_NO_AUDIO - SDL_InitSubSystem( SDL_INIT_AUDIO ); -#endif - SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ); - - char *exe_basepath = SDL_GetBasePath(); - u32 len = vg_align8( strlen(exe_basepath)+1 ); - char *dest = vg_stack_allocate( &vg.rtmem, len, 1, "Exe basepath" ); - strcpy( dest, exe_basepath ); - SDL_free( exe_basepath ); - vg.base_path = dest; - vg_info( "Basepath: %s\n", vg.base_path ); - - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 ); - SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 ); - SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE ); - SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR, SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH ); - 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_STENCIL_SIZE, 0 ); - - /* - * Get monitor information - */ - vg_info( "Getting display count\n" ); - int display_count = 0, - display_index = 0, - mode_index = 0; - - SDL_DisplayMode video_mode; - if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) ) - { - vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() ); - SDL_Quit(); - exit(0); - } - - vg.display_refresh_rate = video_mode.refresh_rate; - vg.window_x = video_mode.w; - vg.window_y = video_mode.h; - - if( vg.screen_mode == 2 ) - { - vg.window_x = 1280; - vg.window_y = 720; - } - -#ifndef _WIN32 - SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" ); - SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" ); - SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" ); -#endif - - u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_GRABBED | - SDL_WINDOW_RESIZABLE; - - if( vg.screen_mode == 1 ) - flags |= SDL_WINDOW_FULLSCREEN; - else if( vg.screen_mode == 0 ) - flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - - vg_info( "CreateWindow( %d %d %u )\n", vg.window_x, vg.window_y, flags ); - if((vg.window = SDL_CreateWindow( window_name, 0, 0, vg.window_x, vg.window_y, flags ))) - { - if( vg.screen_mode == 2 ) - SDL_SetWindowPosition( vg.window, video_mode.w-vg.window_x, 0 ); - } - else - { - vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() ); - exit(0); - } - - SDL_RaiseWindow( vg.window ); - SDL_SetWindowMinimumSize( vg.window, 1280, 720 ); - SDL_SetWindowMaximumSize( vg.window, 4096, 4096 ); - - vg_info( "CreateContext\n" ); + vg_audio_lock(); + vg_audio_sync_ui_master_controls(); + vg_audio_unlock(); - /* ????? */ - if( SDL_IsTextInputActive() ) - SDL_StopTextInput(); - - /* - * OpenGL loading - */ - if( (vg.gl_context = SDL_GL_CreateContext(vg.window) )) - { - SDL_GL_GetDrawableSize( vg.window, &vg.window_x, &vg.window_y ); - vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y ); - } - else - { - vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() ); - SDL_Quit(); - exit(0); - } - - if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) - { - vg_error( "Glad Failed to initialize\n" ); - SDL_GL_DeleteContext( vg.gl_context ); - SDL_Quit(); - exit(0); - } - - const unsigned char* glver = glGetString( GL_VERSION ); - vg_success( "Load setup complete, OpenGL version: %s\n", glver ); - SDL_GL_SetSwapInterval(0); /* disable vsync while loading */ - - SDL_DisplayMode dispmode; - if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) ) - { - if( dispmode.refresh_rate ) - { - vg.display_refresh_rate = dispmode.refresh_rate; - } - } - - if( vg.display_refresh_rate < 25 || vg.display_refresh_rate > 300 ) - { - vg.display_refresh_rate = 60; - } - - vg_info( "Display refresh rate: %d\n", dispmode.refresh_rate ); - if( !vg.fps_limit ) - vg.fps_limit = vg.display_refresh_rate; -} - -static void _vg_terminate(void) -{ - /* Shutdown */ - vg_magi_save(); - vg_console_write_persistent(); - - SDL_AtomicSet( &vg.engine_status, k_engine_status_none ); - vg_loader_atexit(); - - SDL_GL_DeleteContext( vg.gl_context ); - SDL_Quit(); - vg_steam_shutdown(); - exit(0); -} - -static int _vg_loader_thread( void *pfn ) -{ - SDL_TLSSet( vg.thread_purpose, &_thread_purpose_loader, NULL ); - while( vg_async_process_next_task( &vg.loader_tasks ) ) {} - return 0; -} - -static void vg_on_client_ready( vg_signal_id id, bool state ) -{ - if( state ) - vg_magi_restore(); -} - -#ifdef _WIN32 -#include -#include -#endif -static int cmd_vg_settings_toggle( int argc, const char *argv[] ); -void vg_init( int argc, const char *argv[], const char *window_name ) -{ - vg.window_name = window_name; - vg.thread_purpose = SDL_TLSCreate(); - VG_ASSERT( vg.thread_purpose ); - SDL_TLSSet( vg.thread_purpose, &_thread_purpose_main, NULL ); - vg_log_init(); - - /* launch options */ - _vg_opt_init( argc, argv ); - const char *arg; - if( (arg = vg_opt_arg( 'w', "Render output width" )) ) - vg.window_x = atoi( arg ); - - if( (arg = vg_opt_arg( 'h', "Render output height" )) ) - vg.window_y = atoi( arg ); - - if( (arg = vg_long_opt_arg( "samples", "Rendering samples per pixel" )) ) - vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) ); - - if( vg_long_opt( "high-performance", "Turn graphics to lowest quality" ) ) - vg.quality_profile = k_quality_profile_low; - - if( (arg = vg_long_opt_arg( "load-step-delay", "Loader step delay (ms)" )) ) - vg.load_step_delay = atoi(arg); - - if( (arg = vg_long_opt_arg( "log", "Log output to text file (without console colours)" )) ) - { - vg_log.plain_output_file = fopen( arg, "w" ); - if( !vg_log.plain_output_file ) - vg_error( "Could not open '%s' for logging.\n", arg ); - } - - if( vg_long_opt( "no-steam", "Disable Steam integration (Good idea for pirating)" ) ) - _steam_api.disabled = 1; - -#ifdef _WIN32 - DWORD pid = GetCurrentProcessId(); - - char report_args_buf[ 512 ]; - vg_str report_args; - vg_strnull( &report_args, report_args_buf, sizeof(report_args_buf) ); - vg_strcat( &report_args, "vgcrashreport.exe " ); - vg_strcatu64( &report_args, pid, 16 ); - vg_strcat( &report_args, " " ); - vg_strcat( &report_args, vg_log.crash_path ); - - STARTUPINFO si={0}; - PROCESS_INFORMATION pi={0}; - si.cb = sizeof(si); - - if( CreateProcess( NULL, report_args_buf, NULL, NULL, 0, BELOW_NORMAL_PRIORITY_CLASS, - NULL, NULL, &si, &pi ) == 0 ) - { - vg_error( "Could not start crash reporter. Reason: %d\n", GetLastError() ); - } - else - vg_success( "Crash watcher started!\n" ); - -#endif - vg.sig_engine = _vg_tower_create_signal( "Engine" ); - vg.sig_client = _vg_tower_create_signal( "Client" ); - _vg_tower_register_trigger( _vg_tower_mask( vg.sig_client ), vg_on_client_ready ); - - if( !vg_init_async_queue( &vg.main_tasks ) ) - vg_fatal_error( "Failed to create main task queue\n" ); - if( !vg_init_async_queue( &vg.loader_tasks ) ) - vg_fatal_error( "Failed to create loader task queue\n" ); - - vg_rand_seed( &vg.rand, 461 ); -} - -static int cmd_die( int argc, const char *argv[] ) -{ - if( argc ) - { - if( !strcmp( argv[0], "segv" ) ) - { - vg_info( "Trying to make a segfault\n" ); - u32 *nothing = (void *)3; - vg_info( "Uhm %u\n", *nothing ); - } - } - else - vg_fatal_error( "VG FATAL ASSERT ERROR" ); - return 0; -} - -#include -static void sync_signal_handler( int signum ) -{ - if( signum == SIGSEGV ) vg_fatal_exit( "SIGSEGV" ); -#ifndef _WIN32 - if( signum == SIGBUS ) vg_fatal_exit( "SIGBUS" ); -#endif - if( signum == SIGFPE ) vg_fatal_exit( "SIGFPE" ); - if( signum == SIGILL ) vg_fatal_exit( "SIGILL" ); - vg_fatal_exit( "UNKNOWN SIGNAL" ); + goto L_new_frame; } - -void vg_run(void) -{ - signal( SIGSEGV, sync_signal_handler ); -#ifndef _WIN32 - signal( SIGBUS, sync_signal_handler ); -#endif - signal( SIGFPE, sync_signal_handler ); - signal( SIGILL, sync_signal_handler ); - - if( !_vg_opt_check() ) - exit(0); - - vg_steam_init(); - - /* Systems init */ - vg_console_init(); - vg_magi_init(); - - vg_console_reg_var( "vg_fps_limit", &vg.fps_limit, k_var_dtype_i32, VG_VAR_PERSISTENT ); - vg_console_reg_var( "vg_vsync", &vg.vsync, k_var_dtype_i32, VG_VAR_PERSISTENT ); - vg_console_reg_var( "vg_quality", &vg.quality_profile, k_var_dtype_i32, VG_VAR_PERSISTENT ); - vg_console_reg_var( "vg_screen_mode", &vg.screen_mode, k_var_dtype_i32, VG_VAR_PERSISTENT ); - vg_console_reg_cmd( "vg_settings", cmd_vg_settings_toggle, NULL ); - vg_console_reg_cmd( "die", cmd_die, NULL ); - rb_register_cvar(); - vg_audio_register(); - vg_framebuffer_register(); - vg_render_register(); - vg_input_register(); - vg_lines_register(); - vg_profiler_register(); - vg_mem_view_register(); - vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL ); - - vg_console_load_autos(); - - _vg_init_window( vg.window_name ); - SDL_SetRelativeMouseMode(1); - - /* Opengl-required systems */ - vg_ui_init(); - vg_loader_step( vg_loader_init, vg_loader_free ); - - SDL_AtomicSet( &vg.engine_status, k_engine_status_load_internal ); - co_run( vg_load_co, NULL ); - - _thread_purpose_loader = 0x2; - SDL_CreateThread( _vg_loader_thread, "vg: loader", NULL ); - _vg_gameloop(); - _vg_terminate(); -} - -/* - * settings menu - * --------------------------------------------------------------------------- - */ - -#ifdef VG_GAME_SETTINGS -extern void vg_game_settings_gui( ui_context *ctx, ui_rect panel ) ; -extern void vg_game_settings_init(void); -#endif - -struct ui_enum_opt vg_settings_vsync_enum[] = { - { 0, "None" }, - { 1, "On" }, - {-1, "Adaptive" }, -}; - -struct ui_enum_opt vg_settings_quality_enum[] = { - { 0, "High Quality" }, - { 1, "Faster" }, - { 2, "Absolute Minimum" }, -}; - -struct ui_enum_opt vg_settings_screen_mode_enum[] = { - { 0, "Fullscreen (desktop)" }, - { 1, "Fullscreen (native)" }, - { 2, "Floating Window" } -}; - -struct ui_enum_opt vg_settings_dsp_enum[] = { - { 1, "Enabled" }, - { 0, "Disabled" }, -}; - -struct { - struct vg_setting_ranged_i32 fps_limit; - struct vg_setting_enum vsync, quality, screenmode, audio_devices, dsp; - i32 temp_audio_choice; -} -static vg_settings = { - .fps_limit = { .label = "Fps Limit", - .min=24, .max=300, .actual_value = &vg.fps_limit }, - .vsync = { .label = "Vsync", - .actual_value = &vg.vsync, - .options = vg_settings_vsync_enum, .option_count = 3 }, - .quality = { .label = "Graphic Quality", - .actual_value = &vg.quality_profile, - .options = vg_settings_quality_enum, .option_count = 3 }, - .screenmode = { .label = "Type", - .actual_value = &vg.screen_mode, - .options = vg_settings_screen_mode_enum, .option_count=3 }, - .audio_devices = { .label = "Audio Device", - .actual_value = &vg_settings.temp_audio_choice, - .options = NULL, .option_count = 0 }, - .dsp = { .label = "Audio effects (reverb etc.)", - .actual_value = &_vg_audio.dsp_enabled_ui, - .options = vg_settings_dsp_enum, .option_count=2 }, -}; - -static void vg_settings_ui_draw_diff( ui_context *ctx, ui_rect orig ) -{ - ui_rect l,r; - ui_split( orig, k_ui_axis_v, -32, 0, l, r ); - ui_text( ctx, r, "*", 1, - k_ui_align_middle_center, ui_colour(ctx, k_ui_blue) ); -} - -/* i32 settings - * ------------------------------------------------------------------------- */ - -static void vg_settings_ui_int( ui_context *ctx, char *buf, u32 len, void *userdata ) -{ - for( u32 i=0, j=0; i= '0') && (buf[i] <= '9')) || (buf[i] == '\0') ) - buf[j ++] = buf[i]; - } -} - -struct ui_textbox_callbacks static vg_settings_ui_int_callbacks = -{ - .change = vg_settings_ui_int -}; - -static bool vg_settings_ranged_i32_valid( struct vg_setting_ranged_i32 *prop ) -{ - if( prop->new_value < prop->min ) return 0; - if( prop->new_value > prop->max ) return 0; - return 1; -} - -static bool vg_settings_ranged_i32_diff( struct vg_setting_ranged_i32 *prop ) -{ - if( prop->new_value != *prop->actual_value ) return 1; - else return 0; -} - -static bool vg_settings_ui_ranged_i32( ui_context *ctx, - struct vg_setting_ranged_i32 *prop, - ui_rect rect ) -{ - ui_rect orig; - rect_copy( rect, orig ); - - ui_textbox( ctx, rect, prop->label, prop->buf, sizeof(prop->buf), - 1, 0, &vg_settings_ui_int_callbacks ); - prop->new_value = atoi( prop->buf ); - - if( vg_settings_ranged_i32_diff( prop ) ) - vg_settings_ui_draw_diff( ctx, orig ); - - bool valid = vg_settings_ranged_i32_valid( prop ); - if( !valid ) - { - ui_rect _null, line; - ui_split( orig, k_ui_axis_h, -1, 0, _null, line ); - line[1] += 3; - - ui_fill( ctx, line, ui_colour( ctx, k_ui_red ) ); - } - - return valid; -} - -void ui_settings_ranged_i32_init( struct vg_setting_ranged_i32 *prop ) -{ - vg_str tmp; - vg_strnull( &tmp, prop->buf, sizeof(prop->buf) ); - vg_strcati32( &tmp, *prop->actual_value ); - prop->new_value = *prop->actual_value; -} - -/* enum settings - * ------------------------------------------------------------------------- */ - -bool vg_settings_enum_diff( struct vg_setting_enum *prop ) -{ - if( prop->new_value != *prop->actual_value ) return 1; - else return 0; -} - -bool vg_settings_enum( ui_context *ctx, - struct vg_setting_enum *prop, ui_rect rect ) -{ - ui_rect orig; - rect_copy( rect, orig ); - - ui_enum( ctx, rect, prop->label, - prop->options, prop->option_count, &prop->new_value ); - - if( vg_settings_enum_diff( prop ) ) - vg_settings_ui_draw_diff( ctx, orig ); - - return 1; -} - -void ui_settings_enum_init( struct vg_setting_enum *prop ) -{ - prop->new_value = *prop->actual_value; -} - -/* .. */ - -void vg_settings_ui_header( ui_context *ctx, - ui_rect inout_panel, const char *name ) -{ - ui_rect rect; - ui_standard_widget( ctx, inout_panel, rect, 2 ); - ui_text( ctx, rect, name, 1, - k_ui_align_middle_center, ui_colour(ctx, k_ui_fg+3) ); -} - - -bool vg_settings_apply_button( ui_context *ctx, ui_rect inout_panel, bool validated ) -{ - ui_rect last_row; - ui_px height = ui_standard_widget_height( ctx, 1 ); - ui_split( inout_panel, k_ui_axis_h, -height, 8, - inout_panel, last_row ); - - const char *string = "Apply"; - if( validated ) - { - if( ui_button( ctx, last_row, string ) == 1 ) - return 1; - } - else - { - ui_rect rect; - ui_standard_widget( ctx, last_row, rect, 1 ); - ui_fill( ctx, rect, ui_colour( ctx, k_ui_bg+1 ) ); - ui_outline( ctx, rect, -1, ui_colour( ctx, k_ui_red ), 0 ); - - ui_rect t = { 0,0, ui_text_line_width( ctx, string ), 14 }; - ui_rect_center( rect, t ); - ui_text( ctx, t, string, 1, k_ui_align_left, ui_colour(ctx,k_ui_fg+3) ); - } - - return 0; -} - -static void vg_settings_video_apply(void) -{ - if( vg_settings_enum_diff( &vg_settings.screenmode ) ) - { - vg.screen_mode = vg_settings.screenmode.new_value; - - if( (vg.screen_mode == 0) || (vg.screen_mode == 1) ) - { - SDL_DisplayMode video_mode; - if( SDL_GetDesktopDisplayMode( 0, &video_mode ) ) - { - vg_error("SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError()); - } - else - { - //vg.display_refresh_rate = video_mode.refresh_rate; - vg.window_x = video_mode.w; - vg.window_y = video_mode.h; - } - SDL_SetWindowSize( vg.window, vg.window_x, vg.window_y ); - } - - if( vg.screen_mode == 0 ) - SDL_SetWindowFullscreen( vg.window, SDL_WINDOW_FULLSCREEN_DESKTOP ); - if( vg.screen_mode == 1 ) - SDL_SetWindowFullscreen( vg.window, SDL_WINDOW_FULLSCREEN ); - if( vg.screen_mode == 2 ) - { - SDL_SetWindowFullscreen( vg.window, 0 ); - SDL_SetWindowSize( vg.window, 1280, 720 ); - SDL_SetWindowPosition( vg.window, 16, 16 ); - SDL_SetWindowMinimumSize( vg.window, 1280, 720 ); - SDL_SetWindowMaximumSize( vg.window, 4096, 4096 ); - } - } - - vg.fps_limit = vg_settings.fps_limit.new_value; - vg.quality_profile = vg_settings.quality.new_value; - vg.vsync = vg_settings.vsync.new_value; -} - -static void vg_settings_video_gui( ui_context *ctx, ui_rect panel ) -{ - bool validated = 1; - ui_rect rq; - ui_standard_widget( ctx, panel, rq, 1 ); - vg_settings_enum( ctx, &vg_settings.quality, rq ); - - /* FIXME */ -#if 0 - if( vg.vsync_feature == k_vsync_feature_error ){ - ui_info( panel, "There was an error activating vsync feature." ); - } -#endif - - /* frame timing */ - vg_settings_ui_header( ctx, panel, "Frame Timing" ); - ui_rect duo, d0,d1; - ui_standard_widget( ctx, panel, duo, 1 ); - ui_split_ratio( duo, k_ui_axis_v, 0.5f, 16, d0, d1 ); - - vg_settings_enum( ctx, &vg_settings.vsync, d0 ); - validated &= vg_settings_ui_ranged_i32( ctx, &vg_settings.fps_limit, d1 ); - - /* profiler */ - ui_standard_widget( ctx, panel, duo, 10 ); - int frame_target = vg.display_refresh_rate; - if( !vg.vsync ) frame_target = vg.fps_limit; - vg_profile_drawn( - ctx, - (struct vg_profile *[]) - { - &vg_prof_update, - &vg_prof_render, - &vg_prof_swap - }, 3, - (1.0f/(f32)frame_target)*1500.0f, - duo, 1, 0 - ); - - ui_fill( ctx, (ui_rect){ duo[0], duo[1]+(duo[3]*2)/3, duo[2], 1 }, - ui_colour(ctx, k_ui_fg) ); - - /* window spec */ - vg_settings_ui_header( ctx, panel, "Window Specification" ); - - ui_standard_widget( ctx, panel, duo, 1 ); - vg_settings_enum( ctx, &vg_settings.screenmode, duo ); - - if( vg_settings_apply_button( ctx, panel, validated ) ) - vg_settings_video_apply(); -} - -static void vg_settings_audio_apply(void) -{ - if( vg_settings_enum_diff( &vg_settings.audio_devices ) ) - { - if( _vg_audio.sdl_output_device ) - { - vg_info( "Closing audio device %d\n", _vg_audio.sdl_output_device ); - SDL_CloseAudioDevice( _vg_audio.sdl_output_device ); - _vg_audio.sdl_output_device = 0; - } - - vg_strfree( &_vg_audio.device_choice ); - - if( vg_settings.audio_devices.new_value == -1 ){ } - else if( vg_settings.audio_devices.new_value == -2 ) - { - vg_fatal_error( "" ); - } - else - { - struct ui_enum_opt *selected = NULL, *oi; - - for( int i=0; ivalue == vg_settings.audio_devices.new_value ) - { - selected = oi; - break; - } - } - - vg_strnull( &_vg_audio.device_choice, NULL, 0 ); - vg_strcat( &_vg_audio.device_choice, oi->alias ); - } - - vg_audio_device_init(); - *vg_settings.audio_devices.actual_value = vg_settings.audio_devices.new_value; - } - - if( vg_settings_enum_diff( &vg_settings.dsp ) ) - *vg_settings.dsp.actual_value = vg_settings.dsp.new_value; -} - -static void vg_settings_audio_gui( ui_context *ctx, ui_rect panel ) -{ - ui_rect rq; - ui_standard_widget( ctx, panel, rq, 1 ); - vg_settings_enum( ctx, &vg_settings.audio_devices, rq ); - - ui_standard_widget( ctx, panel, rq, 1 ); - vg_settings_enum( ctx, &vg_settings.dsp, rq ); - - if( vg_settings_apply_button( ctx, panel, 1 ) ) - vg_settings_audio_apply(); -} - -void vg_settings_open(void) -{ - vg.settings_open = 1; - - ui_settings_ranged_i32_init( &vg_settings.fps_limit ); - ui_settings_enum_init( &vg_settings.vsync ); - ui_settings_enum_init( &vg_settings.quality ); - ui_settings_enum_init( &vg_settings.screenmode ); - - /* Create audio options */ - int count = SDL_GetNumAudioDevices( 0 ); - - struct ui_enum_opt *options = malloc( sizeof(struct ui_enum_opt)*(count+1) ); - vg_settings.audio_devices.options = options; - vg_settings.audio_devices.option_count = count+1; - - struct ui_enum_opt *o0 = &options[0]; - o0->alias = "OS Default"; - o0->value = -1; - - for( int i=0; ialias = malloc( len+1 ); - memcpy( (void *)oi->alias, device_name, len+1 ); - oi->value = i; - } - - if( _vg_audio.device_choice.buffer ) - { - vg_settings.temp_audio_choice = -2; - - for( int i=0; ialias, _vg_audio.device_choice.buffer ) ) - { - vg_settings.temp_audio_choice = oi->value; - break; - } - } - } - else { - vg_settings.temp_audio_choice = -1; - } - - ui_settings_enum_init( &vg_settings.audio_devices ); - ui_settings_enum_init( &vg_settings.dsp ); - -#ifdef VG_GAME_SETTINGS - vg_game_settings_init(); -#endif -} - -void vg_settings_close(void) -{ - vg.settings_open = 0; - - struct ui_enum_opt *options = vg_settings.audio_devices.options; - for( int i=1; i < vg_settings.audio_devices.option_count; i ++ ) - free( (void *)options[i].alias ); - free( vg_settings.audio_devices.options ); -} - -static void vg_settings_gui( ui_context *ctx ) -{ - ui_rect null; - ui_rect screen = { 0, 0, vg.window_x, vg.window_y }; - ui_rect window = { 0, 0, 1000, 700 }; - ui_rect_center( screen, window ); - ui_capture_mouse( ctx, 1 ); - - ui_fill( ctx, window, ui_colour( ctx, k_ui_bg+1 ) ); - ui_outline( ctx, window, 1, ui_colour( ctx, k_ui_bg+7 ), 0 ); - - ui_rect title, panel; - ui_split( window, k_ui_axis_h, 28, 0, title, panel ); - ui_fill( ctx, title, ui_colour( ctx, k_ui_bg+7 ) ); - ui_text( ctx, title, "Settings", 1, k_ui_align_middle_center, - ui_colourcont(ctx, k_ui_bg+7) ); - - ui_rect quit_button; - ui_split( title, k_ui_axis_v, title[2]-title[3], 2, title, quit_button ); - - if( ui_button_text( ctx, quit_button, "X", 1 ) == k_ui_button_click ) - { - vg_settings_close(); - return; - } - - ui_rect_pad( panel, (ui_px[2]){ 8, 8 } ); - - const char *opts[] = { "video", "audio", -#ifdef VG_GAME_SETTINGS - "game" -#endif - }; - - static i32 page = 0; - ui_tabs( ctx, panel, panel, opts, VG_ARRAY_LEN(opts), &page ); - - if( page == 0 ) - { - vg_settings_video_gui( ctx, panel ); - } - else if( page == 1 ) - vg_settings_audio_gui( ctx, panel ); - -#ifdef VG_GAME_SETTINGS - else if( page == 2 ) - vg_game_settings_gui( ctx, panel ); -#endif -} - -static int cmd_vg_settings_toggle( int argc, const char *argv[] ) -{ - vg_settings_open(); - return 0; -} - -#include "vg_async2.c" -#include "vg_audio.c" -#include "vg_audio_dsp.c" -#include "vg_audio_synth_bird.c" -#include "vg_binstr.c" -#include "vg_bvh.c" -#include "vg_camera.c" -#include "vg_lines.c" -#include "vg_console.c" -#include "vg_input.c" -#include "vg_io.c" -#include "vg_loader.c" -#include "vg_log.c" -#include "vg_tex.c" -#include "vg_mem.c" -#include "vg_mem_pool.c" -#include "vg_mem_queue.c" -#include "vg_msg.c" -#include "vg_opt.c" -#include "vg_perlin.c" -#include "vg_string.c" -#include "vg_profiler.c" -#include "vg_magi.c" -#include "vg_rigidbody_collision.c" -#include "vg_rigidbody_constraints.c" -#include "vg_rigidbody.c" -#include "vg_rigidbody_view.c" -#include "vg_shader.c" -#include "vg_framebuffer.c" -#include "vg_render.c" -#include "vg_opengl.c" -#include "vg_mem_view.c" -#include "vg_tower.c" -#include "laptop_gpu.c" - -#ifdef VG_CUSTOM_SHADERS - #include "shaders/impl.c" -#endif diff --git a/vg_engine.h b/vg_engine.h index 9955dbb..54a519b 100644 --- a/vg_engine.h +++ b/vg_engine.h @@ -1,136 +1,148 @@ -#pragma once - -/* Copyright (C) 2021-2025 Mt.Zero Software - All Rights Reserved */ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_engine.c" +#else /* configuration warnings */ - -#ifndef VG_TIMESTEP_FIXED +#if !defined( VG_TIMESTEP_FIXED ) #warning VG_TIMESTEP_FIXED not defined; setting to 1/60 #define VG_TIMESTEP_FIXED (1.0/60.0) #endif -#ifndef VG_3D - #ifndef VG_2D +#if !defined( VG_3D ) + #if !defined( VG_2D ) #warning VG_3D or VG_2D not defined; defining VG_3D #define VG_3D #endif #endif -#include "vg_opengl.h" +enum vg_event +{ + k_vg_event_opts, + k_vg_event_register, + k_vg_event_init, + k_vg_event_pre_update, + k_vg_event_fixed_update, + k_vg_event_post_update, + k_vg_event_render, + k_vg_event_gui, + k_vg_event_framebuffer_resize, + k_vg_event_invalid_options +}; -#define SDL_MAIN_HANDLED -#include "dep/sdl/include/SDL.h" +typedef struct vg_event_info vg_event_info; +struct vg_event_info +{ + enum vg_event type; + union + { + struct + { + ui_context *ctx; + } + gui; + + struct + { + i32 w, h; + } + framebuffer; + }; +}; -#include "vg_platform.h" -#include "vg_mem.h" -#include "vg_m.h" -#include "vg_font.h" -#include "vg_string.h" -#include "vg_ui/imgui.h" -#include "vg_async2.h" -#include "vg_tower.h" +typedef void (*vg_event_callback)( vg_event_info *info ); /* API */ -void vg_init( int argc, const char *argv[], const char *window_name ); -void vg_run(void); - -/* Thread 1 */ -void vg_bake_shaders(void); +VG_API void vg_run( int argc, const char *argv[], vg_event_callback fn ); /* Main thread */ -extern void vg_framebuffer_resize(int w, int h); -extern void vg_pre_update(void); -extern void vg_fixed_update(void); -extern void vg_post_update(void); - -extern void vg_render(void); -extern void vg_gui( ui_context *ctx ); - -void vg_settings_open(void); -void vg_settings_close(void); - -enum quality_profile{ +enum quality_profile +{ k_quality_profile_high = 0, k_quality_profile_low = 1, k_quality_profile_min = 2 }; -enum engine_status -{ - k_engine_status_none, - k_engine_status_load_internal, - k_engine_status_running, -}; +#define VG_THREAD_OWNS_SDL 0x1 +#define VG_THREAD_OWNS_OPENGL 0x2 +#define VG_THREAD_OWNS_STEAM 0x4 +#define VG_THREAD_BACKGROUND 0x100 +#define VG_THREAD_MAIN (VG_THREAD_OWNS_SDL|VG_THREAD_OWNS_OPENGL|VG_THREAD_OWNS_STEAM) +#define VG_THREAD_ASYNC (VG_THREAD_BACKGROUND) +#define VG_THREAD_MAIN_ID 0 +#define VG_THREAD_ASYNC_ID 1 +#define VG_TEMP_STACK_MAX 8 + +VG_API bool _vg_thread_has_flags( u32 flags ); +VG_API void _vg_thread_set_flags( u32 flags ); +VG_API const c8 *_vg_thread_prefix(void); + +VG_API void _vg_async_context_push_groups( u16 groups ); +VG_API void _vg_async_context_pop_groups(void); +VG_API u16 _vg_async_context_get_groups(void); + +VG_API void *_vg_async_alloc( u32 thread_id_target, u32 bytes ); +VG_API void _vg_async_send ( void *check_buffer, vg_async_fn fn ); +VG_API void _vg_add_exit_function( void( *fn )( void ) ); + +VG_API u32 _vg_start_temp_frame(void); +VG_API void _vg_end_temp_frame( u32 whence ); +VG_API vg_stack_allocator *_vg_temp_stack(void); + +VG_API void _vg_terminate(void); struct vg_engine { - vg_stack_allocator rtmem, scratch; - /* Engine sync */ - SDL_Window *window; - SDL_GLContext gl_context; - SDL_TLSID thread_purpose; - - vg_async_queue main_tasks, loader_tasks; - SDL_atomic_t engine_status; - - vg_signal_id sig_engine, sig_client; - - /* Window information */ - int window_x, - window_y, - samples, - window_should_close; - const char *base_path; - const char *window_name; - - int display_refresh_rate, - fps_limit, - vsync, - screen_mode, - display_index; - - int settings_open; - - enum vsync_feature{ - k_vsync_feature_disabled=0, - k_vsync_feature_enabled=1, - k_vsync_feature_enabled_adaptive=2, - k_vsync_feature_error=3 + SDL_TLSID thread_tls; + + struct vg_thread_context + { + u32 flags; + vg_stack_allocator temporary_memory; + + u32 temp_stack_depth, + temp_offsets[ VG_TEMP_STACK_MAX ]; + + u16 async_groups[ VG_TEMP_STACK_MAX ]; + u32 async_group_depth; + + vg_async_task *async_task; + vg_async_queue *async_task_queue; + const c8 *log_prefix; } - vsync_feature; + thread_contexts[3]; + + vg_async_queue thread_tasks[2], exit_tasks; + const c8 *base_path; i32 mouse_pos[2], mouse_state; v2f mouse_delta, mouse_wheel; /* Runtime */ - double time, - time_real, - time_delta, - time_rate, - - time_fixed_accumulator, - time_fixed_extrapolate, - time_frame_delta; - + f64 time, + time_real, + time_delta, + time_rate, + time_fixed_accumulator, + time_fixed_extrapolate, + time_frame_delta; f32 time_fixed_delta; u64 time_hp, time_hp_last, time_spinning; int fixed_iterations; - enum engine_stage + enum vg_gameloop_stage { - k_engine_stage_none, - k_engine_stage_update, - k_engine_stage_update_fixed, - k_engine_stage_rendering, - k_engine_stage_ui + k_gameloop_update, + k_gameloop_update_fixed, + k_gameloop_rendering, + k_gameloop_ui } - engine_stage; + gameloop_stage; /* graphics */ -#ifdef VG_3D +#if defined( VG_3D ) m4x4f pv; #else m3x3f pv; @@ -138,34 +150,15 @@ struct vg_engine i32 quality_profile; - float loader_ring; + f32 loader_ring; GLuint tex_missing; - vg_rand rand; - i32 load_step_delay; -} -extern vg; - -struct vg_ui -{ - GLuint vao, vbo, ebo; - m3x3f pv; - ui_context ctx; - GLuint tex_glyphs; - v2f inverse_font_sheet; - - SDL_Cursor *cursor_map[ k_ui_cursor_max ]; - /* at some point this should be implementation specific? */ - v4f colour; - f32 frosting; - v2f bg_inverse_ratio; - GLuint tex_bg; + i32 fps_limit; + u32 profiler; } -extern vg_ui; -void vg_ui_set_screen( i32 width, i32 height ); -void vg_ui_set_mouse_pos( ui_px x, ui_px y ); +extern vg; struct vg_setting_enum { @@ -191,18 +184,4 @@ void vg_settings_ui_header( ui_context *ctx, ui_rect inout_panel, const char *na bool vg_settings_apply_button( ui_context *ctx, ui_rect inout_panel, bool validated ); enum engine_status _vg_engine_status(void); -extern u32 _thread_purpose_main; -extern u32 _thread_purpose_loader; -u32 vg_thread_purpose(void); - -#define THREAD_0 VG_ASSERT( vg_thread_purpose() == _thread_purpose_main ) -#define THREAD_1 VG_ASSERT( vg_thread_purpose() == _thread_purpose_loader ) - -void vg_checkgl( const char *src_info ); -#define VG_STRINGIT( X ) #X -#define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) ) - -/* the few includes we always want. */ -#include "vg_log.h" -#include "vg_shader.h" -#include "vg_console.h" +#endif diff --git a/vg_font.h b/vg_font.h index 9a17561..4406418 100644 --- a/vg_font.h +++ b/vg_font.h @@ -1,5 +1,4 @@ -#pragma once - +#if !defined( VG_IMPLEMENTATION ) typedef struct vg_font_char vg_font_char; typedef struct vg_font_face vg_font_face; typedef struct vg_font_sheet vg_font_sheet; @@ -21,4 +20,4 @@ struct vg_font_sheet i16 w, h; u32 bitmap[]; }; - +#endif diff --git a/vg_framebuffer.c b/vg_framebuffer.c index 616d6a0..ceab2c5 100644 --- a/vg_framebuffer.c +++ b/vg_framebuffer.c @@ -1,6 +1,3 @@ -#include "vg_framebuffer.h" -#include "vg_platform.h" - struct { vg_framebuffer *list[16]; @@ -8,12 +5,29 @@ struct } static _vg_framebuffer; -void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y ) +VG_API vg_framebuffer *_vg_framebuffer_alloc( vg_stack_allocator *stack, u32 attachment_count, bool track ) +{ + u32 size = sizeof(vg_framebuffer) + sizeof(vg_framebuffer_attachment)*attachment_count; + vg_framebuffer *fb = vg_stack_allocate( stack, size, 8, "Framebuffer metadata" ); + vg_zero_mem( fb, size ); + + fb->attachment_count = attachment_count; + if( track ) + { + if( _vg_framebuffer.count != VG_ARRAY_LEN(_vg_framebuffer.list) ) + _vg_framebuffer.list[ _vg_framebuffer.count ++ ] = fb; + else + vg_fatal_error( "Framebuffer list is full, and tried to allocate another.\n"); + } + return fb; +} + +VG_API void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y ) { if( fb->resolution_div ) { - *x = vg.window_x / fb->resolution_div; - *y = vg.window_y / fb->resolution_div; + *x = _vg_window.w / fb->resolution_div; + *y = _vg_window.h / fb->resolution_div; } else { @@ -22,7 +36,7 @@ void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y ) } } -void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse ) +VG_API void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse ) { if( fb ) { @@ -36,11 +50,11 @@ void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse ) } else { - v2_div( (v2f){1.0f,1.0f}, (v2f){ vg.window_x, vg.window_y }, inverse ); + v2_div( (v2f){1.0f,1.0f}, (v2f){ _vg_window.w, _vg_window.h }, inverse ); } } -void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling ) +VG_API void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling ) { int x, y; vg_framebuffer_get_res( fb, &x, &y ); @@ -66,7 +80,7 @@ void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling ) glViewport( 0, 0, x, y ); } -void vg_framebuffer_bind_texture( vg_framebuffer *fb, int attachment, int slot ) +VG_TIER_0 void vg_framebuffer_bind_texture( vg_framebuffer *fb, u32 attachment, u32 slot ) { vg_framebuffer_attachment *at = &fb->attachments[attachment]; @@ -76,8 +90,7 @@ void vg_framebuffer_bind_texture( vg_framebuffer *fb, int attachment, int slot ) vg_fatal_error( "illegal operation: bind non-texture framebuffer attachment to texture slot" ); } - glActiveTexture( GL_TEXTURE0 + slot ); - glBindTexture( GL_TEXTURE_2D, fb->attachments[attachment].id ); + vg_tex_bind( GL_TEXTURE_2D, &fb->attachments[attachment].tex, slot ); } /* @@ -203,46 +216,31 @@ static const char *render_fb_format_str( GLenum format ) /* * Bind and allocate texture for framebuffer attachment */ -static void vg_framebuffer_allocate_texture( vg_framebuffer *fb, - vg_framebuffer_attachment *a ) +static void vg_framebuffer_allocate_texture( vg_framebuffer *fb, vg_framebuffer_attachment *a ) { + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); + if( a->tex.name == 0 ) + return; + int rx, ry; vg_framebuffer_get_res( fb, &rx, &ry ); if( a->purpose == k_framebuffer_attachment_type_renderbuffer ) { - glBindRenderbuffer( GL_RENDERBUFFER, a->id ); + glBindRenderbuffer( GL_RENDERBUFFER, a->tex.name ); glRenderbufferStorage( GL_RENDERBUFFER, a->internalformat, rx, ry ); } else if( a->purpose == k_framebuffer_attachment_type_texture || a->purpose == k_framebuffer_attachment_type_texture_depth ) { - glBindTexture( GL_TEXTURE_2D, a->id ); - glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry, - 0, a->format, a->type, NULL ); + glBindTexture( GL_TEXTURE_2D, a->tex.name ); + glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry, 0, a->format, a->type, NULL ); } } -vg_framebuffer *vg_framebuffer_allocate( vg_stack_allocator *stack, u32 attachment_count, bool track ) +VG_TIER_0 void vg_framebuffer_init( vg_framebuffer *fb ) { - vg_framebuffer *fb = VG_STACK_ALLOCATE_STRUCT( stack, vg_framebuffer ); - - if( track ) - { - if( _vg_framebuffer.count != VG_ARRAY_LEN(_vg_framebuffer.list) ) - _vg_framebuffer.list[ _vg_framebuffer.count ++ ] = fb; - else - vg_fatal_error( "Framebuffer list is full, and tried to allocate another.\n"); - } - - fb->attachments = vg_stack_allocate( stack, sizeof(vg_framebuffer_attachment) * attachment_count, 8, NULL ); - fb->attachment_count = attachment_count; - return fb; -} - -static void async_framebuffer_create( void *_fb ) -{ - vg_framebuffer *fb = _fb; + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); glGenFramebuffers( 1, &fb->id ); glBindFramebuffer( GL_FRAMEBUFFER, fb->id ); @@ -268,24 +266,24 @@ static void async_framebuffer_create( void *_fb ) if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer ) { - glGenRenderbuffers( 1, &attachment->id ); + glGenRenderbuffers( 1, &attachment->tex.name ); + attachment->tex.flags = VG_TEX_COMPLETE|VG_TEX_FRAMEBUFFER_ATTACHMENT|VG_TEX_PRIVATE|VG_TEX_NOMIP; + vg_framebuffer_allocate_texture( fb, attachment ); - glFramebufferRenderbuffer( GL_FRAMEBUFFER, - GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, attachment->id ); + glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, attachment->tex.name ); } else if( attachment->purpose == k_framebuffer_attachment_type_texture || - attachment->purpose == k_framebuffer_attachment_type_texture_depth ) + attachment->purpose == k_framebuffer_attachment_type_texture_depth ) { - glGenTextures( 1, &attachment->id ); + glGenTextures( 1, &attachment->tex.name ); + attachment->tex.flags = VG_TEX_COMPLETE|VG_TEX_FRAMEBUFFER_ATTACHMENT|VG_TEX_LINEAR|VG_TEX_CLAMP|VG_TEX_NOMIP; + vg_framebuffer_allocate_texture( fb, attachment ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - - glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment, - GL_TEXTURE_2D, attachment->id, 0 ); + glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment, GL_TEXTURE_2D, attachment->tex.name, 0 ); if( attachment->purpose == k_framebuffer_attachment_type_texture ) colour_attachments[ colour_count ++ ] = attachment->attachment; @@ -298,7 +296,6 @@ static void async_framebuffer_create( void *_fb ) * Check result */ GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER ); - if( result == GL_FRAMEBUFFER_COMPLETE ) { vg_success( " status: complete\n" ); @@ -319,13 +316,7 @@ static void async_framebuffer_create( void *_fb ) } } -void vg_framebuffer_create( vg_framebuffer *fb ) -{ - THREAD_1; - vg_async_call( &vg.main_tasks, async_framebuffer_create, fb ); -} - -void vg_framebuffer_free( vg_framebuffer *fb ) +VG_TIER_0 void vg_framebuffer_free( vg_framebuffer *fb ) { glDeleteFramebuffers( 1, &fb->id ); @@ -338,22 +329,23 @@ void vg_framebuffer_free( vg_framebuffer *fb ) if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer ) { - glDeleteRenderbuffers( 1, &attachment->id ); + glDeleteRenderbuffers( 1, &attachment->tex.name ); + vg_zero_mem( &attachment->tex, sizeof(vg_tex) ); } else if( attachment->purpose == k_framebuffer_attachment_type_texture || attachment->purpose == k_framebuffer_attachment_type_texture_depth ) { - glDeleteTextures( 1, &attachment->id ); + vg_tex_delete( &attachment->tex ); } } } -void vg_framebuffer_ui( ui_context *ctx ) +VG_API void vg_framebuffer_ui( ui_context *ctx ) { - ui_px w = vg.window_x/3, - h = vg.window_y/3; + ui_px w = _vg_window.w/3, + h = _vg_window.h/3; - ui_rect frame = {0,0,vg.window_x/3,vg.window_y/3}; + ui_rect frame = {0,0,_vg_window.w/3,_vg_window.h/3}; for( int i=0; i<_vg_framebuffer.count; i++ ) { @@ -390,7 +382,7 @@ void vg_framebuffer_ui( ui_context *ctx ) } ui_rect_center( frame, img ); - ui_image( ctx, img, &fb->attachments[j].id, 0 ); + ui_image( ctx, img, &fb->attachments[j].tex, 0 ); } ui_rect panel; @@ -403,7 +395,7 @@ void vg_framebuffer_ui( ui_context *ctx ) frame[0] += w; - if( (frame[0] + w) > vg.window_x ) + if( (frame[0] + w) > _vg_window.w ) { frame[0] = 0; frame[1] += h; @@ -412,13 +404,10 @@ void vg_framebuffer_ui( ui_context *ctx ) } } -static void vg_framebuffer_show( vg_framebuffer *fb, - vg_framebuffer_attachment *at, - int operation ) +static void vg_framebuffer_show( vg_framebuffer *fb, vg_framebuffer_attachment *at, int operation ) { at->debug_view = operation; - vg_info( "%s %s:%s\n", (operation? "shown": "hidden" ), - fb->display_name, at->display_name ); + vg_info( "%s %s:%s\n", (operation? "shown": "hidden" ), fb->display_name, at->display_name ); } /* @@ -531,22 +520,15 @@ static void vg_framebuffer_poll( int argc, char const *argv[] ) } } -void vg_framebuffer_register(void) +VG_API void _vg_framebuffer_register(void) { - //vg_console_reg_var( "blur_strength", &k_blur_strength, k_var_dtype_f32, 0 ); - //vg_console_reg_var( "render_scale", &k_render_scale, - // k_var_dtype_f32, VG_VAR_PERSISTENT ); - //vg_console_reg_var( "fov", &k_fov, k_var_dtype_f32, VG_VAR_PERSISTENT ); - //vg_console_reg_var( "cam_height", &k_cam_height, - // k_var_dtype_f32, VG_VAR_PERSISTENT ); - //vg_console_reg_var( "blur_effect", &k_blur_effect, - // k_var_dtype_i32, VG_VAR_PERSISTENT ); - vg_console_reg_cmd( "fb", vg_framebuffer_control, vg_framebuffer_poll ); } -void vg_framebuffer_update_sizes(void) +VG_API void vg_framebuffer_update_sizes(void) { + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); + for( int i=0; i<_vg_framebuffer.count; i++ ) { vg_framebuffer *fb = _vg_framebuffer.list[i]; diff --git a/vg_framebuffer.h b/vg_framebuffer.h index fef287d..df4ad6d 100644 --- a/vg_framebuffer.h +++ b/vg_framebuffer.h @@ -1,30 +1,30 @@ -/* - * Copyright (C) 2021-2024 Mt.ZERO Software, Harry Godden - All Rights Reserved - */ -#pragma once -#include "vg_engine.h" - +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_framebuffer.c" +#else typedef struct vg_framebuffer vg_framebuffer; typedef struct vg_framebuffer_attachment vg_framebuffer_attachment; +#define VG_FRAMEBUFFER_GLOBAL 1 +#define VG_FRAMEBUFFER_SPECIALIZED 0 + struct vg_framebuffer { - const char *display_name; - int resolution_div, /* If 0: Use fixed_w, fixed_h. - If non-0: Automatically size itself to - the window resolution divided by - this value */ - fixed_w, - fixed_h, - - render_w, /* The currently rendering resolution */ - render_h; + const c8 *display_name; + int resolution_div, /* If 0: Use fixed_w, fixed_h. + If non-0: Automatically size itself to + the window resolution divided by + this value */ + fixed_w, + fixed_h, + + render_w, /* The currently rendering resolution */ + render_h; GLuint id; + u32 attachment_count; struct vg_framebuffer_attachment { - const char *display_name; - + const c8 *display_name; enum vg_framebuffer_attachment_type { k_framebuffer_attachment_type_none, @@ -45,25 +45,24 @@ struct vg_framebuffer format, type, attachment; - - GLuint id; + vg_tex tex; /* Runtime */ int debug_view; } - *attachments; - u32 attachment_count; + attachments[]; }; /* * Initialize framebuffer system */ -void vg_framebuffer_register(void); +VG_API void _vg_framebuffer_register(void); +VG_API vg_framebuffer *_vg_framebuffer_alloc( vg_stack_allocator *stack, u32 attachment_count, bool track ); /* * Get the current (automatically scaled or fixed) resolution of framebuffer */ -void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y ); +VG_API void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y ); /* * Get the inverse ratio to project pixel coordinates (0->1920) to UV coordinates @@ -71,33 +70,27 @@ void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y ); * NOTE: won't necesarily use the full 0->1 range, but may index a subsection * of the framebuffer if using variable scale rendering. */ -void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse ); +VG_API void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse ); /* * Bind framebuffer for drawing to */ -void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling ); +VG_API void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling ); /* * Bind framebuffer attachment's texture */ -void vg_framebuffer_bind_texture( vg_framebuffer *fb, int attachment, int slot ); - -/* - * Allocation of a framebuffer memory. Optionally, track this framebuffer in - * debugging systems. - */ -vg_framebuffer *vg_framebuffer_allocate( vg_stack_allocator *stack, u32 attachment_count, bool track ); +VG_TIER_0 void vg_framebuffer_bind_texture( vg_framebuffer *fb, u32 attachment, u32 slot ); /* * Allocate graphics memory and initialize */ -void vg_framebuffer_create( vg_framebuffer *fb ); /* LOADER THREAD */ -void vg_framebuffer_free( vg_framebuffer *fb ); +VG_TIER_0 void vg_framebuffer_init( vg_framebuffer *fb ); +VG_TIER_0 void vg_framebuffer_free( vg_framebuffer *fb ); /* * Draw framebuffer debugging stuff */ -void vg_framebuffer_ui( ui_context *ctx ); - -void vg_framebuffer_update_sizes(void); +VG_API void vg_framebuffer_ui( ui_context *ctx ); +VG_API void vg_framebuffer_update_sizes(void); +#endif diff --git a/vg_input.c b/vg_input.c index 8933e98..8917e2d 100644 --- a/vg_input.c +++ b/vg_input.c @@ -1,10 +1,3 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ - -#include "vg_input.h" -#include "vg_loader.h" -#include "vg_engine.h" -#include "vg_string.h" - f32 controller_deadzone = 0.05f; struct vg_input vg_input = { @@ -256,8 +249,29 @@ void vg_process_inputs(void) vg_input.hidden_mouse_travel = 0.0f; } -static void async_vg_input_init( void *_ ) +VG_API void _vg_input_register(void) +{ + VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT ); +} + +static void vg_input_free(void) +{ + for( int i=0; ihandle ) + { + SDL_GameControllerClose( controller->handle ); + controller->handle = NULL; + } + } +} + +VG_API void _vg_input_init(void) { + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) ); + vg_info( "Checking for controllers\n" ); SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" ); @@ -272,31 +286,8 @@ static void async_vg_input_init( void *_ ) if( is_controller ) vg_open_gamecontroller( i ); } -} - -void vg_input_register(void) -{ - VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT ); -} -void vg_input_init(void) -{ - THREAD_1; - vg_async_call( &vg.main_tasks, async_vg_input_init, NULL ); -} - -void vg_input_free(void) -{ - for( int i=0; ihandle ) - { - SDL_GameControllerClose( controller->handle ); - controller->handle = NULL; - } - } + _vg_add_exit_function( vg_input_free ); } struct vg_controller *vg_active_controller(void) @@ -499,7 +490,7 @@ void vg_keyboard_key_string( vg_str *str, u32 key, int special_glyphs ) vg_strcat( str, special_glyphs? "\x96 ": "down" ); else { vg_strcat( str, "keyboard key #" ); - vg_strcati32( str, key ); + vg_strcati64( str, key, 10 ); } } @@ -516,7 +507,7 @@ void vg_mouse_button_string( vg_str *str, u32 button, int special_glyphs ) vg_strcat( str, special_glyphs? "\x9c": "middle mouse" ); else{ vg_strcat( str, "mouse button #" ); - vg_strcati32( str, button ); + vg_strcati64( str, button, 10 ); } } @@ -540,7 +531,7 @@ void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis, vg_strcat( str, special_glyphs?"\x8d":"right stick vertical" ); else{ vg_strcat( str, "axis " ); - vg_strcati32( str, axis ); + vg_strcati64( str, axis, 10 ); } } diff --git a/vg_input.h b/vg_input.h index bbb3f26..c6320c4 100644 --- a/vg_input.h +++ b/vg_input.h @@ -1,10 +1,10 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_input.c" +#else /* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ #define VG_MAX_CONTROLLERS 4 -#include "vg_platform.h" - extern f32 controller_deadzone; typedef u32 vg_input_op; @@ -72,22 +72,22 @@ struct vg_input } extern vg_input; +VG_API void _vg_input_register(void); +VG_API void _vg_input_init(void); + u8 vg_getkey( SDL_Keycode kc ); void vg_process_inputs(void); -void vg_input_register(void); -void vg_input_init(void); -void vg_input_free(void); struct vg_controller *vg_active_controller(void); u8 vg_controller_button( SDL_GameControllerButton button ); f32 vg_controller_axis( SDL_GameControllerAxis axis ); -void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops, - void *out_result ); +void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops, void *out_result ); const char *controller_button_str( SDL_GameControllerButton button ); void vg_keyboard_key_string( vg_str *str, u32 key, int special_glyphs ); void vg_mouse_button_string( vg_str *str, u32 button, int special_glyphs ); -void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis, - int special_glyphs ); +void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis, int special_glyphs ); void vg_joy_string( vg_str *str, vg_input_op op, int special_glyphs ); void vg_input_string( vg_str *str, vg_input_op *ops, int glyphs ); void vg_input_device_event( SDL_Event *ev ); void vg_input_controller_event( SDL_Event *ev ); + +#endif diff --git a/vg_io.c b/vg_io.c index cebcd7e..e778989 100644 --- a/vg_io.c +++ b/vg_io.c @@ -1,12 +1,3 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ - -#include "vg_io.h" -#include "vg_platform.h" -#include "vg_log.h" -#include "vg_mem.h" -#include -#include - const char *dir_open_result_str[] = { [k_dir_open_none] = "None", @@ -174,41 +165,155 @@ void vg_dir_close( vg_dir *dir ) dir->index = 0; } -void vg_file_error_info( FILE *fp ) +VG_TIER_0 void vg_buffer_stream_open( vg_stream *stream, const void *buffer, u32 buffer_length, u32 mode ) { - if( feof( fp )) + VG_ASSERT( buffer_length ); + VG_ASSERT( buffer ); + + stream->buffer = buffer; + stream->byte_limit = buffer_length; + stream->byte_count = 0; + stream->flags = mode; +} + +VG_TIER_0 bool vg_file_stream_open( vg_stream *stream, const c8 *path, u32 mode ) +{ +#if defined( VG_ENGINE ) + if( !_vg_thread_has_flags( VG_THREAD_BACKGROUND ) ) + vg_warn( "Performance: I/O file stream opened in main thread. This will cause frame stalls!\n" ); +#endif + stream->fp = fopen( path, (mode & VG_STREAM_WRITE)? "wb": "rb" ); + stream->byte_limit = 0; + stream->byte_count = 0; + stream->flags = mode | VG_STREAM_FILE; + + if( stream->fp ) + return 1; + else { - vg_error( "mdl_open: header too short\n" ); + vg_error( "Failed to open disk stream '%s'\n", path ); + return 0; + } +} + +VG_TIER_0 u32 vg_stream_usable_length( vg_stream *stream, u32 length ) +{ + if( stream->byte_limit && ((stream->byte_count + length) > stream->byte_limit) ) + length = stream->byte_limit - stream->byte_count; + return length; +} + +VG_TIER_0 u32 vg_stream_read( vg_stream *stream, void *buffer, u32 length ) +{ + VG_ASSERT( stream->flags & VG_STREAM_READ ); + u32 read_length = vg_stream_usable_length( stream, length ); + + if( stream->flags & VG_STREAM_FILE ) + { + u64 l = (u32)fread( buffer, 1, read_length, stream->fp ); + if( l != read_length ) + { + if( !feof( stream->fp ) ) + { + if( ferror( stream->fp ) ) + { + fclose( stream->fp ); + vg_fatal_error( "Read error (%u: %s)\n", (u32)errno, strerror(errno) ); + } + else + { + fclose( stream->fp ); + vg_fatal_error( "Unknown read error (fread)\n" ); + } + } + } + read_length = l; } else { - if( ferror( fp )) vg_info( "fopen: %s\n", strerror(errno) ); - else vg_info( "fopen: unkown failure\n" ); + for( u32 i=0; ibuffer)[stream->byte_count + i]; } + + for( u32 i=read_length; ibyte_count += read_length; + return read_length; } -#define VG_FILE_IO_CHUNK_SIZE VG_KB(256) +VG_TIER_0 u32 vg_stream_offset( vg_stream *stream ) +{ + return stream->byte_count; +} +VG_TIER_0 void vg_stream_seek( vg_stream *stream, u32 offset ) +{ + if( stream->flags & VG_STREAM_FILE ) + { + if( fseek( stream->fp, offset, SEEK_SET ) == -1 ) + { + vg_fatal_error( "fseek error (%u: %s)\n", (u32)errno, strerror(errno) ); + } + } + stream->byte_count = offset; +} + +VG_TIER_0 u32 vg_stream_write( vg_stream *stream, const void *buffer, u32 length ) +{ + VG_ASSERT( stream->flags & VG_STREAM_WRITE ); + u32 write_length = vg_stream_usable_length( stream, length ); + + if( stream->flags & VG_STREAM_FILE ) + { + u64 l = fwrite( buffer, 1, write_length, stream->fp ); + if( l != write_length ) + { + if( ferror( stream->fp ) ) + { + fclose( stream->fp ); + vg_fatal_error( "Write error (%u: %s)\n", (u32)errno, strerror(errno) ); + } + else + { + fclose( stream->fp ); + vg_fatal_error( "Unknown write error (fwrite)\n" ); + } + } + } + else + { + for( u32 i=0; ibuffer)[stream->byte_count + i] = ((u8 *)buffer)[i]; + } + + stream->byte_count += write_length; + return write_length; +} + +VG_TIER_0 void vg_file_stream_close( vg_stream *stream ) +{ + VG_ASSERT( stream->flags & VG_STREAM_FILE ); + fclose( stream->fp ); + stream->fp = NULL; +} + +#define VG_FILE_IO_CHUNK_SIZE VG_KB(256) /* read entire binary file */ void *vg_file_read( vg_stack_allocator *stack, const char *path, u32 *size, bool text ) { + VG_ASSERT( stack ); + FILE *f = fopen( path, "rb" ); if( f ) { - if( !stack ) - { - stack = alloca( sizeof(vg_stack_allocator) ); - vg_stack_init( stack, NULL, VG_FILE_IO_CHUNK_SIZE, "Stretchy file buffer" ); - vg_stack_set_flags( stack, VG_STACK_ALLOCATOR_DOUBLE_IF_FULL ); - } - u8 *buffer = vg_stack_allocate( stack, 0, 8, "File data" ); u64 current = 0; /* read in chunks */ for( u32 i=0; 1; i++ ) { - buffer = vg_stack_extend_last( stack, VG_FILE_IO_CHUNK_SIZE ); + vg_stack_extend_last( stack, +VG_FILE_IO_CHUNK_SIZE ); u64 l = fread( buffer + current, 1, VG_FILE_IO_CHUNK_SIZE, f ); current += l; @@ -234,11 +339,9 @@ void *vg_file_read( vg_stack_allocator *stack, const char *path, u32 *size, bool if( text ) { - buffer = vg_stack_extend_last( stack, 1 ); + vg_stack_extend_last( stack, +1 ); buffer[ current ++ ] = '\0'; } - - buffer = vg_stack_resize_last( stack, current ); fclose( f ); *size = (u32)current; diff --git a/vg_io.h b/vg_io.h index 6a84aaf..032e4d5 100644 --- a/vg_io.h +++ b/vg_io.h @@ -1,31 +1,51 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ - -#pragma once -#include "vg_platform.h" -#include "vg_log.h" -#include "vg_mem.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_io.c" +#else typedef struct vg_dir vg_dir; -#ifndef _WIN32 - #include - #include - #include - struct vg_dir{ - DIR *h; - struct dirent *data; - u32 index; - }; -#else - #include - #include - struct vg_dir{ + +#if defined( _WIN32 ) +struct vg_dir +{ HANDLE h; WIN32_FIND_DATA data; u32 index; - }; +}; +#else +struct vg_dir +{ + DIR *h; + struct dirent *data; + u32 index; +}; #endif -enum vg_entry_type{ +typedef struct vg_stream vg_stream; +struct vg_stream +{ + u32 flags, byte_limit, byte_count; + + union + { + FILE *fp; + const void *buffer; + }; +}; + +#define VG_STREAM_FILE 0x4 +#define VG_STREAM_WRITE 0x2 +#define VG_STREAM_READ 0x1 + +VG_TIER_0 bool vg_file_stream_open( vg_stream *stream, const c8 *path, u32 mode ); +VG_TIER_0 void vg_buffer_stream_open( vg_stream *stream, const void *buffer, u32 buffer_length, u32 mode ); +VG_TIER_0 u32 vg_stream_read( vg_stream *stream, void *buffer, u32 length ); +VG_TIER_0 u32 vg_stream_write( vg_stream *stream, const void *buffer, u32 length ); +VG_TIER_0 void vg_file_stream_close( vg_stream *stream ); +VG_TIER_0 u32 vg_stream_offset( vg_stream *stream ); +VG_TIER_0 void vg_stream_seek( vg_stream *stream, u32 offset ); + +enum vg_entry_type +{ k_vg_entry_type_unknown, k_vg_entry_type_file, k_vg_entry_type_dir @@ -59,4 +79,5 @@ void *vg_file_read( vg_stack_allocator *stack, const char *path, u32 *size, bool bool vg_asset_write( const char *path, void *data, i64 size ); const char *vg_path_filename( const char *path ); -void vg_file_error_info( FILE *fp ); + +#endif diff --git a/vg_kv.c b/vg_kv.c index aaf68a9..382a2a6 100644 --- a/vg_kv.c +++ b/vg_kv.c @@ -1,12 +1,13 @@ -#include "vg_kv.h" - static vg_kv *vg_kvs_newkv( vg_kvs *kvs ) { void *kv_page; if( (kvs->kv_page_count == VG_KV_PAGE_COUNT) || (kvs->kv_page_offset == 0) ) { u32 page_size = sizeof(vg_kv)*VG_KV_PAGE_COUNT; - kv_page = vg_stack_allocate( kvs->stack, page_size, 64, "KV Page" ); + + // FIXME FIXME: 64 is causing issues with metadata enabled buffers!!!!!!!!!!!!!!!!!!!!!! + // kv_page = vg_stack_allocate( kvs->stack, page_size, 64, "KV Page" ); + kv_page = vg_stack_allocate( kvs->stack, page_size, 8, "KV Page" ); vg_zero_mem( kv_page, page_size ); kvs->kv_page_offset = vg_stack_offset( kvs->stack, kv_page ); kvs->kv_page_count = 0; @@ -29,7 +30,7 @@ void vg_kvs_init( vg_kvs *kvs, vg_stack_allocator *stack ) kvs->root_offset = vg_stack_offset( kvs->stack, root_kv ); } -static u32 vg_kv_string_append( vg_kvs *kvs, c8 *string ) +static u32 vg_kv_string_append( vg_kvs *kvs, const c8 *string ) { if( string == NULL ) return 0; @@ -38,8 +39,11 @@ static u32 vg_kv_string_append( vg_kvs *kvs, c8 *string ) return vg_stack_offset( kvs->stack, buf ); } -u32 vg_kv_append( vg_kvs *kvs, u32 parent_offset, c8 *key, c8 *value ) +u32 vg_kv_append( vg_kvs *kvs, u32 parent_offset, const c8 *key, const c8 *value ) { + if( parent_offset == 0 ) + parent_offset = kvs->root_offset; + vg_kv *kv = vg_kvs_newkv( kvs ); u32 key_offset = vg_kv_string_append( kvs, key ), value_offset = vg_kv_string_append( kvs, value ); @@ -101,15 +105,12 @@ void vg_kv_link( vg_kv_parser *parser, u32 offset, u32 depth ) parser->frame_stack[ depth ].latest_child_offset = offset; } -void vg_kv_parse_buffer( vg_kv_parser *parser, c8 *buffer, u32 buffer_length ) +void vg_kv_parse_stream( vg_kv_parser *parser, vg_stream *in_stream ) { - if( buffer_length == 0 ) - buffer_length = 0xffffffff; - - for( u32 i=0; istat_source_characters ++; - char c = buffer[i]; if( c == '\0' ) break; @@ -130,7 +131,7 @@ void vg_kv_parse_buffer( vg_kv_parser *parser, c8 *buffer, u32 buffer_length ) if( parser->token0_length ) { parser->token0_length --; - parser->token0_buffer = vg_stack_extend_last( parser->kvs->stack, 1 ); + vg_stack_extend_last( parser->kvs->stack, +1 ); parser->token0_buffer[ parser->token0_length ] = '\0'; if( parser->token1_length ) @@ -194,7 +195,7 @@ void vg_kv_parse_buffer( vg_kv_parser *parser, c8 *buffer, u32 buffer_length ) { if( parser->token0_length ) { - parser->token0_buffer = vg_stack_extend_last( parser->kvs->stack, 1 ); + vg_stack_extend_last( parser->kvs->stack, +1 ); parser->token0_buffer[ parser->token0_length-1 ] = c; parser->token0_length ++; parser->token0_hash = ((parser->token0_hash << 5) + parser->token0_hash) + (u32)c; @@ -230,21 +231,30 @@ u32 vg_kv_type( vg_kvs *kvs, u32 kv_offset ) return (kv->key_info >> 30) & 0x3; } -c8 *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length ) +const c8 *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length ) { + if( kv_offset == 0 ) + return NULL; + vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset ); - *out_length = (kv->key_info >> 20) & 0x3FF; - return (*out_length)? vg_stack_pointer( kvs->stack, kv->key_offset ): NULL; + u32 length = (kv->key_info >> 20) & 0x3FF; + if( out_length ) + *out_length = length; + return length? vg_stack_pointer( kvs->stack, kv->key_offset ): NULL; } -c8 *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length ) -{ +const c8 *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length ) +{ + if( kv_offset == 0 ) + return NULL; + if( vg_kv_type( kvs, kv_offset ) == 0x0 ) return NULL; else { vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset ); - *out_length = kv->value.length; + if( out_length ) + *out_length = kv->value.length; return vg_stack_pointer( kvs->stack, kv->key_offset + kv->value.offset_from_key ); } } @@ -272,19 +282,22 @@ u32 vg_kv_child( vg_kvs *kvs, u32 root_offset, u32 index ) else return 0; } -u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, c8 *key ) +u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, const c8 *key ) { + if( root_offset == 0 ) + root_offset = kvs->root_offset; + u32 hash = vg_strdjb2( key ); u32 child_offset = vg_kv_child( kvs, root_offset, 0 ); while( child_offset ) { vg_kv *kv = vg_stack_pointer( kvs->stack, child_offset ); u32 key_length; - c8 *child_key = vg_kv_key( kvs, child_offset, &key_length ); + const c8 *child_key = vg_kv_key( kvs, child_offset, &key_length ); if( ((kv->key_info ^ hash) & 0xFFFFF) == 0 ) { u32 key_length; - c8 *child_key = vg_kv_key( kvs, child_offset, &key_length ); + const c8 *child_key = vg_kv_key( kvs, child_offset, &key_length ); if( child_key ) { for( u32 i=0; ifp ); } -static void vg_kv_write_string( vg_kv_write *w, c8 *string, u32 length ) +static void vg_kv_write_string( vg_kv_write *w, const c8 *string, u32 length ) { if( length == 0 ) length = 0xffffffff; @@ -341,7 +354,7 @@ static void vg_kv_write_string( vg_kv_write *w, c8 *string, u32 length ) if( delim ) fputc( delim, w->fp ); } -void vg_kv_write_block( vg_kv_write *w, c8 *name, u32 name_length ) +void vg_kv_write_block( vg_kv_write *w, const c8 *name, u32 name_length ) { vg_kv_write_indent( w ); if( name ) @@ -364,7 +377,7 @@ void vg_kv_end_block( vg_kv_write *w ) fputc( '\n', w->fp ); } -void vg_kv_write_kv( vg_kv_write *w, c8 *key, u32 key_len, c8 *value, u32 value_len ) +void vg_kv_write_kv( vg_kv_write *w, const c8 *key, u32 key_len, const c8 *value, u32 value_len ) { vg_kv_write_indent( w ); vg_kv_write_string( w, key, key_len ); @@ -387,10 +400,13 @@ void vg_kv_parser_print_info( vg_kv_parser *parser ) void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset ) { + if( root_offset == 0 ) + root_offset = kvs->root_offset; + VG_ASSERT( vg_kv_type( kvs, root_offset ) == 0x0 ); u32 root_len; - c8 *root_str = vg_kv_key( kvs, root_offset, &root_len ); + const c8 *root_str = vg_kv_key( kvs, root_offset, &root_len ); vg_kv_write_block( w, root_str, root_len ); u32 child_offset = vg_kv_child( kvs, root_offset, 0 ); @@ -401,10 +417,10 @@ void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset ) else { u32 key_len; - c8 *key_str = vg_kv_key( kvs, child_offset, &key_len ); + const c8 *key_str = vg_kv_key( kvs, child_offset, &key_len ); u32 value_len; - c8 *value_str = vg_kv_value( kvs, child_offset, &value_len ); + const c8 *value_str = vg_kv_value( kvs, child_offset, &value_len ); if( key_str && value_str ) vg_kv_write_kv( w, key_str, key_len, value_str, value_len ); @@ -415,3 +431,77 @@ void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset ) vg_kv_end_block( w ); } + +bool vg_kv_read_vu32( vg_kvs *kvs, u32 root_offset, const c8 *key, u32 *default_value, u32 *out_value, u32 len ) +{ + bool good = 1; + vg_strp s = { .buffer = vg_kv_value( kvs, vg_kv_find( kvs, root_offset, key ), NULL ) }; + for( u32 i=0; ifn_free(); - } -} - -struct async_set_information_args -{ - const char *string; -}; - -void async_loader_set_user_information( vg_async_task *task ) -{ - struct async_set_information_args *args = (void *)task->data; - vg_loader.information_for_user = args->string; } -void vg_loader_set_user_information( const char *information ) +VG_API void _vg_loader_set_user_information( const c8 *information ) { - if( vg_thread_purpose() == _thread_purpose_loader ) - { - vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, sizeof(struct async_set_information_args), 1 ); - struct async_set_information_args *args = (void *)task->data; - args->string = information; - vg_async_task_dispatch( task, async_loader_set_user_information ); - - if( vg.load_step_delay ) - SDL_Delay( vg.load_step_delay ); - } - else - vg_loader.information_for_user = information; + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); + vg_loader.information_for_user = information; } void vg_loader_render_ring( f32 opacity ) { + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); @@ -133,19 +33,21 @@ void vg_loader_render_ring( f32 opacity ) opacity *= opacity; - glUseProgram( _shader_loader.id ); - glUniform1f( glGetUniformLocation( _shader_loader.id, "uTime" ), vg.time_real ); - f32 ratio = (f32)vg.window_x / (f32)vg.window_y; - glUniform1f( glGetUniformLocation( _shader_loader.id, "uRatio"), ratio ); - glUniform1f( glGetUniformLocation( _shader_loader.id, "uOpacity"), opacity ); + shader_vgloader_use(); + shader_vgloader_uInverseRatio( (v2f){1.0f,1.0f} ); + shader_vgloader_uTime( vg.time_real ); + + f32 ratio = (f32)_vg_window.w / (f32)_vg_window.h; + shader_vgloader_uRatio( ratio ); + shader_vgloader_uOpacity( opacity ); glBindVertexArray( vg_loader.vao ); glDrawArrays( GL_TRIANGLES, 0, 6 ); ui_prerender( &vg_ui.ctx ); - vg_ui_set_screen( vg.window_x, vg.window_y ); + vg_ui_set_screen( _vg_window.w, _vg_window.h ); - ui_rect test = { 0, vg.window_y - 28*2, vg.window_x, 28 }; + ui_rect test = { 0, _vg_window.h - 28*2, _vg_window.w, 28 }; if( vg_loader.information_for_user ) { vg_ui.ctx.font = &vgf_default_large; @@ -159,35 +61,11 @@ void vg_loader_render_ring( f32 opacity ) void vg_loader_render(void) { - glViewport( 0,0, vg.window_x, vg.window_y ); + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); + glViewport( 0,0, _vg_window.w, _vg_window.h ); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); vg.loader_ring = 1.0f; } -/* - * Schedule something to be ran now, freed later. Checks in with engine status - */ -void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void), const char *alias ) -{ - THREAD_1; - - u64 t0 = SDL_GetPerformanceCounter(); - if( fn_load ) - fn_load(); - - u64 function_run_time = SDL_GetPerformanceCounter() - t0; - f64 run_time_seconds = (f64)function_run_time / (f64)SDL_GetPerformanceFrequency(); - vg_info( "%s: %fs\n", alias, run_time_seconds ); - - if( fn_free ) - { - struct loader_free_step step; - step.fn_free = fn_free; - - VG_ASSERT( vg_loader.step_count < VG_ARRAY_LEN(vg_loader.step_buffer) ); - vg_loader.step_buffer[ vg_loader.step_count ++ ] = step; - } -} - diff --git a/vg_loader.h b/vg_loader.h index 9dfb600..eb17e1c 100644 --- a/vg_loader.h +++ b/vg_loader.h @@ -1,17 +1,6 @@ - -/* - * Copyright 2021-2025 (C) Mount0 Software, Harry Godden - All Rights Reserved - * ----------------------------------------------------------------------------- - * - * Splash / load screen - * - * ----------------------------------------------------------------------------- - */ - -#pragma once - -#include "vg_platform.h" -#include "vg_engine.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_loader.c" +#else #define VG_ENGINE_READY 0x1 #define VG_CLIENT_READY 0x2 @@ -36,15 +25,10 @@ struct vg_loader } extern vg_loader; -//void vg_loader_start( void(*pfn)(void *data), void *data ); -void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void), const char *alias ); -void vg_loader_set_user_information( const char *information ); +VG_API void _vg_loader_init(void); + +VG_API void _vg_loader_set_user_information( const c8 *information ); void vg_loader_render(void); void vg_loader_render_ring( f32 opacity ); -void vg_loader_free(void); -void vg_loader_atexit(void); -void vg_loader_init(void); -//int vg_loader_availible(void); -#define vg_loader_step( FN, FN_FREE )\ - _vg_loader_step( FN, FN_FREE, #FN ) +#endif diff --git a/vg_log.c b/vg_log.c index 1eb4941..e8777d2 100644 --- a/vg_log.c +++ b/vg_log.c @@ -1,17 +1,3 @@ -#include -#include -#include -#include -#include "vg_platform.h" -#include "vg_log.h" -#include "vg_string.h" - -#ifdef VG_MULTITHREAD - #ifndef VG_ENGINE - #include - #endif -#endif - struct vg_log vg_log; static void _vg_log_append_line( const char *str ) @@ -26,7 +12,7 @@ static void _vg_log_append_line( const char *str ) vg_log.log_line_current = 0; } -void vg_log_init(void) +VG_API void _vg_log_pre_init(void) { vg_log.initialized = 1; @@ -39,19 +25,7 @@ void vg_log_init(void) if( !vg_strgood( &log_path ) ) exit(-2); -#ifdef VG_MULTITHREAD - #ifdef VG_ENGINE - vg_log.mutex = SDL_CreateMutex(); - if( vg_log.mutex == NULL ) - { - printf( "SDL2 Error: %s\n", SDL_GetError() ); - exit(-1); - } - #else - if( pthread_mutex_init( &vg_log.lock, NULL ) ) - exit(-1); - #endif -#endif + VG_ASSERT( VG_MUTEX_INIT( vg_log.lock ) ); } void _vg_logx_va( FILE *file, const char *location, const char *prefix, @@ -65,19 +39,12 @@ void _vg_logx_va( FILE *file, const char *location, const char *prefix, return; } -#ifdef VG_MULTITHREAD - #ifdef VG_ENGINE - if( SDL_LockMutex( vg_log.mutex ) ) - vg_fatal_error( "" ); - #else - pthread_mutex_lock( &vg_log.lock ); - #endif -#endif + VG_MUTEX_LOCK( vg_log.lock ); char buffer[4096], line[96]; vsnprintf( buffer, VG_ARRAY_LEN(buffer), fmt, args ); -#ifdef VG_ENGINE +#if defined( VG_ENGINE ) if( vg_log.plain_output_file ) { fputs( prefix, vg_log.plain_output_file ); @@ -86,7 +53,7 @@ void _vg_logx_va( FILE *file, const char *location, const char *prefix, } #endif - int line_length = snprintf( line, 90, "%s%7s" KNRM "|%s ", colour, prefix, colour ); + int line_length = snprintf( line, 90, "%s%3s" KNRM "|%s ", colour, prefix, colour ); for( u32 i=0; i - #include -#else - #include -#endif -#include -#include -#include - void vg_fatal_exit( const char *comment ) { +#if defined( VG_RELEASE_MODE ) int fd = open( vg_log.crash_path, O_CREAT | O_WRONLY, 0666 ); if( fd == -1 ) exit(-3); +#else + fflush( stdout ); + int fd = STDOUT_FILENO; +#endif char buf[ 1024 ]; vg_str line; vg_strnull( &line, buf, sizeof(buf) ); -#ifdef _WIN32 +#if defined( _WIN32 ) HANDLE process = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); CONTEXT context; @@ -267,6 +218,7 @@ void vg_fatal_exit( const char *comment ) backtrace_symbols_fd( functions, count, fd ); #endif +#if defined( VG_RELEASE_MODE ) vg_strcat( &line, "\n\nVG Console log\n" "-----------------------------------\n" ); @@ -302,5 +254,7 @@ void vg_fatal_exit( const char *comment ) } close( fd ); +#endif + exit(-1); } diff --git a/vg_log.h b/vg_log.h index 9781115..6c788b0 100644 --- a/vg_log.h +++ b/vg_log.h @@ -1,7 +1,6 @@ -#pragma once - -#include "vg_platform.h" -#include +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_log.c" +#else #define VG_LOG_MCSTR(S) VG_LOG_MCSTR2(S) #define VG_LOG_MCSTR2(S) #S @@ -9,17 +8,26 @@ " " #define vg_success( ... ) \ - vg_logx(stdout,VG_LOG_WHERE,"success",KGRN,__VA_ARGS__) + vg_logx(stdout,VG_LOG_WHERE,"ok",KGRN,__VA_ARGS__) #define vg_info( ... ) \ - vg_logx(stdout,VG_LOG_WHERE,"info",KNRM,__VA_ARGS__) + vg_logx(stdout,VG_LOG_WHERE,"inf",KNRM,__VA_ARGS__) #define vg_warn( ... ) \ - vg_logx(stdout,VG_LOG_WHERE,"warn",KYEL,__VA_ARGS__ ) + vg_logx(stdout,VG_LOG_WHERE,"wrn",KYEL,__VA_ARGS__ ) #define vg_error( ... ) \ - vg_logx(stdout,VG_LOG_WHERE,"error",KRED,__VA_ARGS__) + vg_logx(stdout,VG_LOG_WHERE,"err",KRED,__VA_ARGS__) #define vg_low( ... ) \ - vg_logx(stdout,VG_LOG_WHERE,"log",KWHT,__VA_ARGS__) + vg_logx(stdout,VG_LOG_WHERE,"log",KBLK,__VA_ARGS__) #define vg_logsteam( ... ) \ - vg_logx(stdout,VG_LOG_WHERE,"steam",KBLU,__VA_ARGS__) + vg_logx(stdout,VG_LOG_WHERE,"stm",KBLU,__VA_ARGS__) + +/* TODO: Virtualize these colour codes to be our own. These vt codes are uncomfy to code against + look bad if they + * accidently leak outside of the terminal. + * + * Instead we can do like: + * "Jim is [c red]a strange fellow" + * + * Or maybe there is a common standard? + */ #define KNRM "\x1B[0m" #define KBLK "\x1B[30m" @@ -31,27 +39,6 @@ #define KCYN "\x1B[36m" #define KWHT "\x1B[37m" -#define PRINTF_v2f( V2 ) "%.4f %.4f\n", V2[0], V2[1] -#define PRINTF_v3f( V3 ) "%.4f %.4f %.4f\n", V3[0], V3[1], V3[2] -#define PRINTF_v4f( V4 ) "%.4f %.4f %.4f %.4f\n", V4[0], V4[1], V4[2], V4[3] - -/* fuck off you stupid fucks */ -#ifdef _WIN32 - #define PRINTF_U64 "%llu" - #define PRINTF_X64 "%llx" -#else - #define PRINTF_U64 "%lu" - #define PRINTF_X64 "%lx" -#endif - -#ifdef VG_ENGINE - #include "dep/sdl/include/SDL.h" -#endif - -#ifdef VG_ENGINE - #define VG_MULTITHREAD -#endif - struct vg_log { char crash_path[256]; @@ -59,19 +46,15 @@ struct vg_log u32 log_line_count, log_line_current; FILE *plain_output_file; - bool initialized; -#ifdef VG_MULTITHREAD - #ifdef VG_ENGINE - SDL_mutex *mutex; - #else - pthread_mutex_t lock; - #endif + +#if defined( VG_MULTITHREAD ) + vg_mutex lock; #endif } extern vg_log; -void vg_log_init(void); +VG_API void _vg_log_pre_init(void); void vg_log_free(void); void vg_logx( FILE *file, @@ -83,3 +66,5 @@ void _vg_logx_va( FILE *file, const char *location, const char *prefix, const char *colour, const char *fmt, va_list args ); + +#endif diff --git a/vg_m.h b/vg_m.h index d582bf0..5a92c4c 100644 --- a/vg_m.h +++ b/vg_m.h @@ -1,4 +1,7 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved +#if !defined( VG_MATH_HEADER ) +# define VG_MATH_HEADER + +/* Copyright (C) 2021-2025 Harry Godden (hgn) - All Rights Reserved * * 0. Misc * 1. Scalar operations @@ -24,12 +27,6 @@ * 6.a Random numbers */ -#pragma once - -#include "vg_platform.h" -#include -#include - #define VG_PIf 3.14159265358979323846264338327950288f #define VG_TAUf 6.28318530717958647692528676655900576f @@ -2682,3 +2679,5 @@ static void vg_rgb_hsv( v3f rgb, v3f hsv ){ hsv[0] = vg_fractf( hsv[0] * (60.0f/360.0f) ); } + +#endif diff --git a/vg_magi.c b/vg_magi.c index 15ad090..71c1f2e 100644 --- a/vg_magi.c +++ b/vg_magi.c @@ -1,6 +1,3 @@ -#include "vg_magi.h" -#include "vg_console.h" - struct vg_magi _vg_magi; struct vg_magi_panel *_vg_magi_open( ui_px w, ui_px h, u32 flags ) @@ -263,7 +260,7 @@ void vg_magi_save(void) if( magi->flags & VG_MAGI_PERSISTENT ) { - ui_rect vp = {0,0,vg.window_x,vg.window_y}; + ui_rect vp = {0,0,_vg_window.w,_vg_window.h}; ui_px c[2], p[2]; vg_magi_getcorner( vp, magi->corner, c ); p[0] = magi->rect[0] - c[0]; @@ -311,7 +308,7 @@ static int cmd_vg_magi_dim( int argc, const char *argv[] ) if( argc >= 6 ) { - ui_rect vp = {0,0,vg.window_x,vg.window_y}; + ui_rect vp = {0,0,_vg_window.w,_vg_window.h}; ui_px c[2]; magi->corner = atoi( argv[5] ); vg_magi_getcorner( vp, magi->corner, c ); @@ -322,7 +319,7 @@ static int cmd_vg_magi_dim( int argc, const char *argv[] ) return 1; } -void vg_magi_init(void) +VG_API void _vg_magi_register(void) { vg_console_reg_cmd( "magi_pos", cmd_vg_magi_dim, NULL ); } diff --git a/vg_magi.h b/vg_magi.h index 0290914..d986e73 100644 --- a/vg_magi.h +++ b/vg_magi.h @@ -1,4 +1,7 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_magi.c" +#else + #define VG_MAGI_MAX_PANELS 8 #define VG_MAGI_RESIZEABLE 0x1 @@ -44,9 +47,12 @@ struct vg_magi } extern _vg_magi; +VG_API void _vg_magi_register(void); + struct vg_magi_panel *_vg_magi_open( ui_px w, ui_px h, u32 flags ); void _vg_magi_render( ui_context *ctx ); -void vg_magi_init(void); void vg_magi_save(void); void vg_magi_restore(void); void _vg_magi_area_change( i32 d[2] ); + +#endif diff --git a/vg_mem.c b/vg_mem.c index 9d3a730..f5acab6 100644 --- a/vg_mem.c +++ b/vg_mem.c @@ -1,13 +1,3 @@ -#pragma once - -#include "vg_platform.h" -#include "vg_log.h" -#include "vg_mem.h" - -#include -#include -#include - void *vg_malloc( u64 size ) { void *buf = malloc( size ); @@ -57,94 +47,37 @@ u32 vg_align4( u32 s ) void vg_stack_init( vg_stack_allocator *stack, void *buffer, u32 capacity, const char *debug_name ) { - VG_ASSERT( sizeof( vg_allocation_meta ) == 24 ); VG_ASSERT( sizeof( vg_stack_allocator ) == 24 ); - vg_zero_mem( stack, sizeof(vg_stack_allocator) ); stack->data = buffer? buffer: vg_malloc( capacity ); stack->capacity = capacity; stack->flags = buffer? 0: VG_STACK_ALLOCATOR_BUFFER_FROM_MALLOC; } -vg_stack_allocator *vg_stack_make_substack( vg_stack_allocator *parent, u32 capacity, const char *debug_name ) -{ - void *block = vg_stack_allocate( parent, sizeof(vg_stack_allocator) + capacity, 8, debug_name ); - vg_stack_set_flags_last( parent, VG_ALLOCATION_FLAG_IS_STACK ); - - vg_stack_allocator *stack = block; - vg_stack_init( stack, block + sizeof(vg_stack_allocator), capacity, debug_name ); - return stack; -} - -static void vg_stack_growmaybe( vg_stack_allocator *stack, u32 new_usage ) -{ - if( new_usage > stack->capacity ) - { - if( stack->flags & VG_STACK_ALLOCATOR_DOUBLE_IF_FULL ) - { - u32 cap = stack->capacity * 2; - if( new_usage > cap ) - cap = new_usage; - - stack->data = vg_realloc( stack->data, cap ); - stack->capacity = cap; - } - else - { - vg_fatal_error( "Stack allocator overflow (capacity: %u, used: %u, new_usage: %u)\n", - stack->capacity, stack->used, new_usage ); - } - } -} - void *vg_stack_allocate( vg_stack_allocator *stack, u32 size, u32 alignment, const char *debug_name ) { + if( !stack ) + return vg_malloc( size ); VG_ASSERT( (alignment >= 1) && (alignment <= 64) ); - VG_ASSERT( ~stack->used & 0x10000000 ); - VG_ASSERT( ~size & 0x10000000 ); - - u32 block_size = size; - if( stack->flags & VG_STACK_ALLOCATOR_METADATA ) - { - if( alignment > 8 ) - { - vg_fatal_error( "Allocation wants %u bytes alignment, but allocator has METADATA flag set." - " Alignment with this flag is always 8!\n", alignment ); - } - block_size += sizeof( vg_allocation_meta ); - alignment = 8; - } - - u32 previous_base_offset = stack->used - stack->last_allocation_totalsize; - stack->last_allocation_totalsize = block_size; - - u32 new_usage = stack->used + block_size + alignment; - vg_stack_growmaybe( stack, new_usage ); + VG_ASSERT( ~stack->offset & 0x10000000 ); + VG_ASSERT( ~size & 0x10000000 ); - while( ((u64)stack->data + stack->used) & (alignment-1) ) - stack->used ++; - - void *block = (void *)stack->data + stack->used; - stack->used += block_size; - - if( stack->flags & VG_STACK_ALLOCATOR_METADATA ) + u32 new_usage = stack->offset + size + alignment; + if( new_usage > stack->capacity ) { - vg_allocation_meta *meta = block; - memset( block, 0, sizeof(vg_allocation_meta) ); - meta->name = debug_name; - meta->size = size; - meta->previous_offset = previous_base_offset; - meta->flags = 0; - - return (void *)meta->data; + vg_fatal_error( "Stack allocator overflow (capacity: %u, used: %u, new_usage: %u)\n", + stack->capacity, stack->offset, new_usage ); } - else return block; + while( ((u64)stack->data + stack->offset) & (alignment-1) ) + stack->offset ++; + void *block = stack->data + stack->offset; + stack->offset += size; + return block; } void vg_stack_clear( vg_stack_allocator *stack ) { - stack->used = 0; - stack->last_allocation_totalsize = 0; + stack->offset = 0; } void vg_stack_free( vg_stack_allocator *stack ) @@ -154,19 +87,6 @@ void vg_stack_free( vg_stack_allocator *stack ) vg_zero_mem( stack, sizeof(vg_stack_allocator) ); } -void vg_stack_set_flags( vg_stack_allocator *stack, u16 append_flags ) -{ - if( append_flags & VG_STACK_ALLOCATOR_DOUBLE_IF_FULL ) - if( !(stack->flags & VG_STACK_ALLOCATOR_BUFFER_FROM_MALLOC) ) - vg_fatal_error( "Cannot append DOUBLE_IF_FULL to stack allocator, since buffer wasn't from malloc.\n" ); - - if( append_flags & VG_STACK_ALLOCATOR_METADATA ) - if( stack->used ) - vg_fatal_error( "Cannot append METADATA to stack allocator after allocating something.\n" ); - - stack->flags |= append_flags; -} - u32 vg_stack_offset( vg_stack_allocator *stack, void *pointer ) { return pointer - stack->data; @@ -177,56 +97,9 @@ void *vg_stack_pointer( vg_stack_allocator *stack, u32 offset ) return stack->data + offset; } -void vg_stack_set_flags_last( vg_stack_allocator *stack, u16 append_flags ) -{ - if( !(stack->flags & VG_STACK_ALLOCATOR_METADATA) ) - vg_fatal_error( "Stack allocator does not have the METADATA flag set.\n" ); - - vg_allocation_meta *meta = (void *)stack->data + (stack->used - stack->last_allocation_totalsize); - meta->flags |= append_flags; -} - -void *vg_stack_resize_last( vg_stack_allocator *stack, u32 new_size ) +void vg_stack_extend_last( vg_stack_allocator *stack, i32 extra_bytes ) { - u32 base_offset = stack->used - stack->last_allocation_totalsize, - block_size = new_size; - - if( stack->flags & VG_STACK_ALLOCATOR_METADATA ) - { - vg_allocation_meta *meta = (void *)stack->data + base_offset; - meta->size = new_size; - block_size += sizeof( vg_allocation_meta ); - } - - u32 new_usage = base_offset + block_size; - vg_stack_growmaybe( stack, new_usage ); - stack->used = new_usage; - stack->last_allocation_totalsize = block_size; - return stack->data + (new_usage - new_size); -} - -void *vg_stack_extend_last( vg_stack_allocator *stack, u32 extra_bytes ) -{ - u32 current_size = stack->last_allocation_totalsize; - if( stack->flags & VG_STACK_ALLOCATOR_METADATA ) - current_size -= sizeof(vg_allocation_meta); - return vg_stack_resize_last( stack, current_size + extra_bytes ); -} - -void *vg_stack_pop_last( vg_stack_allocator *stack ) -{ - if( !(stack->flags & VG_STACK_ALLOCATOR_METADATA) ) - vg_fatal_error( "Stack allocator does not have the METADATA flag set.\n" ); - - if( !stack->last_allocation_totalsize ) - return NULL; - - u32 base_offset = stack->used - stack->last_allocation_totalsize; - vg_allocation_meta *meta = (void *)stack->data + base_offset; - - stack->last_allocation_totalsize = base_offset - meta->previous_offset; - stack->used = base_offset; - return (void *)meta->data; + stack->offset += extra_bytes; } static void vg_mem_print_size( u32 bytes, char buf[32] ) @@ -275,3 +148,41 @@ void vg_mem_dumphex( FILE *fp, void *buffer, u32 offset, u32 bytes ) fprintf( fp, "----------------------------------------------------------------------------\n" ); } + +#if !defined( VG_ENGINE ) + +#define VG_TEMP_STACK_MAX 8 +vg_stack_allocator _temp_allocator; +u32 _temp_offsets[ VG_TEMP_STACK_MAX ]; +u32 _temp_stack_depth = 0; + +VG_API u32 _vg_start_temp_frame(void) +{ + if( !_temp_allocator.data ) + vg_stack_init( &_temp_allocator, NULL, VG_MB(20), "Temp allocator" ); + VG_ASSERT( _temp_stack_depth < VG_TEMP_STACK_MAX ); + u32 offset = _temp_allocator.offset; + _temp_offsets[ _temp_stack_depth ++ ] = offset; + return offset; +} + +VG_API void _vg_end_temp_frame( u32 whence ) +{ + VG_ASSERT( _temp_stack_depth ); + _temp_stack_depth --; + VG_ASSERT( _temp_offsets[ _temp_stack_depth ] == whence ); + _temp_allocator.offset = whence; +} + +VG_API void *_vg_temp_alloc( u32 bytes, u32 alignment ) +{ + VG_ASSERT( _temp_stack_depth ); + return vg_stack_allocate( &_temp_allocator, bytes, alignment, NULL ); +} + +VG_API vg_stack_allocator *_vg_temp_stack(void) +{ + return &_temp_allocator; +} + +#endif diff --git a/vg_mem.h b/vg_mem.h index 7a230c3..fd91e66 100644 --- a/vg_mem.h +++ b/vg_mem.h @@ -1,11 +1,11 @@ -#pragma once -#include "vg_platform.h" -#include -#include +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_mem.c" +#else #define VG_KB( X ) (X*1024) #define VG_MB( X ) (X*1024*1024) #define VG_GB( X ) (X*1024*1024*1024) +#define VG_STACK_USE_HEAP NULL void *vg_malloc( u64 size ); void *vg_realloc( void *buf, u64 size ); @@ -13,27 +13,6 @@ void vg_free( void *buf ); void vg_zero_mem( void *buffer, u32 length ); typedef struct vg_stack_allocator vg_stack_allocator; -typedef struct vg_allocation_meta vg_allocation_meta; - -#define VG_ALLOCATION_FLAG_IS_STACK 0x1 - -/* 24 bytes + size */ -struct vg_allocation_meta -{ - const char *name; - - u32 size; - u32 previous_offset; - u16 flags; - u16 unused0; - u32 unused1; - - u64 data[]; -}; - -/* configuration flags */ -#define VG_STACK_ALLOCATOR_METADATA 0x1 -#define VG_STACK_ALLOCATOR_DOUBLE_IF_FULL 0x2 /* system flags */ #define VG_STACK_ALLOCATOR_BUFFER_FROM_MALLOC 0x4 @@ -42,11 +21,9 @@ struct vg_allocation_meta struct vg_stack_allocator { u32 capacity; /* bytes */ - u32 used; - u32 last_allocation_totalsize; + u32 offset; u16 flags; - u16 unused0; - + u16 unused0, unused1; void *data; }; @@ -63,23 +40,21 @@ u32 vg_align4( u32 s ); */ void vg_stack_init( vg_stack_allocator *stack, void *buffer, u32 capacity, const char *debug_name ); void *vg_stack_allocate( vg_stack_allocator *stack, u32 size, u32 alignment, const char *debug_name ); +void *vg_stack_allocate_locked(); + void vg_stack_clear( vg_stack_allocator *stack ); void vg_stack_free( vg_stack_allocator *stack ); void vg_stack_set_flags( vg_stack_allocator *stack, u16 append_flags ); -vg_stack_allocator *vg_stack_make_substack( vg_stack_allocator *parent, u32 capacity, const char *debug_name ); - -#define VG_STACK_ALLOCATE_STRUCT( STACK, STRUCT ) vg_stack_allocate( STACK, sizeof(STRUCT), alignof(STRUCT), NULL ) +void vg_stack_extend_last( vg_stack_allocator *stack, i32 extra_bytes ); -__attribute__((warn_unused_result)) -void *vg_stack_resize_last( vg_stack_allocator *stack, u32 new_size ); - -__attribute__((warn_unused_result)) -void *vg_stack_extend_last( vg_stack_allocator *stack, u32 extra_bytes ); u32 vg_stack_offset( vg_stack_allocator *stack, void *pointer ); void *vg_stack_pointer( vg_stack_allocator *stack, u32 offset ); -/* - * The following are availible if allocator has the flag METADATA set - */ -void vg_stack_set_flags_last( vg_stack_allocator *stack, u16 append_flags ); -void *vg_stack_pop_last( vg_stack_allocator *stack ); +#if !defined( VG_ENGINE ) +VG_API u32 _vg_start_temp_frame(void); +VG_API void _vg_end_temp_frame( u32 whence ); +VG_API void *_vg_temp_alloc( u32 bytes, u32 alignment ); +VG_API vg_stack_allocator *_vg_temp_stack(void); +#endif + +#endif diff --git a/vg_mem_pool.c b/vg_mem_pool.c index 44ce62c..68e9d32 100644 --- a/vg_mem_pool.c +++ b/vg_mem_pool.c @@ -1,7 +1,3 @@ -#include "vg_platform.h" -#include "vg_mem.h" -#include "vg_mem_pool.h" - void vg_pool_switch( vg_pool *pool, vg_pool_chain *source, vg_pool_chain *dest, u16 which ) { VG_ASSERT( which ); @@ -65,7 +61,7 @@ u16 vg_pool_reference( vg_pool *pool, u16 pool_id, bool increment ) void vg_pool_init( vg_pool *pool, vg_pool_chain *start_chain, u16 count, vg_stack_allocator *stack ) { u32 size = sizeof(vg_pool_node) * count; - pool->nodes = stack? vg_stack_allocate( stack, size, 8, "Pool Nodes" ): vg_malloc( size ); + pool->nodes = vg_stack_allocate( stack, size, 8, "Pool Nodes" ); pool->count = count; for( u16 i=0; i -#include - void vg_queue_memcpy( vg_queue *q, void *dst, u32 start, u32 size ) { if( start + size > q->size ) @@ -28,10 +22,20 @@ void *vg_queue_tail_data( vg_queue *q ) else return NULL; } +u32 vg_queue_offset( vg_queue *q, void *pointer ) +{ + return pointer - q->buffer; +} + +void *vg_queue_pointer( vg_queue *q, u32 offset ) +{ + return q->buffer + offset; +} + /* - * Allocate memory on the queue. Returns NULL if allocation failed for any reason & out_offset undefined + * Allocate memory on the queue. Returns NULL if allocation failed for any reason */ -void *vg_queue_alloc( vg_queue *q, u32 size, u32 *out_offset ) +void *vg_queue_alloc( vg_queue *q, u32 size ) { u32 total = vg_align8(size) + sizeof(vg_queue_item); if( total > q->size ) @@ -80,15 +84,12 @@ void *vg_queue_alloc( vg_queue *q, u32 size, u32 *out_offset ) } vg_queue_item *item = (vg_queue_item *)(q->buffer + new_item_offset); - memset( item, 0, sizeof(vg_queue_item) ); + vg_zero_mem( item, sizeof(vg_queue_item) ); item->alloc_size = total; item->prev_size = prev_size; q->head_offset = new_item_offset; q->allocation_count ++; - - if( out_offset ) - *out_offset = new_item_offset; return item->data; } diff --git a/vg_mem_queue.h b/vg_mem_queue.h index 3af3de6..a16701f 100644 --- a/vg_mem_queue.h +++ b/vg_mem_queue.h @@ -1,4 +1,6 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_mem_queue.c" +#else #define VG_MEM_QUEUE_INVALID 0xffffffff @@ -19,10 +21,11 @@ struct vg_queue u32 allocation_count; }; -void *vg_queue_alloc( vg_queue *q, u32 size, u32 *out_offset ); +void *vg_queue_alloc( vg_queue *q, u32 size ); void *vg_queue_data( vg_queue *q, u32 offset ); void *vg_queue_tail_data( vg_queue *q ); +u32 vg_queue_offset( vg_queue *q, void *pointer ); /* warning: this is not the size but the allocation size (may be padded) */ u32 vg_queue_item_size( vg_queue *q, u32 item_id ); @@ -33,3 +36,5 @@ void vg_queue_memcpy( vg_queue *q, void *dst, u32 start, u32 size ); u32 vg_queue_usage( vg_queue *q ); void vg_queue_clear( vg_queue *q ); + +#endif diff --git a/vg_mem_view.c b/vg_mem_view.c index 5159ac8..e77edd4 100644 --- a/vg_mem_view.c +++ b/vg_mem_view.c @@ -1,5 +1,3 @@ -#include "vg_ui/imgui.h" - struct mem_view_data { void *base_buffer; @@ -304,6 +302,9 @@ static void cb_vg_mem_view( ui_context *ctx, ui_rect rect, struct vg_magi_panel ui_flush( ctx, k_ui_shader_colour, NULL ); ui_fill_rect( ctx, freq_box, 0xffffffff, (ui_px[4]){ 0,256,256,0 } ); + + // FIXME FIXME +#if 0 struct ui_batch_shader_data_image_gradient inf = { .resource = &mv->vis_tex, .log = 1, @@ -321,6 +322,7 @@ static void cb_vg_mem_view( ui_context *ctx, ui_rect rect, struct vg_magi_panel mv->route[ mv->route_depth ] = context.highlight_id; } } +#endif } } @@ -333,12 +335,11 @@ static void cb_mem_view_close( struct vg_magi_panel *me ) static struct { - const char *name; + const c8 *name; vg_stack_allocator *stack; } _vg_mem_named_buffers[] = { - { "rtmem", &vg.rtmem }, }; int _CB_vg_mem_infosort( const void *a, const void *b ) @@ -356,6 +357,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] ) return 0; } +#if 0 for( u32 i=0; iinfos[0].buffer_offset = 0; mv->infos[0].buffer_size = _vg_mem_named_buffers[i].stack->capacity; mv->infos[0].is_stack = 1; - mv->infos[0].stack_used_bytes = _vg_mem_named_buffers[i].stack->used; + mv->infos[0].stack_used_bytes = _vg_mem_named_buffers[i].stack->offset; mv->sizes[0] = sqrtf(mv->infos[0].buffer_size); mv->base_buffer = _vg_mem_named_buffers[i].stack->data; @@ -407,9 +409,9 @@ static int cmd_vg_mem_view( int argc, const char *argv[] ) frame->info->stack_children_count = 0; /* create array of all immediate child allocations */ - u32 offset = frame->stack->used - frame->stack->last_allocation_totalsize; + u32 offset = frame->stack->offset - frame->stack->last_allocation_totalsize; - if( (frame->stack->used == 0) || !(frame->stack->flags & VG_STACK_ALLOCATOR_METADATA) ) + if( (frame->stack->offset == 0) || !(frame->stack->flags & VG_STACK_ALLOCATOR_METADATA) ) goto l1; l2:{ @@ -434,7 +436,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] ) info->is_stack = 1; vg_stack_allocator *substack = mv->base_buffer + info->buffer_offset; - info->stack_used_bytes = substack->used; + info->stack_used_bytes = substack->offset; info->stack_children_start = 0; /* deferred to !frame->init */ info->stack_children_count = 0; } @@ -504,6 +506,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] ) return 1; } } +#endif vg_error( "No named buffer '%s'\n", argv[0] ); return 0; @@ -518,7 +521,7 @@ static void cmd_vg_mem_view_poll( int argc, const char *argv[] ) console_suggest_score_text( _vg_mem_named_buffers[i].name, term, 0 ); } -void vg_mem_view_register(void) +VG_API void _vg_mem_view_register(void) { vg_console_reg_cmd( "vg_mem_view", cmd_vg_mem_view, cmd_vg_mem_view_poll ); } diff --git a/vg_mem_view.h b/vg_mem_view.h index 29e793f..02c593f 100644 --- a/vg_mem_view.h +++ b/vg_mem_view.h @@ -1,4 +1,6 @@ -#pragma once -#include "vg_ui/imgui.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_mem_view.c" +#else extern int vg_mem_view; -void vg_mem_view_register(void); +VG_API void _vg_mem_view_register(void); +#endif diff --git a/vg_msg.c b/vg_msg.c index 35a937c..0457bd2 100644 --- a/vg_msg.c +++ b/vg_msg.c @@ -1,10 +1,4 @@ -#include "vg_msg.h" -#include "vg_platform.h" -#include "vg_string.h" -#include -#include - -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* write a buffer from msg, rang checked. */ void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len ) { @@ -34,7 +28,7 @@ void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len ) } } -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* write null terminated string to stream */ void vg_msg_wstr( vg_msg *msg, const char *str ) { @@ -66,7 +60,7 @@ const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ) return str; } -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* begin a new frame in message stream */ void vg_msg_frame( vg_msg *msg, const char *name ) { @@ -129,7 +123,7 @@ u8 vg_msg_count_bits( u32 count ) return ((count-1)<<2); } -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* write a sized type */ void vg_msg_wkvnum( vg_msg *msg, const char *key, u8 type, u8 count, void *data ) { @@ -162,14 +156,13 @@ int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ) vg_msg_rbuf( msg, &cmd->code, 1 ); if( msg->error != k_vg_msg_error_OK ) return 0; -#ifdef VG_MSG_V1_SUPPORT /* |sized| |count-1| |shift| * 0 1 0 0 0 1 0 0 0x44 (1 byte, float[2]. So, never used anyway) * converts to * 1 0 0 0 0 0 1 0 0x82 */ if( cmd->code == 0x44 ) cmd->code = 0x82; -#endif + cmd->key_djb2 = 0; if( msg->error != k_vg_msg_error_OK ) return 0; @@ -218,7 +211,7 @@ int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ) return 1; } -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* move through the frame(and subframes) until we fall out of it */ int vg_msg_skip_frame( vg_msg *msg ) { @@ -327,7 +320,7 @@ f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size ) return (f64)vg_msg_cast_to_i64( src, src_base, src_size ); } -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* * Convert any full integral type code to another * Passing in non-integral codes is undefined @@ -403,7 +396,7 @@ int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd ) return 0; } -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) /* * Read a integral KV out to dst, and perform conversion if needed * dst is always defined, if its not found its set to 0 @@ -438,7 +431,7 @@ const char *vg_msg_getkvstr( vg_msg *msg, const char *key ) } #endif -#if !defined( VG_MSG_TO_KVS ) +#if defined( VG_MSG_LEGACY ) int vg_msg_getkvvecf( vg_msg *msg, const char *key, u8 type, void *v, void *default_value ) { @@ -526,8 +519,7 @@ void vg_msg_print( vg_msg *msg, u32 len ) #endif #if defined( VG_MSG_TO_KVS ) -#include "vg_kv.h" -bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len, vg_stack_allocator *stack ) +bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len ) { vg_msg b; vg_msg_init( &b, buffer, len ); @@ -574,21 +566,21 @@ bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 le if( base == k_vg_msg_unsigned ) { u64 val = vg_msg_cast_to_u64( p, base, size ); - vg_strcatu64_internal( &value_str, val, 10, 0 ); + vg_strcatu64( &value_str, val, 10 ); } else if( base == k_vg_msg_signed ) { i64 val = vg_msg_cast_to_i64( p, base, size ); - vg_strcati64( &value_str, val, 10, 0 ); + vg_strcati64( &value_str, val, 10 ); } else { f64 val = vg_msg_cast_to_f64( p, base, size ); - vg_strcatf64( &value_str, val, 10, 0 ); + vg_strcatf64( &value_str, val, 10, 5 ); } if( i+1!=count ) - vg_strcatch( &format_str, ' ' ); + vg_strcatch( &value_str, ' ' ); } if( !vg_strgood( &value_str ) ) diff --git a/vg_msg.h b/vg_msg.h index 5f48586..897153e 100644 --- a/vg_msg.h +++ b/vg_msg.h @@ -1,83 +1,6 @@ -#pragma once -/* - * Example data: - * kvstr "someinfo" - * kvint 200 - * frame "person"{ - * name "jeff" - * country "england" - * } - * frame "building"{ - * capacity 1000 - * } - * frame "person"{ - * country "wales" - * name "micheal" - * } - * - * Creating the data in code: - * ----------------------------------------------------------------------------- - * u8 data_buf[512]; - * vg_msg data; - * vg_msg_init( &data, data_buf, 512 ); - * - * vg_msg_wkvstr( &data, "kvstr", "someinfo" ); - * vg_msg_wkvu32( &data, "kvint", 200 ); - * - * vg_msg_frame( &data, "person" ); - * vg_msg_wkvstr( &data, "name", "jeff" ); - * vg_msg_wkvstr( &data, "country", "england" ); - * vg_msg_end_frame( &data ); - * - * vg_msg_frame( &data, "building" ); - * vg_msg_wkvu32( &data, "capacity", 1000 ); - * vg_msg_end_frame( &data ); - * - * vg_msg_frame( &data, "person" ); - * vg_msg_wkvstr( &data, "country", "wales" ); - * vg_msg_wkvstr( &data, "name", "micheal" ); - * vg_msg_end_frame( &data ); - * - * Saving the data out - * ----------------------------------------------------------------------------- - * - * if( data.error == k_vg_msg_error_OK ){ - * // write data_buf, for length data.cur - * } - * - * Load the data - * ----------------------------------------------------------------------------- - * - * u8 data_buf[512]; - * u32 data_len; - * - * // read data_buf and data_len - * - * vg_msg data; - * vg_msg_init( &data, data_buf, data_len ); - * - * - * Reading data - * ----------------------------------------------------------------------------- - * - * if( vg_msg_seekframe( &msg, "rows" ) ){ - * while( vg_msg_seekframe( &msg, NULL ) ){ - * vg_warn( "%s\n", vg_msg_readkvstr( &msg, "yedo" ) ); - * vg_msg_skip_frame( &msg ); - * } - * } - * - * Reading back the stream linearly - * ----------------------------------------------------------------------------- - * - * vg_msg_cmd cmd; - * while( vg_msg_next( &data, &cmd ) ){ - * if( cmd.code == k_vg_msg_frame ) printf( "{" ); - * else if( cmd.code == k_vg_msg_endframe ) printf( "}" ); - * esle if( cmd.code == k_vg_msg_kvstring ) - * printf( "string: %s\n", cmd.value._buf ); - * } - */ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_msg.c" +#else enum vg_msg_code{ /* low types */ @@ -159,27 +82,30 @@ struct vg_msg_cmd u32 len; /* set if binary type */ }; -void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len ); +u32 vg_msg_cmd_array_count( u8 code ); +u32 vg_msg_cmd_type_size( u8 code ); +u32 vg_msg_cmd_bytecount( u8 code ); +u8 vg_msg_count_bits( u32 count ); + void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len ); -void vg_msg_wstr( vg_msg *msg, const char *str ); const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ); +u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size ); +i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size ); +f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size ); + +#if defined( VG_MSG_LEGACY ) +void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len ); +void vg_msg_wstr( vg_msg *msg, const char *str ); void vg_msg_frame( vg_msg *msg, const char *name ); void vg_msg_end_frame( vg_msg *msg ); void vg_msg_wkvstr( vg_msg *msg, const char *key, const char *value ); void vg_msg_wkvbin( vg_msg *msg, const char *key, u8 *bin, u32 len ); void vg_msg_wkvnum( vg_msg *msg, const char *key, u8 type, u8 count, void *data ); -u32 vg_msg_cmd_array_count( u8 code ); -u32 vg_msg_cmd_type_size( u8 code ); -u32 vg_msg_cmd_bytecount( u8 code ); -u8 vg_msg_count_bits( u32 count ); void vg_msg_init( vg_msg *msg, u8 *buffer, u32 len ); int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ); int vg_msg_skip_frame( vg_msg *msg ); int vg_msg_seekframe( vg_msg *msg, const char *name ); -u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size ); -i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size ); -f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size ); void vg_msg_cast( const void *src, u8 src_code, void *dst, u8 dst_code ); int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd ); @@ -187,14 +113,16 @@ int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd ); /* * Read a integral KV out to dst, and perform conversion if needed */ -int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst, - void *default_value ); +int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst, void *default_value ); /* helper for reading string kvs. returns NULL if not found */ const char *vg_msg_getkvstr( vg_msg *msg, const char *key ); -int vg_msg_getkvvecf( vg_msg *msg, const char *key, u8 type, - void *v, void *default_value ); +int vg_msg_getkvvecf( vg_msg *msg, const char *key, u8 type, void *v, void *default_value ); void vg_msg_print( vg_msg *msg, u32 len ); +#endif + +#if defined( VG_MSG_TO_KVS ) +bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len ); +#endif -#include "vg_kv.h" -void vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len, vg_stack_allocator *stack ); +#endif diff --git a/vg_mutex.h b/vg_mutex.h index 7e9974c..b30507c 100644 --- a/vg_mutex.h +++ b/vg_mutex.h @@ -1,34 +1,45 @@ -#ifdef VG_ENGINE -#define vg_semaphore SDL_sem * -#define vg_mutex SDL_mutex * -#define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (SEMAPHORE = SDL_CreateSemaphore( VALUE )) -#define VG_SEMAPHORE_FREE( SEMAPHORE ) SDL_DestroySemaphore( SEMAPHORE ); SEMAPHORE = NULL; -#define VG_MUTEX_INIT( MUTEX ) (MUTEX = SDL_CreateMutex()) -#define VG_MUTEX_FREE( MUTEX ) SDL_DestroyMutex( MUTEX ); MUTEX = NULL; -#define VG_MUTEX_LOCK( MUTEX ) SDL_LockMutex( MUTEX ) -#define VG_MUTEX_UNLOCK( MUTEX ) SDL_UnlockMutex( MUTEX ) -#define VG_SEMAPHORE_WAIT( SEMAPHORE ) SDL_SemWait( SEMAPHORE ) -#define VG_SEMAPHORE_POST( SEMAPHORE ) SDL_SemPost( SEMAPHORE ) -#define VG_SEMAPHORE_VALUE( SEMAPHORE ) SDL_SemValue( SEMAPHORE ) -#else -#include -#include -#define vg_semaphore sem_t -#define vg_mutex pthread_mutex_t -#define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (sem_init( &SEMAPHORE, 0, VALUE )==0) -#define VG_SEMAPHORE_FREE( SEMAPHORE ) sem_destroy( &SEMAPHORE ) -#define VG_SEMAPHORE_WAIT( SEMAPHORE ) sem_wait( &SEMAPHORE ) -#define VG_SEMAPHORE_POST( SEMAPHORE ) sem_post( &SEMAPHORE ) +#if defined( VG_MULTITHREAD ) +# if defined( VG_ENGINE ) +# define vg_semaphore SDL_sem * +# define vg_mutex SDL_mutex * +# define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (SEMAPHORE = SDL_CreateSemaphore( VALUE )) +# define VG_SEMAPHORE_FREE( SEMAPHORE ) SDL_DestroySemaphore( SEMAPHORE ); SEMAPHORE = NULL; +# define VG_MUTEX_INIT( MUTEX ) (MUTEX = SDL_CreateMutex()) +# define VG_MUTEX_FREE( MUTEX ) SDL_DestroyMutex( MUTEX ); MUTEX = NULL; +# define VG_MUTEX_LOCK( MUTEX ) SDL_LockMutex( MUTEX ) +# define VG_MUTEX_UNLOCK( MUTEX ) SDL_UnlockMutex( MUTEX ) +# define VG_SEMAPHORE_WAIT( SEMAPHORE ) SDL_SemWait( SEMAPHORE ) +# define VG_SEMAPHORE_POST( SEMAPHORE ) SDL_SemPost( SEMAPHORE ) +# define VG_SEMAPHORE_VALUE( SEMAPHORE ) SDL_SemValue( SEMAPHORE ) +# else +# define vg_semaphore sem_t +# define vg_mutex pthread_mutex_t +# define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (sem_init( &SEMAPHORE, 0, VALUE )==0) +# define VG_SEMAPHORE_FREE( SEMAPHORE ) sem_destroy( &SEMAPHORE ) +# define VG_SEMAPHORE_WAIT( SEMAPHORE ) sem_wait( &SEMAPHORE ) +# define VG_SEMAPHORE_POST( SEMAPHORE ) sem_post( &SEMAPHORE ) static inline i32 get_semaphore_value( sem_t *sem ) { int value = 0; sem_getvalue( sem, &value ); return value; } -#define VG_SEMAPHORE_VALUE( SEMAPHORE ) get_semaphore_value( &SEMAPHORE ) -#define VG_MUTEX_INIT( MUTEX ) (pthread_mutex_init( &MUTEX, NULL )==0) -#define VG_MUTEX_FREE( MUTEX ) pthread_mutex_destroy( &MUTEX ) -#define VG_MUTEX_LOCK( MUTEX ) pthread_mutex_lock( &MUTEX ) -#define VG_MUTEX_UNLOCK( MUTEX ) pthread_mutex_unlock( &MUTEX ) +# define VG_SEMAPHORE_VALUE( SEMAPHORE ) get_semaphore_value( &SEMAPHORE ) +# define VG_MUTEX_INIT( MUTEX ) (pthread_mutex_init( &MUTEX, NULL )==0) +# define VG_MUTEX_FREE( MUTEX ) pthread_mutex_destroy( &MUTEX ) +# define VG_MUTEX_LOCK( MUTEX ) pthread_mutex_lock( &MUTEX ) +# define VG_MUTEX_UNLOCK( MUTEX ) pthread_mutex_unlock( &MUTEX ) +# endif +#else +# define vg_semaphore void * +# define vg_mutex void * +# define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) 1 +# define VG_SEMAPHORE_FREE( SEMAPHORE ) ; +# define VG_MUTEX_INIT( MUTEX ) 1 +# define VG_MUTEX_FREE( MUTEX ) ; +# define VG_MUTEX_LOCK( MUTEX ) 1 +# define VG_MUTEX_UNLOCK( MUTEX ) 1 +# define VG_SEMAPHORE_WAIT( SEMAPHORE ) ; +# define VG_SEMAPHORE_POST( SEMAPHORE ) ; +# define VG_SEMAPHORE_VALUE( SEMAPHORE ) 0 #endif - diff --git a/vg_opt.c b/vg_opt.c index ce224e3..a9a966a 100644 --- a/vg_opt.c +++ b/vg_opt.c @@ -1,12 +1,3 @@ -/* - * Copyright (C) 2021-2025 Mt.ZERO Software, Harry Godden - All Rights Reserved - */ - -#include "vg_opt.h" -#include "vg_platform.h" -#include "vg_log.h" -#include - /* * Supported: * short flags | -abc diff --git a/vg_opt.h b/vg_opt.h index 12e6187..bd8f498 100644 --- a/vg_opt.h +++ b/vg_opt.h @@ -1,9 +1,6 @@ -/* - * Copyright (C) 2021-2025 Mt.ZERO Software - All Rights Reserved - */ - -#pragma once -#include "vg_platform.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_opt.c" +#else void _vg_opt_init( int argc, const char *argv[] ); bool _vg_opt_check(void); @@ -22,3 +19,5 @@ const char *vg_long_opt_arg( char *name, const char *desc ); /* Example: get regular_thing */ const char *vg_arg( u32 index ); + +#endif diff --git a/vg_perlin.c b/vg_perlin.c index 6b8b087..6647a91 100644 --- a/vg_perlin.c +++ b/vg_perlin.c @@ -1,6 +1,3 @@ -#include "vg_m.h" -#include "vg_perlin.h" - static int perlin_hash[] = { 0x46,0xD5,0xB8,0xD3,0xF2,0xE5,0xCC,0x07,0xD0,0xB3,0x7A,0xA2,0xC3,0xDA,0xDC,0x7F, 0xE0,0xB7,0x42,0xA0,0xBF,0x41,0x92,0x32,0x6F,0x0D,0x45,0xC7,0x54,0xDB,0x30,0xC2, diff --git a/vg_perlin.h b/vg_perlin.h index 1d28c76..56d240c 100644 --- a/vg_perlin.h +++ b/vg_perlin.h @@ -1,6 +1,10 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_perlin.c" +#else f32 vg_perlin_noise2d( f32 x, f32 y, int seed ); f32 vg_perlin_noise1d( f32 v, int seed ); f32 vg_perlin_fract_1d( f32 v, f32 freq, int octaves, int seed ); f32 vg_perlin_fract_2d( f32 x, f32 y, f32 freq, int octaves, int seed ); + +#endif diff --git a/vg_platform.h b/vg_platform.h index 5c6e3e6..7f3ddb1 100644 --- a/vg_platform.h +++ b/vg_platform.h @@ -1,7 +1,68 @@ -#pragma once +#if !defined( VG_IMPLEMENTATION ) -#include -#include +/* + * Tiers: each tier can only access functions from the tier below it. + * All client code is considered to be above VG_TIER_HL + */ + +#define VG_TIER_0 +/* Tier0: subroutines + * + * Operate on simple structures or arrays, and return a code to indicate status or void. Operates purely in stack + * area. Each function should be likely inlineable, each invocation will take up a MAXIMUM of 8kb + * + * Example functions: + * v3_add( v3f a, v3f b, v3f d ); + * u32 i32_to_string( i32 i, char buffer[32] ); + * + * Restrictions: + * No dynamic allocation + * No dynamic fatal errors (only assertions allowed) + * No globals / external interaction + * No multi-threading + */ + +#define VG_TIER_1 +/* Tier1: Allocating routines + * + * These functions are ones who require dynamic memory, or operating sizes larger than 8kb. They will either; + * - Take some allocator as a parameter eg. vg_stack_allocator + * - Or reference a structure that happens to have an allocator attached earlier, like vg_str. + * + * The only fatal errors that are allowed here are out of memory errors (eg. malloc fails, or an allocator overflows). + * + * Example functions: + * void vg_strcat( vg_str *str, const c8 *substr ); + * + * Restrictions: + * No globals + * Must be thread safe / does not START anything asynchronously, but can be an async call itself. + */ + +#define VG_TIER_2 +/* Tier2: Composite allocating routines + * + * Very similar to tier 1, except will allocate in multiple places and/or make use of scratch allocators. + * These functions typically take a vg_mem_context structure which defines how exactly memory is to be allocated. + */ + +#define VG_API +#define VG_API_INTERNAL +/* Tier HL: Global, high level system functions + * + * These functions typically do not take any allocators, as they are pre-determined by the systems. Raw data and + * allocations are innaccessible to client code, and functions on this level typically return a handle of small size. + * (usually from a pool, or circular buffer). + * + * These types of functions are often asynchronous + * + * Example functions: + * u32 vg_texture_handle( const c8 *path ); + * void vg_texture_release( u32 id ); + */ + +#define VG_ENGINE_HOOK( X ) +#define VG_STACK_INLINE typedef uint8_t u8; typedef char c8; @@ -16,18 +77,17 @@ typedef float f32; typedef double f64; typedef uint8_t bool; -typedef unsigned int uint; -typedef int v2i[2]; -typedef int v3i[3]; -typedef int v4i[4]; -typedef float v2f[2]; -typedef float v3f[3]; -typedef float v4f[4]; -typedef v2f m2x2f[2]; -typedef v3f m3x3f[3]; -typedef v3f m4x3f[4]; -typedef v4f m4x4f[4]; -typedef v3f boxf[2]; +typedef i32 v2i[2]; +typedef i32 v3i[3]; +typedef i32 v4i[4]; +typedef f32 v2f[2]; +typedef f32 v3f[3]; +typedef f32 v4f[4]; +typedef v2f m2x2f[2]; +typedef v3f m3x3f[3]; +typedef v3f m4x3f[4]; +typedef v4f m4x4f[4]; +typedef v3f boxf[2]; /* anything compiled against VG shall implement vg_fatal_exit() somewhere. */ void vg_fatal_exit( const char *comment ); @@ -41,3 +101,5 @@ void vg_fatal_error( const char *fmt, ... ); #define VG_MIN( A, B ) ((A)<(B)?(A):(B)) #define VG_MAX( A, B ) ((A)>(B)?(A):(B)) #define VG_ARRAY_LEN( A ) (sizeof(A)/sizeof(A[0])) + +#endif diff --git a/vg_profiler.c b/vg_profiler.c index aa7b16f..f4bd4fc 100644 --- a/vg_profiler.c +++ b/vg_profiler.c @@ -1,147 +1,197 @@ -#include "vg_platform.h" -#include "vg_profiler.h" -#include "vg_engine.h" -#include "vg_ui/imgui.h" +#define PROFILER_ROW_MAX 16 +#define PROFILER_STACK_MAX 8 +#define PROFILER_HISTORY_LENGTH 64 -struct _vg_profiler _vg_profiler; +struct vg_profiler +{ + const c8 *name; + f32 history_ms[ PROFILER_HISTORY_LENGTH ][ PROFILER_ROW_MAX ]; + u32 buffer_current; + + u64 row_accumulated[ PROFILER_ROW_MAX ]; + const c8 *row_names[ PROFILER_ROW_MAX ]; + u32 row_length; + + u64 sample_stack[ PROFILER_STACK_MAX ]; + u32 block_stack[ PROFILER_STACK_MAX ]; + u32 stack_height; + + u64 tick_last, segment_last; -void vg_profile_begin( struct vg_profile *profile ) + f32 default_budget_ms; +}; + +struct { - profile->start = SDL_GetPerformanceCounter(); + vg_stack_allocator stack; + u32 count; + vg_mutex tick_lock; } +_vg_profiler; -void vg_profile_increment( struct vg_profile *profile ) +VG_API void _vg_profiler_init(void) { - profile->buffer_current ++; + vg_stack_init( &_vg_profiler.stack, NULL, VG_MB(8), "Profilers" ); + VG_ASSERT( VG_MUTEX_INIT( _vg_profiler.tick_lock ) ); +} - if( profile->buffer_count < VG_PROFILE_SAMPLE_COUNT ) - profile->buffer_count ++; +VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms ) +{ + struct vg_profiler *profiler = vg_stack_allocate( &_vg_profiler.stack, sizeof(struct vg_profiler), 1, "Profiler" ); + vg_zero_mem( profiler, sizeof(struct vg_profiler) ); + profiler->name = name; + profiler->default_budget_ms = default_budget_ms; + _vg_profiler.count ++; + return vg_stack_offset( &_vg_profiler.stack, profiler ); +} + +VG_API_INTERNAL static struct vg_profiler *_vg_profiler_get( u32 id ) +{ + return vg_stack_pointer( &_vg_profiler.stack, id ); +} + +VG_API void _vg_profiler_tick( u32 profiler_id ) +{ + struct vg_profiler *profiler = _vg_profiler_get( profiler_id ); - if( profile->buffer_current >= VG_PROFILE_SAMPLE_COUNT ) - profile->buffer_current = 0; + u64 new_tick = SDL_GetPerformanceCounter(), + duration = new_tick - profiler->tick_last; - profile->samples[ profile->buffer_current ] = 0; + f64 to_ms = 1000.0 / (f64)SDL_GetPerformanceFrequency(); + + VG_MUTEX_LOCK( _vg_profiler.tick_lock ); + for( u32 i=0; ihistory_ms[ profiler->buffer_current ][ i ] = (f32)((f64)profiler->row_accumulated[i] * to_ms); + profiler->row_accumulated[i] = 0; + } + profiler->buffer_current ++; + if( profiler->buffer_current >= PROFILER_HISTORY_LENGTH ) + profiler->buffer_current = 0; + VG_MUTEX_UNLOCK( _vg_profiler.tick_lock ); + + profiler->tick_last = new_tick; +} + +static u32 vg_profiler_block_id( struct vg_profiler *profiler, const c8 *block_name ) +{ + for( u32 i=0; irow_length; i ++ ) + if( profiler->row_names[i] == block_name ) + return i + 1; + if( profiler->row_length < PROFILER_ROW_MAX ) + { + profiler->row_names[ profiler->row_length ++ ] = block_name; + return profiler->row_length; + } + else return 0; } -void vg_profile_end( struct vg_profile *profile ) +VG_API void _vg_profiler_enter_block( u32 profiler_id, const c8 *block_name ) { - u64 time_end = SDL_GetPerformanceCounter(), - delta = time_end - profile->start; + u64 seg_end = SDL_GetPerformanceCounter(); - if( profile->mode == k_profile_mode_frame ) + struct vg_profiler *profiler = _vg_profiler_get( profiler_id ); + VG_ASSERT( profiler->stack_height < PROFILER_STACK_MAX ); + + if( profiler->stack_height ) { - profile->samples[ profile->buffer_current ] = delta; - vg_profile_increment( profile ); + u32 lower_block = profiler->block_stack[ profiler->stack_height ]; + if( lower_block ) + profiler->row_accumulated[ lower_block-1 ] += (seg_end - profiler->segment_last); } - else - profile->samples[ profile->buffer_current ] += delta; + + u32 block_id = vg_profiler_block_id( profiler, block_name ); + profiler->block_stack[ profiler->stack_height ] = block_id; + profiler->stack_height ++; + profiler->segment_last = seg_end; } -void vg_profile_drawn( ui_context *ctx, struct vg_profile **profiles, u32 count, - f64 budget, ui_rect panel, - int dir, i32 normalize ) +VG_API void _vg_profiler_exit_block( u32 profiler_id ) { + u64 seg_end = SDL_GetPerformanceCounter(); + + struct vg_profiler *profiler = _vg_profiler_get( profiler_id ); + VG_ASSERT( profiler->stack_height ); + + profiler->stack_height --; + u32 block_id = profiler->block_stack[ profiler->stack_height ]; + + if( block_id ) + profiler->row_accumulated[ block_id-1 ] += (seg_end - profiler->segment_last ); + + profiler->segment_last = seg_end; +} + +VG_API void _vg_profiler_draw( ui_context *ctx, u32 profiler_id, f32 budget_ms, ui_rect panel, i32 dir, bool normalize ) +{ + struct vg_profiler *profiler = _vg_profiler_get( profiler_id ); if( panel[2] == 0 ) panel[2] = 256; - if( panel[3] == 0 ) - panel[3] = VG_PROFILE_SAMPLE_COUNT * 2; + panel[3] = PROFILER_HISTORY_LENGTH * 2; - f64 sh = (f32)panel[3^dir] / (f32)VG_PROFILE_SAMPLE_COUNT, + f32 sh = (f32)panel[3^dir] / (f32)PROFILER_HISTORY_LENGTH, sw = (f32)panel[2^dir]; ui_fill( ctx, panel, 0xa0000000 ); - VG_ASSERT( count <= 8 ); - - f64 avgs[8]; - u32 colours[8]; - for( u32 i=0; irow_length; i ++ ) + colours[i] = vg_strdjb2( profiler->row_names[i] ) | 0xff000000; - for( i32 i=0; irow_length; j ++ ) { - budget = 0.0; - for( u32 j=0; jsamples[i] * rate_mul; - } - - for( int j=0; jsamples[i] * rate_mul, - px = (total / budget) * sw, - wx = (sample / budget) * sw; + f32 sample_ms = profiler->history_ms[i][j], + pos_x = (total_ms / budget_ms) * sw, + width = (sample_ms / budget_ms) * sw; ui_rect block; - block[0^dir] = panel[0^dir] + px; + block[0^dir] = panel[0^dir] + pos_x; block[1^dir] = panel[1^dir] + (f32)i*sh; - block[2^dir] = VG_MAX( 1, wx-1 ); + block[2^dir] = VG_MAX( 1, width-1 ); block[3^dir] = ceilf(sh)-1; ui_fill( ctx, block, colours[j] ); - - total += sample; - avgs[j] += sample; + total_ms += sample_ms; } } - char infbuf[64]; - +#if 0 + c8 infbuf[64]; snprintf( infbuf, 64, "accuracy: %.7fms", rate_mul ); - ui_text( ctx, - (ui_rect){ panel[0], panel[1], panel[2]-4, 24 }, - infbuf, - 1, - k_ui_align_right, 0 ); + ui_text( ctx, (ui_rect){ panel[0], panel[1], panel[2]-4, 24 }, infbuf, 1, k_ui_align_right, 0 ); for( int i=0; iname ); - - ui_text( ctx, - (ui_rect){ panel[0], panel[1] + 24 + i*14, - panel[2]-4, 14 }, - infbuf, 1, k_ui_align_right, 0 ); + const c8 *name = _vg_profiler_get( profilers[i] )->name; + snprintf( infbuf, 64, "%.4fms %s", avgs[i] * (1.0f/(VG_PROFILE_SAMPLE_COUNT-1)), name ); + ui_text( ctx, (ui_rect){ panel[0], panel[1] + 24 + i*14, panel[2]-4, 14 }, infbuf, 1, k_ui_align_right, 0 ); } +#endif } -void _vg_profile_reg_set( struct vg_profile_set *set ) -{ - VG_ASSERT(_vg_profiler.named_count < VG_ARRAY_LEN(_vg_profiler.named_sets)); - _vg_profiler.named_sets[ _vg_profiler.named_count ++ ] = set; -} - -static void cb_vg_profiler( ui_context *ctx, ui_rect rect, - struct vg_magi_panel *magi ) +static void cb_vg_profiler( ui_context *ctx, ui_rect rect, struct vg_magi_panel *magi ) { - struct vg_profile_set *ps = magi->data; - vg_profile_drawn( ctx, ps->list, ps->count, ps->get_budget(), rect, 0, 0 ); + struct vg_profiler *profiler = magi->data; + _vg_profiler_draw( ctx, vg_stack_offset( &_vg_profiler.stack, profiler ), profiler->default_budget_ms, rect, 0, 0 ); } static int cmd_vg_profile( int argc, const char *argv[] ) { if( argc == 1 ) { - for( u32 i=0; i<_vg_profiler.named_count; i ++ ) + for( u32 i=0; i<_vg_profiler.count; i ++ ) { - struct vg_profile_set *ps = _vg_profiler.named_sets[i]; - if( !strcmp( argv[0], ps->name ) ) + struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) ); + if( !strcmp( argv[0], profiler->name ) ) { - ui_px w = 256, h = VG_PROFILE_SAMPLE_COUNT * 2; - struct vg_magi_panel *magi = - _vg_magi_open( w,h, VG_MAGI_MOVEABLE|VG_MAGI_PERSISTENT ); + ui_px w = 256, h = PROFILER_HISTORY_LENGTH * 2; + struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_MOVEABLE|VG_MAGI_PERSISTENT ); magi->title = "Profiler"; - magi->data = ps; + magi->data = profiler; magi->ui_cb = cb_vg_profiler; magi->close_cb = NULL; return 1; @@ -154,16 +204,20 @@ static int cmd_vg_profile( int argc, const char *argv[] ) return 0; } -static void cmd_vg_profile_poll( int argc, const char *argv[] ) +static void cmd_vg_profile_poll( int argc, const c8 *argv[] ) { - const char *term = argv[ argc-1 ]; - + const c8 *term = argv[ argc-1 ]; if( argc == 1 ) - for( u32 i=0; i<_vg_profiler.named_count; i ++ ) - console_suggest_score_text( _vg_profiler.named_sets[i]->name,term,0 ); + { + for( u32 i=0; i<_vg_profiler.count; i ++ ) + { + struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) ); + console_suggest_score_text( profiler->name, term, 0 ); + } + } } -void vg_profiler_register(void) +VG_API void _vg_profiler_register(void) { vg_console_reg_cmd( "vg_profile", cmd_vg_profile, cmd_vg_profile_poll ); } diff --git a/vg_profiler.h b/vg_profiler.h index dd6d694..8c57e3f 100644 --- a/vg_profiler.h +++ b/vg_profiler.h @@ -1,45 +1,14 @@ -#pragma once -#include "vg_platform.h" -#include "vg_ui/imgui.h" -#define VG_PROFILE_SAMPLE_COUNT 128 +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_profiler.c" +#else -struct vg_profile -{ - const char *name; +VG_API void _vg_profiler_register(void); +VG_API void _vg_profiler_init(void); - u64 samples[ VG_PROFILE_SAMPLE_COUNT ]; - u32 buffer_count, buffer_current; +VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms ); +VG_API void _vg_profiler_tick( u32 profiler_id ); +VG_API void _vg_profiler_enter_block( u32 profiler_id, const c8 *block_name ); +VG_API void _vg_profiler_exit_block( u32 profiler_id ); +VG_API void _vg_profiler_draw( ui_context *ctx, u32 profiler_id, f32 budget_ms, ui_rect panel, i32 dir, bool normalize ); - enum profile_mode - { - k_profile_mode_frame, - k_profile_mode_accum - } - mode; - - u64 start; -}; - -struct _vg_profiler -{ - struct vg_profile_set - { - const char *name; - u32 count; - f64 (*get_budget)(void); - struct vg_profile *list[]; - } - *named_sets[ 8 ]; - u32 named_count; -} -extern _vg_profiler; - -void vg_profile_begin( struct vg_profile *profile ); -void vg_profile_increment( struct vg_profile *profile ); -void vg_profile_end( struct vg_profile *profile ); -void vg_profile_drawn( ui_context *ctx, struct vg_profile **profiles, u32 count, - f64 budget, ui_rect panel, - int dir, i32 normalize ); -void vg_profiler_register(void); - -void _vg_profile_reg_set( struct vg_profile_set *set ); +#endif diff --git a/vg_render.c b/vg_render.c index ba631a8..4a22a89 100644 --- a/vg_render.c +++ b/vg_render.c @@ -1,39 +1,3 @@ -#include "vg_render.h" - -#ifdef VG_3D -#include "shaders/blitblur.h" -#endif - -static struct vg_shader _shader_blit = -{ - .name = "[vg] blit texture", - .vs = { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "" - "uniform vec2 uInverseRatio;" - "out vec2 aUv;" - "" - "void main(){" - "gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);" - "aUv = a_co * uInverseRatio;" - "}", - }, - .fs = { - .orig_file = NULL, - .static_src = - "uniform sampler2D uTexMain;" - "out vec4 FragColor;" - "" - "in vec2 aUv;" - "" - "void main(){" - "FragColor = texture( uTexMain, aUv );" - "}" - } -}; - struct vg_postprocess _vg_postprocess = { #ifdef VG_3D @@ -47,10 +11,16 @@ struct vg_render _vg_render = .scale = 1.0f }; -static void vg_async_postprocess_init( void *userdata ) +VG_API void _vg_render_register(void) { - THREAD_0; + vg_console_reg_var( "render_scale", &_vg_render.scale, k_var_dtype_f32, VG_VAR_PERSISTENT ); + vg_console_reg_var( "blur_strength", &_vg_postprocess.blur_strength, k_var_dtype_f32, 0 ); + vg_console_reg_var( "blur_effect", &_vg_postprocess.blur_effect, k_var_dtype_i32, VG_VAR_PERSISTENT ); +} +VG_API void _vg_render_init(void) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); f32 quad[] = { 0.00f,0.00f, 1.00f,1.00f, 0.00f,1.00f, @@ -65,28 +35,13 @@ static void vg_async_postprocess_init( void *userdata ) glBindVertexArray( _vg_postprocess.quad_vao ); glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(f32)*2, (void*)0 ); glEnableVertexAttribArray( 0 ); - vg_compile_shader( &_shader_blit ); -} - -void vg_render_register(void) -{ - vg_console_reg_var( "render_scale", &_vg_render.scale, k_var_dtype_f32, VG_VAR_PERSISTENT ); - vg_console_reg_var( "blur_strength", &_vg_postprocess.blur_strength, k_var_dtype_f32, 0 ); - vg_console_reg_var( "blur_effect", &_vg_postprocess.blur_effect, k_var_dtype_i32, VG_VAR_PERSISTENT ); -} - -void vg_render_init(void) -{ - THREAD_1; - - vg_async_call( &vg.main_tasks, vg_async_postprocess_init, NULL ); -#ifdef VG_3D +#if defined( VG_3D ) /* * Main framebuffer */ - _vg_render.fb_main = vg_framebuffer_allocate( &vg.rtmem, 3, 1 ); + _vg_render.fb_main = _vg_framebuffer_alloc( NULL, 3, VG_FRAMEBUFFER_GLOBAL ); _vg_render.fb_main->display_name = "main"; _vg_render.fb_main->resolution_div = 1; _vg_render.fb_main->attachments[0] = (vg_framebuffer_attachment) @@ -116,12 +71,12 @@ void vg_render_init(void) .type = GL_UNSIGNED_INT_24_8, .attachment = GL_DEPTH_STENCIL_ATTACHMENT }; - vg_framebuffer_create( _vg_render.fb_main ); + vg_framebuffer_init( _vg_render.fb_main ); /* * Water reflection */ - _vg_render.fb_water_reflection = vg_framebuffer_allocate( &vg.rtmem, 2, 1 ); + _vg_render.fb_water_reflection = _vg_framebuffer_alloc( NULL, 2, VG_FRAMEBUFFER_GLOBAL ); _vg_render.fb_water_reflection->display_name = "water_reflection"; _vg_render.fb_water_reflection->resolution_div = 2; _vg_render.fb_water_reflection->attachments[0] = (vg_framebuffer_attachment) @@ -138,13 +93,13 @@ void vg_render_init(void) .internalformat = GL_DEPTH24_STENCIL8, .attachment = GL_DEPTH_STENCIL_ATTACHMENT }; - vg_framebuffer_create( _vg_render.fb_water_reflection ); + vg_framebuffer_init( _vg_render.fb_water_reflection ); /* * Thid rendered view from the perspective of the camera, but just * captures stuff thats under the water */ - _vg_render.fb_water_beneath = vg_framebuffer_allocate( &vg.rtmem, 2, 1 ); + _vg_render.fb_water_beneath = _vg_framebuffer_alloc( NULL, 2, VG_FRAMEBUFFER_GLOBAL ); _vg_render.fb_water_beneath->display_name = "water_beneath"; _vg_render.fb_water_beneath->resolution_div = 2; _vg_render.fb_water_beneath->attachments[0] = (vg_framebuffer_attachment) @@ -161,7 +116,7 @@ void vg_render_init(void) .internalformat = GL_DEPTH24_STENCIL8, .attachment = GL_DEPTH_STENCIL_ATTACHMENT }; - vg_framebuffer_create( _vg_render.fb_water_beneath ); + vg_framebuffer_init( _vg_render.fb_water_beneath ); #endif } @@ -178,7 +133,7 @@ void vg_render_fullscreen_quad(void) void vg_postprocess_to_screen( vg_framebuffer *fb ) { glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - glViewport( 0,0, vg.window_x, vg.window_y ); + glViewport( 0,0, _vg_window.w, _vg_window.h ); glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); @@ -210,9 +165,9 @@ void vg_postprocess_to_screen( vg_framebuffer *fb ) else #endif { - glUseProgram( _shader_blit.id ); - glUniform1i( glGetUniformLocation( _shader_blit.id, "uTexMain" ), 0 ); - glUniform2fv( glGetUniformLocation( _shader_blit.id, "uInverseRatio" ), 1, vg_ui.bg_inverse_ratio ); + shader_blit_use(); + shader_blit_uTexMain( 0 ); + shader_blit_uInverseRatio( vg_ui.bg_inverse_ratio ); vg_framebuffer_bind_texture( fb, 0, 0 ); } diff --git a/vg_render.h b/vg_render.h index 5da2ae4..7a4f74e 100644 --- a/vg_render.h +++ b/vg_render.h @@ -1,6 +1,6 @@ -#pragma once -#include "vg_engine.h" -#include "vg_framebuffer.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_render.c" +#else struct vg_postprocess { @@ -19,15 +19,17 @@ struct vg_render { f32 scale; -#ifdef VG_3D +#if defined( VG_3D ) vg_framebuffer *fb_main, *fb_water_reflection, *fb_water_beneath; #endif } extern _vg_render; +VG_API void _vg_render_register(void); +VG_API void _vg_render_init(void); -void vg_render_register(void); -void vg_render_init(void); void vg_render_fullscreen_quad(void); void vg_postprocess_to_screen( vg_framebuffer *fb ); + +#endif diff --git a/vg_rigidbody.c b/vg_rigidbody.c index c833a9d..4afccfb 100644 --- a/vg_rigidbody.c +++ b/vg_rigidbody.c @@ -1,10 +1,3 @@ -#include "vg_console.h" -#include "vg_m.h" -#include "vg_rigidbody.h" -#include "vg_platform.h" -#include "vg_engine.h" -#include - static float k_limit_bias = 0.02f, k_joint_correction = 0.01f, @@ -13,7 +6,7 @@ static float f32 k_gravity = 9.6f; -void rb_register_cvar(void) +VG_API void _vg_rigidbody_register(void) { VG_VAR_F32( k_limit_bias, flags=VG_VAR_CHEAT ); VG_VAR_F32( k_joint_bias, flags=VG_VAR_CHEAT ); diff --git a/vg_rigidbody.h b/vg_rigidbody.h index 68ed57d..59c1524 100644 --- a/vg_rigidbody.h +++ b/vg_rigidbody.h @@ -1,5 +1,6 @@ -#pragma once -#include "vg/vg_platform.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_rigidbody.c" +#else /* * Copyright (C) 2021-2024 Mt.ZERO Software - All Rights Reserved @@ -46,7 +47,7 @@ struct rigidbody m4x3f to_world, to_local; }; -void rb_register_cvar(void); +VG_API void _vg_rigidbody_register(void); /* * Initialize rigidbody inverse mass and inertia tensor with some common shapes @@ -83,3 +84,5 @@ void rb_linear_impulse( rigidbody *rb, v3f delta, v3f impulse ); */ void rb_effect_simple_bouyency( rigidbody *ra, v4f plane, f32 amt, f32 drag ); void rb_effect_spring_target_vector( rigidbody *rba, v3f ra, v3f rt, f32 spring, f32 dampening, f32 timestep ); + +#endif diff --git a/vg_rigidbody_collision.c b/vg_rigidbody_collision.c index 2989a46..156416e 100644 --- a/vg_rigidbody_collision.c +++ b/vg_rigidbody_collision.c @@ -1,9 +1,3 @@ -#include "vg_rigidbody.h" -#include "vg_rigidbody_collision.h" -#include "vg_m.h" -#include "vg_lines.h" -#include "vg_platform.h" - int rb_contact_count = 0; struct rb_ct rb_contact_buffer[VG_MAX_CONTACTS]; diff --git a/vg_rigidbody_collision.h b/vg_rigidbody_collision.h index 9a1957d..024a35a 100644 --- a/vg_rigidbody_collision.h +++ b/vg_rigidbody_collision.h @@ -1,6 +1,6 @@ -#pragma once -#include "vg_m.h" -#include "vg_rigidbody.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_rigidbody_collision.c" +#else /* TODO: Get rid of this! */ #define VG_MAX_CONTACTS 256 @@ -70,3 +70,5 @@ void rb_depenetrate( rb_ct *manifold, int len, v3f dt ); void rb_presolve_contacts( rb_ct *buffer, f32 dt, int len ); void rb_contact_restitution( rb_ct *ct, float cr ); void rb_solve_contacts( rb_ct *buf, int len ); + +#endif diff --git a/vg_rigidbody_constraints.c b/vg_rigidbody_constraints.c index 3d6b932..ea26487 100644 --- a/vg_rigidbody_constraints.c +++ b/vg_rigidbody_constraints.c @@ -1,9 +1,3 @@ -#pragma once -#include "vg_rigidbody.h" -#include "vg_rigidbody_constraints.h" -#include "vg_m.h" -#include "vg_lines.h" - /* * ----------------------------------------------------------------------------- * Constraints @@ -431,8 +425,7 @@ void rb_correct_position_constraints( rb_constr_pos *buf, int len, f32 amt ) } } -void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, - int len, float amt ) +void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, int len, float amt ) { for( int i=0; idata; - - glGenVertexArrays( 1, &vg_rb_view.vao ); - glGenBuffers( 1, &vg_rb_view.vbo ); - glGenBuffers( 1, &vg_rb_view.ebo ); - glBindVertexArray( vg_rb_view.vao ); - - - glBindBuffer( GL_ARRAY_BUFFER, vg_rb_view.vbo ); - glBufferData( GL_ARRAY_BUFFER, inf->verts_size, inf->verts, GL_STATIC_DRAW ); - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_rb_view.ebo ); - glBufferData( GL_ELEMENT_ARRAY_BUFFER, - inf->tris_size, inf->tris, GL_STATIC_DRAW ); - - /* 0: coordinates */ - size_t stride = sizeof(rb_view_vert); - glVertexAttribPointer( 0, 4, GL_FLOAT, GL_FALSE, stride, (void*)0 ); - glEnableVertexAttribArray( 0 ); - - /* 1: normal */ - glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, - stride, (void *)offsetof(rb_view_vert, n) ); - glEnableVertexAttribArray( 1 ); -} - -void vg_rb_view_init(void) -{ - THREAD_1; - - vg_shader_register( &_shader_rigidbody ); + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); u32 H = 20, V = 16, @@ -157,26 +35,16 @@ void vg_rb_view_init(void) tris_count += H*2 + (V-2)*(H*2); vg_rb_view.sphere_count = H*2 + (V-2)*(H*2); - u32 hdr_size = vg_align8( sizeof(struct vg_rb_mesh_init) ), - vert_size = vg_align8( verts_count * sizeof(rb_view_vert) ), - tris_size = vg_align8( tris_count * 3 * sizeof(u16) ); - - vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, hdr_size + vert_size + tris_size, 1 ); - struct vg_rb_mesh_init *inf = (void *)task->data; - rb_view_vert *verts = ((void *)inf) + hdr_size; - u16 *tris = ((void *)inf) + hdr_size + vert_size; - - inf->verts = verts; - inf->tris = tris; - inf->verts_size = vert_size; - inf->tris_size = tris_size; + rb_view_vert verts[ verts_count ]; + u16 tris[ tris_count*3 ]; u32 tri_index = 0, vert_index = 0; /* box * ----------------------------------------------------------- */ - for( u32 i=0; i<6; i ++ ){ + for( u32 i=0; i<6; i ++ ) + { v3f n = {i%3==0,i%3==1,i%3==2}; if( i >= 3 ) v3_negate( n, n ); v3f v0, v1; @@ -185,7 +53,8 @@ void vg_rb_view_init(void) rb_view_vert *vs = &verts[vert_index]; vert_index += 4; - for( u32 j=0; j<4; j ++ ){ + for( u32 j=0; j<4; j ++ ) + { v3_copy( n, vs[j].n ); v3_muladds( n, v0, j&0x1?1.0f:-1.0f, vs[j].co ); v3_muladds( vs[j].co, v1, j&0x2?1.0f:-1.0f, vs[j].co ); @@ -208,24 +77,28 @@ void vg_rb_view_init(void) v4_copy( (v4f){0,-1,0,0}, verts[vert_index].co ); v3_copy( (v3f){0,-1,0}, verts[vert_index ++].n ); - for( u32 x=0; x=(V/2) }; v4_copy( co, verts[vert_index].co ); v4_copy( co, verts[vert_index ++].n ); - if( y < V-2 ){ + if( y < V-2 ) + { tris[tri_index*3+0] = base+1 + ybase*H + x; tris[tri_index*3+1] = base+1 + (ybase+1)*H + ((x+1)%H); tris[tri_index*3+2] = base+1 + ybase*H + ((x+1)%H); @@ -240,14 +113,32 @@ void vg_rb_view_init(void) v4_copy( (v4f){0, 1,0,1}, verts[vert_index].co ); v3_copy( (v3f){0, 1,0}, verts[vert_index ++].n ); - for( u32 x=0; x= '0') && (buf[i] <= '9')) || (buf[i] == '\0') ) + buf[j ++] = buf[i]; + } +} + +struct ui_textbox_callbacks static vg_settings_ui_int_callbacks = +{ + .change = vg_settings_ui_int +}; + +static bool vg_settings_ranged_i32_valid( struct vg_setting_ranged_i32 *prop ) +{ + if( prop->new_value < prop->min ) return 0; + if( prop->new_value > prop->max ) return 0; + return 1; +} + +static bool vg_settings_ranged_i32_diff( struct vg_setting_ranged_i32 *prop ) +{ + if( prop->new_value != *prop->actual_value ) return 1; + else return 0; +} + +static bool vg_settings_ui_ranged_i32( ui_context *ctx, struct vg_setting_ranged_i32 *prop, ui_rect rect ) +{ + ui_rect orig; + rect_copy( rect, orig ); + + ui_textbox( ctx, rect, prop->label, prop->buf, sizeof(prop->buf), + 1, 0, &vg_settings_ui_int_callbacks ); + prop->new_value = atoi( prop->buf ); + + if( vg_settings_ranged_i32_diff( prop ) ) + vg_settings_ui_draw_diff( ctx, orig ); + + bool valid = vg_settings_ranged_i32_valid( prop ); + if( !valid ) + { + ui_rect _null, line; + ui_split( orig, k_ui_axis_h, -1, 0, _null, line ); + line[1] += 3; + + ui_fill( ctx, line, ui_colour( ctx, k_ui_red ) ); + } + + return valid; +} + +void ui_settings_ranged_i32_init( struct vg_setting_ranged_i32 *prop ) +{ + vg_str tmp; + vg_strnull( &tmp, prop->buf, sizeof(prop->buf) ); + vg_strcati64( &tmp, *prop->actual_value, 10 ); + prop->new_value = *prop->actual_value; +} + +/* enum settings + * ------------------------------------------------------------------------- */ + +bool vg_settings_enum_diff( struct vg_setting_enum *prop ) +{ + if( prop->new_value != *prop->actual_value ) return 1; + else return 0; +} + +bool vg_settings_enum( ui_context *ctx, struct vg_setting_enum *prop, ui_rect rect ) +{ + ui_rect orig; + rect_copy( rect, orig ); + + ui_enum( ctx, rect, prop->label, + prop->options, prop->option_count, &prop->new_value ); + + if( vg_settings_enum_diff( prop ) ) + vg_settings_ui_draw_diff( ctx, orig ); + + return 1; +} + +void ui_settings_enum_init( struct vg_setting_enum *prop ) +{ + prop->new_value = *prop->actual_value; +} + +/* .. */ +void vg_settings_ui_header( ui_context *ctx, ui_rect inout_panel, const char *name ) +{ + ui_rect rect; + ui_standard_widget( ctx, inout_panel, rect, 2 ); + ui_text( ctx, rect, name, 1, k_ui_align_middle_center, ui_colour(ctx, k_ui_fg+3) ); +} + +bool vg_settings_apply_button( ui_context *ctx, ui_rect inout_panel, bool validated ) +{ + ui_rect last_row; + ui_px height = ui_standard_widget_height( ctx, 1 ); + ui_split( inout_panel, k_ui_axis_h, -height, 8, inout_panel, last_row ); + + const char *string = "Apply"; + if( validated ) + { + if( ui_button( ctx, last_row, string ) == 1 ) + return 1; + } + else + { + ui_rect rect; + ui_standard_widget( ctx, last_row, rect, 1 ); + ui_fill( ctx, rect, ui_colour( ctx, k_ui_bg+1 ) ); + ui_outline( ctx, rect, -1, ui_colour( ctx, k_ui_red ), 0 ); + + ui_rect t = { 0,0, ui_text_line_width( ctx, string ), 14 }; + ui_rect_center( rect, t ); + ui_text( ctx, t, string, 1, k_ui_align_left, ui_colour(ctx,k_ui_fg+3) ); + } + + return 0; +} + +static void vg_settings_video_apply(void) +{ + if( vg_settings_enum_diff( &vg_settings.screenmode ) ) + { + _vg_window.display_mode = vg_settings.screenmode.new_value; + + if( (_vg_window.display_mode == k_vg_window_fullscreen_desktop) || + (_vg_window.display_mode == k_vg_window_fullscreen) ) + { + SDL_DisplayMode video_mode; + if( SDL_GetDesktopDisplayMode( 0, &video_mode ) ) + { + vg_error("SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError()); + } + else + { + //vg.display_refresh_rate = video_mode.refresh_rate; + _vg_window.w = video_mode.w; + _vg_window.h = video_mode.h; + } + SDL_SetWindowSize( _vg_window.sdl_hwindow, _vg_window.w, _vg_window.h ); + } + + if( _vg_window.display_mode == k_vg_window_fullscreen_desktop ) + SDL_SetWindowFullscreen( _vg_window.sdl_hwindow, SDL_WINDOW_FULLSCREEN_DESKTOP ); + if( _vg_window.display_mode == k_vg_window_fullscreen ) + SDL_SetWindowFullscreen( _vg_window.sdl_hwindow, SDL_WINDOW_FULLSCREEN ); + if( _vg_window.display_mode == k_vg_window_windowed ) + { + SDL_SetWindowFullscreen( _vg_window.sdl_hwindow, 0 ); + SDL_SetWindowSize( _vg_window.sdl_hwindow, 1280, 720 ); + SDL_SetWindowPosition( _vg_window.sdl_hwindow, 16, 16 ); + SDL_SetWindowMinimumSize( _vg_window.sdl_hwindow, 1280, 720 ); + SDL_SetWindowMaximumSize( _vg_window.sdl_hwindow, 4096, 4096 ); + } + } + + vg.fps_limit = vg_settings.fps_limit.new_value; + vg.quality_profile = vg_settings.quality.new_value; + _vg_window.vsync = vg_settings.vsync.new_value; +} + +static void vg_settings_video_gui( ui_context *ctx, ui_rect panel ) +{ + bool validated = 1; + ui_rect rq; + ui_standard_widget( ctx, panel, rq, 1 ); + vg_settings_enum( ctx, &vg_settings.quality, rq ); + + /* FIXME */ +#if 0 + if( vg.vsync_feature == k_vsync_feature_error ){ + ui_info( panel, "There was an error activating vsync feature." ); + } +#endif + + /* frame timing */ + vg_settings_ui_header( ctx, panel, "Frame Timing" ); + ui_rect duo, d0,d1; + ui_standard_widget( ctx, panel, duo, 1 ); + ui_split_ratio( duo, k_ui_axis_v, 0.5f, 16, d0, d1 ); + + vg_settings_enum( ctx, &vg_settings.vsync, d0 ); + validated &= vg_settings_ui_ranged_i32( ctx, &vg_settings.fps_limit, d1 ); + + /* profiler */ + ui_standard_widget( ctx, panel, duo, 10 ); + int frame_target = _vg_window.monitor_refresh_rate; + if( !_vg_window.vsync ) + frame_target = vg.fps_limit; + + _vg_profiler_draw( ctx, vg.profiler, (1.0f/(f32)frame_target)*1500.0f, duo, 1, 0 ); + ui_fill( ctx, (ui_rect){ duo[0], duo[1]+(duo[3]*2)/3, duo[2], 1 }, ui_colour(ctx, k_ui_fg) ); + + /* window spec */ + vg_settings_ui_header( ctx, panel, "Window Specification" ); + + ui_standard_widget( ctx, panel, duo, 1 ); + vg_settings_enum( ctx, &vg_settings.screenmode, duo ); + + if( vg_settings_apply_button( ctx, panel, validated ) ) + vg_settings_video_apply(); +} + +static void vg_settings_audio_apply(void) +{ + if( vg_settings_enum_diff( &vg_settings.audio_devices ) ) + { + if( _vg_audio.sdl_output_device ) + { + vg_info( "Closing audio device %d\n", _vg_audio.sdl_output_device ); + SDL_CloseAudioDevice( _vg_audio.sdl_output_device ); + _vg_audio.sdl_output_device = 0; + } + + vg_strfree( &_vg_audio.device_choice ); + + if( vg_settings.audio_devices.new_value == -1 ){ } + else if( vg_settings.audio_devices.new_value == -2 ) + { + vg_fatal_error( "" ); + } + else + { + struct ui_enum_opt *selected = NULL, *oi; + + for( int i=0; ivalue == vg_settings.audio_devices.new_value ) + { + selected = oi; + break; + } + } + + vg_strnull( &_vg_audio.device_choice, NULL, 0 ); + vg_strcat( &_vg_audio.device_choice, oi->alias ); + } + + vg_audio_device_init(); + *vg_settings.audio_devices.actual_value = vg_settings.audio_devices.new_value; + } + + if( vg_settings_enum_diff( &vg_settings.dsp ) ) + *vg_settings.dsp.actual_value = vg_settings.dsp.new_value; +} + +static void vg_settings_audio_gui( ui_context *ctx, ui_rect panel ) +{ + ui_rect rq; + ui_standard_widget( ctx, panel, rq, 1 ); + vg_settings_enum( ctx, &vg_settings.audio_devices, rq ); + + ui_standard_widget( ctx, panel, rq, 1 ); + vg_settings_enum( ctx, &vg_settings.dsp, rq ); + + if( vg_settings_apply_button( ctx, panel, 1 ) ) + vg_settings_audio_apply(); +} + +VG_API void _vg_settings_open(void) +{ + vg_settings.open = 1; + + ui_settings_ranged_i32_init( &vg_settings.fps_limit ); + ui_settings_enum_init( &vg_settings.vsync ); + ui_settings_enum_init( &vg_settings.quality ); + ui_settings_enum_init( &vg_settings.screenmode ); + + /* Create audio options */ + int count = SDL_GetNumAudioDevices( 0 ); + + struct ui_enum_opt *options = malloc( sizeof(struct ui_enum_opt)*(count+1) ); + vg_settings.audio_devices.options = options; + vg_settings.audio_devices.option_count = count+1; + + struct ui_enum_opt *o0 = &options[0]; + o0->alias = "OS Default"; + o0->value = -1; + + for( int i=0; ialias = malloc( len+1 ); + memcpy( (void *)oi->alias, device_name, len+1 ); + oi->value = i; + } + + if( _vg_audio.device_choice.buffer ) + { + vg_settings.temp_audio_choice = -2; + + for( int i=0; ialias, _vg_audio.device_choice.buffer ) ) + { + vg_settings.temp_audio_choice = oi->value; + break; + } + } + } + else + { + vg_settings.temp_audio_choice = -1; + } + + ui_settings_enum_init( &vg_settings.audio_devices ); + ui_settings_enum_init( &vg_settings.dsp ); + +#ifdef VG_GAME_SETTINGS + vg_game_settings_init(); +#endif +} + +VG_API void _vg_settings_close(void) +{ + vg_settings.open = 0; + + struct ui_enum_opt *options = vg_settings.audio_devices.options; + for( int i=1; i < vg_settings.audio_devices.option_count; i ++ ) + free( (void *)options[i].alias ); + free( vg_settings.audio_devices.options ); +} + +VG_API void _vg_settings_gui( ui_context *ctx ) +{ + if( !vg_settings.open ) + return; + + ui_rect null; + ui_rect screen = { 0, 0, ctx->area[0], ctx->area[1] }; + ui_rect window = { 0, 0, 1000, 700 }; + ui_rect_center( screen, window ); + ui_capture_mouse( ctx, 1 ); + + ui_fill( ctx, window, ui_colour( ctx, k_ui_bg+1 ) ); + ui_outline( ctx, window, 1, ui_colour( ctx, k_ui_bg+7 ), 0 ); + + ui_rect title, panel; + ui_split( window, k_ui_axis_h, 28, 0, title, panel ); + ui_fill( ctx, title, ui_colour( ctx, k_ui_bg+7 ) ); + ui_text( ctx, title, "Settings", 1, k_ui_align_middle_center, + ui_colourcont(ctx, k_ui_bg+7) ); + + ui_rect quit_button; + ui_split( title, k_ui_axis_v, title[2]-title[3], 2, title, quit_button ); + + if( ui_button_text( ctx, quit_button, "X", 1 ) == k_ui_button_click ) + { + _vg_settings_close(); + return; + } + + ui_rect_pad( panel, (ui_px[2]){ 8, 8 } ); + + const char *opts[] = { "video", "audio" }; + + static i32 page = 0; + ui_tabs( ctx, panel, panel, opts, VG_ARRAY_LEN(opts), &page ); + + if( page == 0 ) + { + vg_settings_video_gui( ctx, panel ); + } + else if( page == 1 ) + vg_settings_audio_gui( ctx, panel ); +} + +static int cmd_vg_settings_toggle( int argc, const char *argv[] ) +{ + _vg_settings_open(); + return 0; +} + +VG_API void _vg_settings_register(void) +{ + vg_console_reg_cmd( "vg_settings", cmd_vg_settings_toggle, NULL ); +} + +VG_API bool _vg_settings_is_open(void) +{ + return vg_settings.open; +} diff --git a/vg_settings.h b/vg_settings.h new file mode 100644 index 0000000..fd9d842 --- /dev/null +++ b/vg_settings.h @@ -0,0 +1,9 @@ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_settings.c" +#else +VG_API void _vg_settings_register(void); +VG_API void _vg_settings_gui( ui_context *ctx ); +VG_API void _vg_settings_open(void); +VG_API void _vg_settings_close(void); +VG_API bool _vg_settings_is_open(void); +#endif diff --git a/vg_shader.c b/vg_shader.c index 7888b9d..e530014 100644 --- a/vg_shader.c +++ b/vg_shader.c @@ -1,37 +1,15 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ - -#pragma once -#include "vg_platform.h" -#include "vg_shader.h" -#include "vg_engine.h" - -#define STB_INCLUDE_IMPLEMENTATION -#define STB_INCLUDE_LINE_GLSL -#define STB_MALLOC vg_alloc -#define STB_FREE vg_free -#define STB_REALLOC vg_realloc -#include "submodules/stb/stb_include.h" - const char *vg_shader_gl_ver = "#version 330 core\n"; -struct vg_shaders -{ - vg_shader *shaders[48]; - u32 count; -} -static vg_shaders; - /* * Compile OpenGL subshader from GLSL source. Type is subshader type. * If critical is set to 1, the program will fatal exit on compile failure. */ -static GLuint vg_compile_opengl_subshader( GLint type, const char *src, bool critical ) +static GLuint vg_compile_opengl_subshader( GLint type, const c8 *src, bool critical, const c8 *debug_path ) { GLuint shader = glCreateShader( type ); if( shader == 0 ) { - vg_opengl_log_errors(); vg_fatal_error( "glCreateShader returned 0.\n" ); return 0; } @@ -58,7 +36,7 @@ static GLuint vg_compile_opengl_subshader( GLint type, const char *src, bool cri else if( type == GL_FRAGMENT_SHADER ) type_str = "GL_FRAGMENT_SHADER"; if( critical ) - vg_fatal_error( "%s subshader compile error:\n\n%s\n", type_str, info ); + vg_fatal_error( "shader source path: %s\n %s subshader compile error:\n\n%s\n", debug_path, type_str, info ); return 0; } } @@ -93,11 +71,10 @@ void vg_compile_shader( struct vg_shader *shader ) { VG_ASSERT( shader->compiled == 0 ); - const char *vs = shader->vs.static_src, - *fs = shader->fs.static_src; - - GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 1 ), - frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 1 ), + const c8 *vs = _vg_shaders_glsl + shader->vs.glsl, + *fs = _vg_shaders_glsl + shader->fs.glsl; + GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 1, _vg_shaders_infos + shader->vs.src ), + frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 1, _vg_shaders_infos + shader->fs.src ), program = glCreateProgram(); glAttachShader( program, vert ); @@ -108,7 +85,7 @@ void vg_compile_shader( struct vg_shader *shader ) glDeleteShader( vert ); glDeleteShader( frag ); - shader->id = program; + _vg_shader_names[ shader->names_start ] = program; shader->compiled = 1; } @@ -121,32 +98,36 @@ void vg_recompile_shader( struct vg_shader *shader ) VG_ASSERT( shader->compiled == 1 ); char error[260]; - char path[260]; + char path_buf[260]; + vg_str path; - if( shader->vs.orig_file == NULL ) return; - if( shader->fs.orig_file == NULL ) return; + vg_strnull( &path, path_buf, sizeof(path_buf) ); + vg_strcat( &path, "../../" ); + vg_strcat( &path, _vg_shaders_infos + shader->vs.src ); + VG_ASSERT( vg_strgood( &path ) ); + + c8 *vs = stb_include_file( path_buf, "", "../../shaders", error ); - strcpy( path, "../../" ); - strcat( path, shader->vs.orig_file ); - char *vs = stb_include_file( path, "", "../../shaders", error ); + vg_strnull( &path, path_buf, sizeof(path_buf) ); + vg_strcat( &path, "../../" ); + vg_strcat( &path, _vg_shaders_infos + shader->fs.src ); + VG_ASSERT( vg_strgood( &path ) ); - strcpy( path, "../../" ); - strcat( path, shader->fs.orig_file ); - char *fs = stb_include_file( path, "", "../../shaders", error ); + c8 *fs = stb_include_file( path_buf, "", "../../shaders", error ); if( !vs || !fs ) { vg_warn( "Could not recompile shader due to missing source files:\n" ); - if( !vs ) vg_info( " Vertex: %s\n", shader->vs.orig_file ); - if( !fs ) vg_info( " Fragment: %s\n", shader->fs.orig_file ); + if( !vs ) vg_info( " Vertex: %s\n", _vg_shaders_infos + shader->vs.src ); + if( !fs ) vg_info( " Fragment: %s\n", _vg_shaders_infos + shader->fs.src ); free( vs ); free( fs ); return; } - GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 0 ), - frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 0 ); + GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 0, _vg_shaders_infos + shader->vs.src ), + frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 0, _vg_shaders_infos + shader->fs.src ); free( vs ); free( fs ); @@ -161,8 +142,8 @@ void vg_recompile_shader( struct vg_shader *shader ) if( vg_link_opengl_program( program, 0 ) ) { /* replace existing */ - glDeleteProgram( shader->id ); - shader->id = program; + glDeleteProgram( _vg_shader_names[ shader->names_start ] ); + _vg_shader_names[ shader->names_start ] = program; } else { @@ -178,49 +159,54 @@ void vg_free_shader( struct vg_shader *shader ) { if( shader->compiled ) { - glDeleteProgram( shader->id ); - shader->id = 0; shader->compiled = 0; + glDeleteProgram( _vg_shader_names[ shader->names_start ] ); + _vg_shader_names[ shader->names_start ] = 0; + for( u32 i=0; iuniform_count; i ++ ) + _vg_shader_names[ shader->names_start + 1 + i ] = 0; } } -#ifdef VG_CUSTOM_SHADERS -void vg_auto_shader_link(void); -#endif +static void vg_shader_link( struct vg_shader *shader ) +{ + GLuint program = _vg_shader_names[ shader->names_start ]; + const c8 *uniform_alias = _vg_shaders_uniform_names + shader->uniform_aliases_offset; + + for( u32 i=0; iuniform_count; i ++ ) + { + u32 index = shader->names_start + 1 + i; + if( uniform_alias[0] == '$' ) _vg_shader_names[ index ] = glGetUniformBlockIndex( program, uniform_alias+1 ); + else _vg_shader_names[ index ] = glGetUniformLocation( program, uniform_alias ); + while( *uniform_alias ) + uniform_alias ++; + uniform_alias ++; + } +} -void vg_shaders_compile(void *_) +VG_API void _vg_shaders_init(void) { - THREAD_0; + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) ); vg_info( "Compiling shaders\n" ); - for( int i=0; icompiled = 0; - shader->id = 0; /* TODO: make this an error shader */ - vg_shaders.shaders[ vg_shaders.count ++ ] = shader; + vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL ); } diff --git a/vg_shader.h b/vg_shader.h index afcdc81..4f7bdf7 100644 --- a/vg_shader.h +++ b/vg_shader.h @@ -1,23 +1,27 @@ -#pragma once +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_shader.c" +#else typedef struct vg_shader vg_shader; struct vg_shader { - GLuint id; - const char *name; - + u32 alias_offset; struct vg_subshader { - const char *orig_file, - *static_src; + u32 glsl, src; } vs, fs; - int compiled; + + u32 names_start, uniform_aliases_offset, uniform_count; + bool compiled; }; -void vg_shaders_compile(void *_); +VG_API void _vg_shaders_register(void); +VG_API void _vg_shaders_init(void); + int vg_shaders_live_recompile(int argc, const char *argv[]); -void vg_shader_register( struct vg_shader *shader ); void vg_compile_shader( struct vg_shader *shader ); void vg_recompile_shader( struct vg_shader *shader ); void vg_free_shader( struct vg_shader *shader ); + +#endif diff --git a/vg_steam2.c b/vg_steam2.c index cc10baf..1c14bbd 100644 --- a/vg_steam2.c +++ b/vg_steam2.c @@ -1,11 +1,3 @@ -#include "vg_steam2.h" -#include "vg_log.h" -#include "vg_string.h" -#include "vg_mem.h" -#include "vg_io.h" -#include -#include - struct vg_steam_api _steam_api; static void cb_steam_warning( i32 severity, const c8 *pchMessage ) @@ -51,10 +43,10 @@ static u8 vg_char_base16( c8 c ) #endif #if defined( VG_SERVER ) -bool vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, const c8 *pchVersionString, - const c8 *appid_str ) +VG_API bool _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, + const c8 *pchVersionString, const c8 *appid_str ) #else -bool vg_steam_init(void) +VG_API bool _vg_steam_init(void) #endif { if( _steam_api.disabled ) @@ -65,6 +57,7 @@ bool vg_steam_init(void) /* Steamworks init step * ---------------------------------------------------------------------------- */ #if defined( VG_ENGINE ) + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) ); const char *pszInternalCheckInterfaceVersions = STEAMUTILS_INTERFACE_VERSION "\0" @@ -113,7 +106,7 @@ bool vg_steam_init(void) vg_stack_allocator stack; vg_stack_init( &stack, NULL, VG_KB(256), NULL ); u32 size; - char *src = vg_file_read( &stack, "application_key", &size, 0 ); + c8 *src = vg_file_read( &stack, "application_key", &size, 0 ); if( src ) { if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen ) @@ -324,7 +317,7 @@ void vg_steam_frame(void) } } -void vg_steam_shutdown(void) +VG_API void _vg_steam_shutdown(void) { #if defined( VG_SERVER ) if( _steam_api.is_connected ) diff --git a/vg_steam2.h b/vg_steam2.h index 857f24e..010647d 100644 --- a/vg_steam2.h +++ b/vg_steam2.h @@ -1,13 +1,13 @@ -#pragma once -#include "vg_platform.h" - -#if defined( VG_ENGINE ) || defined( VG_SERVER ) -# if defined( VG_ENGINE ) && defined( VG_SERVER ) -# error Can't be both! -# endif +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_steam2.c" #else -# error Must define VG_ENGINE or VG_SERVER -#endif +# if defined( VG_ENGINE ) || defined( VG_SERVER ) +# if defined( VG_ENGINE ) && defined( VG_SERVER ) +# error Can't be both! +# endif +# else +# error Must define VG_ENGINE or VG_SERVER +# endif #define STEAMTIMELINE_INTERFACE_VERSION "STEAMTIMELINE_INTERFACE_V004" #define STEAMUTILS_INTERFACE_VERSION "SteamUtils010" @@ -1195,13 +1195,15 @@ struct vg_steam_api extern _steam_api; #if defined( VG_SERVER ) -bool vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, const c8 *pchVersionString, - const c8 *appid_str ); +VG_API bool _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, + const c8 *pchVersionString, const c8 *appid_str ); #else -bool vg_steam_init(void); +VG_API bool _vg_steam_init(void); #endif void vg_steam_frame(void); -void vg_steam_shutdown(void); +VG_API void _vg_steam_shutdown(void); vg_steam_api_call *vg_alloc_async_steam_api_call(void); void vg_steam_set_achievement( const c8 *name, bool yes ); + +#endif diff --git a/vg_string.c b/vg_string.c index 60a82d0..449193e 100644 --- a/vg_string.c +++ b/vg_string.c @@ -1,11 +1,3 @@ -#include "vg_string.h" -#include "vg_platform.h" -#include -#include -#include -#include -#include "submodules/anyascii/impl/c/anyascii.c" - i32 vg_str_storage( vg_str *str ) { if( str->len == 0 ) @@ -24,7 +16,7 @@ i32 vg_str_storage( vg_str *str ) * Reset string. If len is 0 (dynamically allocated), buffer must be either * NULL or be acquired from malloc or realloc */ -void vg_strnull( vg_str *str, char *buffer, i32 len ) +void vg_strnull( vg_str *str, c8 *buffer, i32 len ) { VG_ASSERT( len >= 0 ); @@ -43,7 +35,7 @@ void vg_strfree( vg_str *str ) if( str->buffer ) { vg_str_dynamic *arr = (vg_str_dynamic *)str->buffer; - free( arr-1 ); + vg_free( arr-1 ); str->buffer = NULL; str->i = 0; @@ -61,22 +53,22 @@ static i32 vg_str_dynamic_grow( vg_str *str ) { vg_str_dynamic *hdr = ((vg_str_dynamic *)str->buffer) - 1; i32 total = (hdr->len + sizeof(vg_str_dynamic)) * 2; - hdr = realloc( hdr, total ); + hdr = vg_realloc( hdr, total ); hdr->len = total - sizeof(vg_str_dynamic); - str->buffer = (char *)(hdr+1); + str->buffer = (c8 *)(hdr+1); return hdr->len; } else { - vg_str_dynamic *hdr = malloc(16); + vg_str_dynamic *hdr = vg_malloc(16); hdr->len = 16-sizeof(vg_str_dynamic); - str->buffer = (char *)(hdr+1); + str->buffer = (c8 *)(hdr+1); str->buffer[0] = '\0'; return hdr->len; } } -static bool _vg_strcatch( vg_str *str, char c ) +static bool _vg_strcatch( vg_str *str, c8 c ) { if( str->i == -1 ) return 0; @@ -97,7 +89,7 @@ static bool _vg_strcatch( vg_str *str, char c ) return 1; } -void vg_strcat( vg_str *str, const char *append ) +void vg_strcat_limit( vg_str *str, const c8 *append, u32 max ) { if( !append || (str->i == -1) ) return; @@ -105,10 +97,13 @@ void vg_strcat( vg_str *str, const char *append ) i32 i = 0; append:; - char c = append[ i ++ ]; + c8 c = append[ i ++ ]; if( !_vg_strcatch( str, c ) ) return; + if( max && i >= max ) + return; + if( c == '\0' ) { str->i --; @@ -117,7 +112,12 @@ append:; else goto append; } -void vg_strcatch( vg_str *str, char c ) +void vg_strcat( vg_str *str, const c8 *append ) +{ + vg_strcat_limit( str, append, 0 ); +} + +void vg_strcatch( vg_str *str, c8 c ) { _vg_strcatch( str, c ); _vg_strcatch( str, '\x00' ); @@ -138,11 +138,11 @@ bool vg_str_flushfd( vg_str *str, int fd ) } /* - * Returns pointer to last instance of character + * Returns pointer to last instance of c8acter */ -char *vg_strch( vg_str *str, char c ) +c8 *vg_strch( vg_str *str, c8 c ) { - char *ptr = NULL; + c8 *ptr = NULL; for( i32 i=0; ii; i++ ){ if( str->buffer[i] == c ) ptr = str->buffer+i; @@ -151,7 +151,30 @@ char *vg_strch( vg_str *str, char c ) return ptr; } -u32 vg_strncpy( const char *src, char *dst, u32 len, enum strncpy_behaviour behaviour ) +u32 vg_strcpy( const c8 *src, c8 *dst ) +{ + for( u32 i=0; i<32*1024; i ++ ) + { + dst[i] = src[i]; + if( !src[i] ) + return i; + } + + vg_fatal_error( "Hit string copy limit! (32 KB max length)\n" ); + return 0; +} + +u32 vg_strlen( const c8 *src ) +{ + for( u32 i=0; i<32*1024; i ++ ) + if( !src[i] ) + return i; + + vg_fatal_error( "Hit string len limit! (32 KB max length)\n" ); + return 0; +} + +u32 vg_strncpy( const c8 *src, c8 *dst, u32 len, enum strncpy_behaviour behaviour ) { if( src == NULL ) { @@ -180,14 +203,14 @@ u32 vg_strncpy( const char *src, char *dst, u32 len, enum strncpy_behaviour beha return 0; } -static void _vg_strcatf_va( vg_str *str, const char *fmt, va_list args ) +static void _vg_strcatf_va( vg_str *str, const c8 *fmt, va_list args ) { - char buffer[4096]; + c8 buffer[4096]; vsnprintf( buffer, VG_ARRAY_LEN(buffer), fmt, args ); vg_strcat( str, buffer ); } -void vg_strcatf( vg_str *str, const char *fmt, ... ) +void vg_strcatf( vg_str *str, const c8 *fmt, ... ) { va_list args; va_start( args, fmt ); @@ -204,7 +227,7 @@ u32 vg_strdjb2( const c8 *str ) return hash; } -bool vg_strdjb2_eq( const char *s1, u32 h1, const char *s2, u32 h2 ) +bool vg_strdjb2_eq( const c8 *s1, u32 h1, const c8 *s2, u32 h2 ) { if( h1 == h2 ) { @@ -214,7 +237,7 @@ bool vg_strdjb2_eq( const char *s1, u32 h1, const char *s2, u32 h2 ) else return 0; } -bool vg_str_eq( const char *s1, const char *s2 ) +bool vg_str_eq( const c8 *s1, const c8 *s2 ) { if( vg_strdjb2( s1 ) == vg_strdjb2( s2 ) ) return !strcmp( s1, s2 ); @@ -222,18 +245,18 @@ bool vg_str_eq( const char *s1, const char *s2 ) return 0; } -static u32 utf8_byte0_byte_count( u8 char0 ) +static u32 utf8_byte0_byte_count( u8 c80 ) { for( u32 k=2; k<4; k++ ) { - if( !(char0 & (0x80 >> k)) ) + if( !(c80 & (0x80 >> k)) ) return k; } return 0; } -u32 str_utf8_collapse( const char *str, char *buf, u32 length ) +u32 str_utf8_collapse( const c8 *str, c8 *buf, u32 length ) { u8 *ustr = (u8 *)str; u32 utf32_code = 0x00000000; @@ -250,9 +273,9 @@ u32 str_utf8_collapse( const char *str, char *buf, u32 length ) utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6); if( !utf32_byte_ct ) { - const char *match; - size_t chars = anyascii( utf32_code, &match ); - for( u32 k=0; kbuffer == NULL ) @@ -312,7 +335,7 @@ vg_strp_info vg_strp_u64( vg_strp *p, u64 *value ) c8 c; vg_strp_info info = k_vg_strp_whitespace; while( info == k_vg_strp_whitespace ) - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); u64 result = 0; bool got = 0; @@ -325,13 +348,13 @@ vg_strp_info vg_strp_u64( vg_strp *p, u64 *value ) } else goto err; - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); } info = k_vg_strp_ok; goto ok; err: while( info == k_vg_strp_ok ) - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); info = k_vg_strp_error; ok: *value = result; @@ -343,7 +366,7 @@ vg_strp_info vg_strp_i64( vg_strp *p, i64 *value ) c8 c; vg_strp_info info = k_vg_strp_whitespace; while( info == k_vg_strp_whitespace ) - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); i64 result = 0, sign = 1; @@ -352,7 +375,7 @@ vg_strp_info vg_strp_i64( vg_strp *p, i64 *value ) { if( c == '-' ) sign = -1; - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); } bool got = 0; @@ -366,13 +389,13 @@ vg_strp_info vg_strp_i64( vg_strp *p, i64 *value ) else goto err; - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); } info = k_vg_strp_ok; goto ok; err: while( info == k_vg_strp_ok ) - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); info = k_vg_strp_error; ok: *value = result*sign; @@ -384,7 +407,7 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value ) c8 c; vg_strp_info info = k_vg_strp_whitespace; while( info == k_vg_strp_whitespace ) - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); i64 result = 0, resultm= 0; @@ -395,7 +418,7 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value ) { if( c == '-' ) sign = -1.0; - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); } bool got = 0, got_decimal = 0; @@ -418,14 +441,14 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value ) } else goto err; - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); } info = k_vg_strp_ok; goto ok; err: while( info == k_vg_strp_ok ) - info = vg_strp_char( p, &c ); + info = vg_strp_c8( p, &c ); info = k_vg_strp_error; ok:; @@ -444,18 +467,18 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value ) return (got||got_decimal)? info: k_vg_strp_eof; } -static u32 vg_strcatu64_internal( c8 reverse_buffer[64], u64 value, u64 base, u32 max_characters ) +static u32 vg_strcatu64_internal( c8 reverse_buffer[64], u64 value, u64 base, u32 max_c8acters ) { VG_ASSERT( base >= 2 ); - if( max_characters == 0 ) - max_characters = 64; + if( max_c8acters == 0 ) + max_c8acters = 64; const c8 *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if( value ) { u32 i = 0; - while( value && (i= 2 ); @@ -485,7 +508,7 @@ void vg_strcati64r( vg_str *str, i64 value, u64 base, u32 width, c8 blank_charac padding = width - digits; for( u32 i=0; i - -static u8 const_vg_tex2d_err[] ={ -#ifdef VG_DEBUG - 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, - 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, - 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, - 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, - 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, - 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, -#else - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, - 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, -#endif -}; - -#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_COLOR_HASH(C) (C.rgba[0]*3 + C.rgba[1]*5 + C.rgba[2]*7 + C.rgba[3]*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; -} + (((u32)'q') | ((u32)'o') << 8 | \ + ((u32)'i') << 16 | ((u32)'f') << 24) -static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { - bytes[(*p)++] = (0xff000000 & v) >> 24; - bytes[(*p)++] = (0x00ff0000 & v) >> 16; - bytes[(*p)++] = (0x0000ff00 & v) >> 8; - bytes[(*p)++] = (0x000000ff & v); -} +static const u8 qoi_padding[8] = {0,0,0,0,0,0,0,1}; -struct texture_load_info +#define cpu_to_big32 big32_to_cpu +VG_TIER_0 static inline u32 big32_to_cpu( u32 x ) { - GLuint *dest; - u32 width, height, flags; - u8 *rgba; -}; + return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24); +} -static void async_vg_tex2d_upload( vg_async_task *task ) +VG_TIER_0 bool vg_qoi_validate( const qoi_desc *desc ) { - THREAD_0; - struct texture_load_info *info = (void *)task->data; - - glGenTextures( 1, info->dest ); - - if( info->flags & VG_TEX2D_CUBEMAP ) + if( (desc->width == 0) || (desc->height == 0) || + (desc->width >= 2048) || (desc->height >= 2048) ) { - bool not_error = !(info->flags & VG_TEX2D_ERROR); - u32 w = info->width, - h = not_error? info->height/6: info->height; - - glBindTexture( GL_TEXTURE_CUBE_MAP, *info->dest ); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - for( u32 j=0; j<6; j ++ ) - { - u32 offset = not_error? w*h*j*4: 0; + vg_error( "QOI file is invalid; Unpermitted size (%ux%u)\n", desc->width, desc->height ); + return 0; + } - glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, GL_RGBA, - w, h, - 0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba + offset ); - } + if( !(desc->channels == 3 || desc->channels == 4) ) + { + vg_error( "QOI file is invalid; Only 3 or 4 channels permitted. File has %u\n", desc->channels ); + return 0; } - else + + return 1; +} + +/* Initalize stream context and return the number of bytes required to store the final RGB/A image data. */ +VG_TIER_0 u32 vg_qoi_stream_init( qoi_desc *desc, vg_stream *stream ) +{ + vg_stream_read( stream, desc, sizeof(qoi_desc) ); + if( desc->magic != QOI_MAGIC ) { - 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 ); + vg_error( "QOI file is invalid; Magic Number incorrect.\n" ); + return 0; + } - if( !(info->flags & VG_TEX2D_NOMIP) ){ - glGenerateMipmap( GL_TEXTURE_2D ); - } + desc->width = big32_to_cpu( desc->width ); + desc->height = big32_to_cpu( desc->height ); - 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 ); - } + if( vg_qoi_validate( desc ) ) + return desc->width * desc->height * desc->channels; + else return 0; +} - 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 ); - } +VG_TIER_0 void vg_qoi_stream_decode( qoi_desc *desc, vg_stream *stream, u8 *pixels, bool v_flip ) +{ + qoi_rgba_t index[64], px; + vg_zero_mem( index, sizeof(qoi_rgba_t)*64 ); + vg_zero_mem( &px, sizeof(qoi_rgba_t) ); + px.rgba[3] = 255; - 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 ); - } + u32 run=0; + + for( u32 y=0; yheight; y ++ ) + { + for( u32 x=0; xwidth; x ++ ) + { + if( run > 0 ) + run --; + else + { + u8 b1; + vg_stream_read( stream, &b1, 1 ); + + if( b1 == QOI_OP_RGB ) + vg_stream_read( stream, px.rgba, 3 ); + else if( b1 == QOI_OP_RGBA ) + vg_stream_read( stream, px.rgba, 4 ); + else if( (b1 & QOI_MASK_2) == QOI_OP_INDEX ) + px = index[b1]; + else if( (b1 & QOI_MASK_2) == QOI_OP_DIFF ) + { + px.rgba[0] += (i32)((b1 >> 4) & 0x03) - 2; + px.rgba[1] += (i32)((b1 >> 2) & 0x03) - 2; + px.rgba[2] += (i32)( b1 & 0x03) - 2; + } + else if( (b1 & QOI_MASK_2) == QOI_OP_LUMA ) + { + u8 b2; + vg_stream_read( stream, &b2, 1 ); + i32 vg = (i32)(b1 & 0x3f) - 32; + px.rgba[0] += vg - 8 + (i32)((b2 >> 4) & 0x0f); + px.rgba[1] += vg; + px.rgba[2] += vg - 8 + (i32)(b2 & 0x0f); + } + else if( (b1 & QOI_MASK_2) == QOI_OP_RUN ) + run = (b1 & 0x3f); + index[ QOI_COLOR_HASH(px) % 64 ] = px; + } - 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 ); + u32 row = v_flip? desc->height-(y+1): y; + for( u32 i=0; i < desc->channels; i ++ ) + pixels[ ((row*desc->width) + x)*desc->channels + i ] = px.rgba[i]; } } } -void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest ) +VG_TIER_0 u32 vg_query_qoi_max_compressed_size( const qoi_desc *desc ) { - THREAD_1; - - vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, sizeof(struct texture_load_info), 1 ); - struct texture_load_info *info = (void *)task->data; - info->dest = dest; - info->flags = VG_TEX2D_ERROR|VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP; - - if( original_flags & VG_TEX2D_CUBEMAP ) - info->flags |= VG_TEX2D_CUBEMAP; - - info->width = 4; - info->height = 4; - info->rgba = const_vg_tex2d_err; - vg_async_task_dispatch( task, async_vg_tex2d_upload ); + return desc->width * desc->height * (desc->channels + 1) + sizeof(qoi_desc) + sizeof(qoi_padding); } -void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, u32 flags, GLuint *dest ) +VG_TIER_0 u32 vg_qoi_stream_encode( const qoi_desc *desc, const u8 *pixels, vg_stream *stream, bool v_flip ) { - THREAD_1; + if( !vg_qoi_validate( desc ) ) + return 0; - u32 header_magic; + qoi_desc file_header = *desc; + file_header.magic = QOI_MAGIC; + file_header.width = cpu_to_big32( file_header.width ); + file_header.height = cpu_to_big32( file_header.height ); + vg_stream_write( stream, &file_header, sizeof(qoi_desc) ); + 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_async( flags, dest ); - return; - } - - 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_async( flags, dest ); - return; - } - - if (channels == 0) { - channels = desc.channels; - } - - px_len = desc.width * desc.height * channels; - - /* allocate async call - * -------------------------- - */ - u32 hdr_size = vg_align8(sizeof(struct texture_load_info)), - tex_size = vg_align8(px_len); + vg_zero_mem( index, sizeof(qoi_rgba_t)*64 ); + qoi_rgba_t px_prev = { .rgba = { 0, 0, 0, 255 } }; + qoi_rgba_t px = px_prev; - if( !vg_async_checksize( &vg.main_tasks, hdr_size + tex_size ) ) + u32 run = 0; + for( u32 y=0; yheight; y ++ ) { - vg_error( "Texture too large for task buffer! Dimensions: %u x %u x 4 (%u total bytes)\n", - desc.width, desc.height, tex_size ); - vg_tex2d_replace_with_error_async( flags, dest ); - return; - } - - vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, hdr_size + tex_size, 1 ); - struct texture_load_info *info = (void *)task->data; - info->dest = dest; - info->flags = flags; - info->width = desc.width; - info->height = desc.height; - info->rgba = task->data + 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 (p < chunks_len) { - int b1 = bytes[p++]; + for( u32 x=0; xwidth; x ++ ) + { + u32 row = v_flip? desc->height-(y+1): y; + for( u32 i=0; i < desc->channels; i ++ ) + px.rgba[i] = pixels[ ((row*desc->width) + x)*desc->channels + i ]; - 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); + if( px.v == px_prev.v ) + { + run ++; + if( run == 62 || ((y+1 == desc->height) && (x+1 == desc->width)) ) + { + u8 b1 = QOI_OP_RUN | (run - 1); + vg_stream_write( stream, &b1, 1 ); + run = 0; + } } + else + { + if( run > 0 ) + { + u8 b1 = QOI_OP_RUN | (run - 1); + vg_stream_write( stream, &b1, 1 ); + run = 0; + } - index[QOI_COLOR_HASH(px) % 64] = px; - } - - 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; + u32 index_pos = QOI_COLOR_HASH( px ) % 64; + if( index[ index_pos ].v == px.v ) + { + u8 b1 = QOI_OP_INDEX | index_pos; + vg_stream_write( stream, &b1, 1 ); + } + else + { + index[ index_pos ] = px; + if( px.rgba[3] == px_prev.rgba[3] ) + { + i8 vr = px.rgba[0] - px_prev.rgba[0], + vg = px.rgba[1] - px_prev.rgba[1], + vb = px.rgba[2] - px_prev.rgba[2], + vg_r = vr - vg, + vg_b = vb - vg; + + if( vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 ) + { + vg_stream_write( stream, (u8[]){ QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2) }, 1 ); + } + else if( vg_r > -9 && vg_r < 8 && + vg > -33 && vg < 32 && + vg_b > -9 && vg_b < 8 ) + { + vg_stream_write( stream, (u8[]){ QOI_OP_LUMA | (vg + 32), + (vg_r + 8) << 4 | (vg_b + 8) }, 2 ); + } + else + { + vg_stream_write( stream, (u8[]){ QOI_OP_RGB }, 1 ); + vg_stream_write( stream, px.rgba, 3 ); + } + } + else + { + vg_stream_write( stream, (u8 []){ QOI_OP_RGBA }, 1 ); + vg_stream_write( stream, px.rgba, 4 ); + } + } + } + px_prev = px; } } - - /* - * Complete the call - * -------------------------- - */ - - vg_async_task_dispatch( task, async_vg_tex2d_upload ); + vg_stream_write( stream, qoi_padding, sizeof(qoi_padding) ); + return 1; } -void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest ) -{ - THREAD_1; - vg_stack_clear( &vg.scratch ); - - u32 size; - const void *data = vg_file_read( &vg.scratch, path, &size, 0 ); - vg_tex2d_load_qoi_async( data, size, flags, dest ); -} +/* VG_PART + * ------------------------------------------------------------------------------------------------------------------ */ -u32 vg_query_qoi_storage_size( const qoi_desc *desc ) +struct { - return - desc->width * desc->height * (desc->channels + 1) + - QOI_HEADER_SIZE + sizeof(qoi_padding); + GLuint error2d, errorcube; } +_vg_tex; -bool vg_encode_qoi2( const u8 *pixels, const qoi_desc *desc, u8 *out_bytes, int *out_len ) +VG_API void _vg_tex_init(void) { - int i = 0, p = 0, run = 0; - int px_len, px_end, px_pos, channels; - qoi_rgba_t index[64]; - qoi_rgba_t px, px_prev; - - if( desc->width == 0 || desc->height == 0 ) - { - vg_error( "vg_write_qoi2: 0 width/height: %dx%d\n", desc->width, desc->height ); - return 0; - } - - if( desc->channels < 3 || desc->channels > 4 ) - { - vg_error( "vg_write_qoi2: invalid channels: %d (min: 3, max: 4)\n", desc->channels ); - return 0; - } + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); - if( desc->colorspace > 1 ) + static u8 const_vg_tex2d_err[] = { - vg_error( "vg_write_qoi2: invalid colourspace: %d (min:0 SRGB, max:1 LINEAR)\n", desc->colorspace ); - return 0; - } - - int total_pixels = desc->height * desc->width; - if( total_pixels >= QOI_PIXELS_MAX ) + 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, + 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, + }; + + glGenTextures( 1, &_vg_tex.error2d ); + glBindTexture( GL_TEXTURE_2D, _vg_tex.error2d ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + + glGenTextures( 1, &_vg_tex.errorcube ); + glBindTexture( GL_TEXTURE_CUBE_MAP, _vg_tex.errorcube ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + + for( u32 j=0; j<6; j ++ ) { - vg_error( "vg_write_qoi2: too many pixels (%d exceeds limit of %d)\n", total_pixels, QOI_PIXELS_MAX ); - return 0; + glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, GL_RGBA, 4, 4, + 0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err ); } +} - qoi_write_32( out_bytes, &p, QOI_MAGIC ); - qoi_write_32( out_bytes, &p, desc->width ); - qoi_write_32( out_bytes, &p, desc->height ); - out_bytes[p++] = desc->channels; - out_bytes[p++] = desc->colorspace; +struct tex_upload_task +{ + vg_tex *tex; + u32 width, height, channels, flags; + u8 image_buffer[]; +}; - QOI_ZEROARR(index); +VG_API_INTERNAL static void _vg_tex_upload( struct tex_upload_task *in_args, vg_async_info *async ) +{ + VG_ASSERT( _vg_tex.errorcube && _vg_tex.error2d ); - run = 0; - px_prev.rgba.r = 0; - px_prev.rgba.g = 0; - px_prev.rgba.b = 0; - px_prev.rgba.a = 255; - px = px_prev; + u32 flags = in_args->flags; + vg_tex *tex = in_args->tex; - px_len = desc->width * desc->height * desc->channels; - px_end = px_len - desc->channels; - channels = desc->channels; + u32 pixel_format = 0; + if( in_args->channels == 3 ) pixel_format = GL_RGB; + else if( in_args->channels == 4 ) pixel_format = GL_RGBA; + else vg_fatal_error( "Can't upload texture with '%u' channels.\n", in_args->channels ); - for( px_pos = 0; px_pos < px_len; px_pos += channels ) + if( flags & VG_TEX_ERROR ) + { + tex->name = (flags & VG_TEX_CUBEMAP)? _vg_tex.errorcube: _vg_tex.error2d; + tex->flags = (flags & VG_TEX_CUBEMAP) | VG_TEX_ERROR | VG_TEX_COMPLETE; + } + else { - px.rgba.r = pixels[px_pos + 0]; - px.rgba.g = pixels[px_pos + 1]; - px.rgba.b = pixels[px_pos + 2]; + glGenTextures( 1, &tex->name ); - if( channels == 4 ) - px.rgba.a = pixels[px_pos + 3]; + u32 filter_min = 0, + filter_mag = 0; + if( flags & VG_TEX_LINEAR ) + { + if( flags & VG_TEX_NOMIP ) filter_min = GL_LINEAR; + else filter_min = GL_LINEAR_MIPMAP_LINEAR; + filter_mag = GL_LINEAR; + } + else + { + VG_ASSERT( flags & VG_TEX_NEAREST ); + filter_min = GL_NEAREST; + filter_mag = GL_NEAREST; + } - if( px.v == px_prev.v ) + if( flags & VG_TEX_CUBEMAP ) { - run++; - if( run == 62 || px_pos == px_end ) + u32 w = in_args->width, + h = in_args->height/6; + + glBindTexture( GL_TEXTURE_CUBE_MAP, tex->name ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter_min ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter_mag ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); /* can this be anything else? */ + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + + for( u32 j=0; j<6; j ++ ) { - out_bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; + u32 offset = w*h*j*in_args->channels; + glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, pixel_format, + w, h, + 0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer + offset ); } + + if( !(flags & VG_TEX_NOMIP) ) + glGenerateMipmap( GL_TEXTURE_CUBE_MAP ); } else { - int index_pos; - if( run > 0 ) - { - out_bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; - } + glBindTexture( GL_TEXTURE_2D, tex->name ); + glTexImage2D( GL_TEXTURE_2D, 0, pixel_format, in_args->width, in_args->height, + 0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer ); - index_pos = QOI_COLOR_HASH(px) % 64; + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_mag ); - if( index[index_pos].v == px.v ) - out_bytes[p++] = QOI_OP_INDEX | index_pos; - else - { - index[index_pos] = px; + u32 wrap_s = 0, + wrap_t = 0; - if( px.rgba.a == px_prev.rgba.a ) - { - i8 vr = px.rgba.r - px_prev.rgba.r, - vg = px.rgba.g - px_prev.rgba.g, - vb = px.rgba.b - px_prev.rgba.b, - vg_r = vr - vg, - vg_b = vb - vg; - - if( vr > -3 && vr < 2 && - vg > -3 && vg < 2 && - vb > -3 && vb < 2 ) - { - out_bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); - } - else if( vg_r > -9 && vg_r < 8 && - vg > -33 && vg < 32 && - vg_b > -9 && vg_b < 8 ) - { - out_bytes[p++] = QOI_OP_LUMA | (vg + 32); - out_bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); - } - else - { - out_bytes[p++] = QOI_OP_RGB; - out_bytes[p++] = px.rgba.r; - out_bytes[p++] = px.rgba.g; - out_bytes[p++] = px.rgba.b; - } - } - else - { - out_bytes[p++] = QOI_OP_RGBA; - out_bytes[p++] = px.rgba.r; - out_bytes[p++] = px.rgba.g; - out_bytes[p++] = px.rgba.b; - out_bytes[p++] = px.rgba.a; - } + if( flags & VG_TEX_CLAMP ) + { + wrap_s = GL_CLAMP_TO_EDGE; + wrap_t = GL_CLAMP_TO_EDGE; + } + else + { + VG_ASSERT( flags & VG_TEX_REPEAT ); + wrap_s = GL_REPEAT; + wrap_t = GL_REPEAT; } + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t ); + + if( !(flags & VG_TEX_NOMIP) ) + glGenerateMipmap( GL_TEXTURE_2D ); } - px_prev = px; + tex->flags = flags | VG_TEX_COMPLETE; } +} - for( i = 0; i < (int)sizeof(qoi_padding); i ++ ) - out_bytes[p ++] = qoi_padding[i]; +VG_API bool _vg_tex_load_stream( vg_tex *out_tex, vg_stream *in_stream, u32 flags ) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); - *out_len = p; - return 1; + qoi_desc qoi; + u32 size = vg_qoi_stream_init( &qoi, in_stream ); + if( size ) + { + _vg_async_context_push_groups( VG_ASYNC_GROUP_OPENGL ); + struct tex_upload_task *out_args = _vg_async_alloc( VG_THREAD_MAIN_ID, sizeof( struct tex_upload_task ) + size ); + vg_qoi_stream_decode( &qoi, in_stream, out_args->image_buffer, flags & VG_TEX_FLIP_V? 1: 0 ); + out_args->tex = out_tex; + out_args->width = qoi.width; + out_args->height = qoi.height; + out_args->channels = qoi.channels; + out_args->flags = flags; + _vg_async_send( out_args, (vg_async_fn)_vg_tex_upload ); + _vg_async_context_pop_groups(); + return 1; + } + else + return 0; +} + +VG_API void _vg_tex_load( vg_tex *out_tex, const c8 *path, u32 flags ) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); + + bool error = 0; + vg_stream file; + if( vg_file_stream_open( &file, path, VG_STREAM_READ ) ) + { + if( !_vg_tex_load_stream( out_tex, &file, flags ) ) + error = 1; + vg_file_stream_close( &file ); + } + else + error = 1; + + if( error ) + { + _vg_async_context_push_groups( VG_ASYNC_GROUP_OPENGL ); + struct tex_upload_task *out_args = _vg_async_alloc( VG_THREAD_MAIN_ID, sizeof( struct tex_upload_task ) ); + out_args->tex = out_tex; + out_args->width = 0; + out_args->height = 0; + out_args->channels = 0; + out_args->flags = VG_TEX_ERROR; + _vg_async_send( out_args, (vg_async_fn)_vg_tex_upload ); + _vg_async_context_pop_groups(); + } +} + +VG_API u32 vg_tex_name( GLuint target, vg_tex *tex ) +{ + if( !tex ) return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube; + if( tex->flags & VG_TEX_COMPLETE ) return tex->name; + else return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube; +} + +VG_API void vg_tex_bind( GLuint target, vg_tex *tex, u32 slot ) +{ + glActiveTexture( GL_TEXTURE0 + slot ); + glBindTexture( target, vg_tex_name( target, tex ) ); +} + +VG_API void vg_tex_delete( vg_tex *tex ) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); + VG_ASSERT( tex->flags & VG_TEX_COMPLETE ); + if( !(tex->flags & VG_TEX_ERROR) ) + glDeleteTextures( 1, &tex->name ); + vg_zero_mem( tex, sizeof(vg_tex) ); } diff --git a/vg_tex.h b/vg_tex.h index b257808..bc1c302 100644 --- a/vg_tex.h +++ b/vg_tex.h @@ -1,72 +1,66 @@ -/* Copyright (C) 2021-2025 Harry Godden (hgn) - All Rights Reserved - * - * A portion of this file is copied and altered from the QOI projects' source, - * Originally written by Dominic Szablewski. It is slightly modified. - * For the original unaltered QOI header, you can find it here: - * https://github.com/phoboslab/qoi/blob/master/qoi.h - * - * Copyright (C) 2021, Dominic Szablewski - * SPDX-License-Identifier: MIT - * - * MIT License - Copyright (c) 2022 Dominic Szablewski - https://phoboslab.org - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_tex.c" +#else -#pragma once -#include "vg_log.h" -#include "vg_image.h" -#include "vg_engine.h" +/* TODO: Include Okaypeg alongside QOI. */ -#define QOI_SRGB 0 -#define QOI_LINEAR 1 +typedef union qoi_rgba_t qoi_rgba_t; +typedef struct qoi_desc qoi_desc; +typedef struct vg_qoi_ctx vg_qoi_ctx; +typedef struct vg_tex vg_tex; +struct vg_tex +{ + u32 name; + u32 flags; +}; -typedef struct { - unsigned int width; - unsigned int height; - unsigned char channels; - unsigned char colorspace; -} qoi_desc; +VG_API void _vg_tex_init(void); -struct vg_sprite +#pragma pack(push,1) +union qoi_rgba_t { - v4f uv_xywh; + u8 rgba[4]; + u32 v; }; -#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 -#define VG_TEX2D_CUBEMAP 0x20 -#define VG_TEX2D_ERROR 0x40 +struct qoi_desc +{ + u32 magic; + u32 width; + u32 height; + u8 channels; + u8 colorspace; +}; +#pragma pack(pop) -/* options to create texutres; call only from loader thread. - * *dest will be replaced synchronously by the main thread when ready. */ +# define QOI_SRGB 0 +# define QOI_LINEAR 1 -void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest ); +/* Reading qois */ +VG_TIER_0 u32 vg_qoi_stream_init( qoi_desc *desc, vg_stream *file ); +VG_TIER_0 void vg_qoi_stream_decode( qoi_desc *desc, vg_stream *file, u8 *dest_buffer, bool v_flip ); -void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, - u32 flags, GLuint *dest ); +/* Writing qois */ +VG_TIER_0 u32 vg_query_qoi_max_compressed_size( const qoi_desc *desc ); +VG_TIER_0 u32 vg_qoi_stream_encode( const qoi_desc *desc, const u8 *pixels, vg_stream *stream, bool v_flip ); -void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest ); +# if defined( VG_ENGINE ) +# define VG_TEX_LINEAR 0x1 +# define VG_TEX_NEAREST 0x2 +# define VG_TEX_REPEAT 0x4 +# define VG_TEX_CLAMP 0x8 +# define VG_TEX_NOMIP 0x10 +# define VG_TEX_CUBEMAP 0x20 +# define VG_TEX_ERROR 0x40 +# define VG_TEX_COMPLETE 0x80 +# define VG_TEX_FLIP_V 0x100 +# define VG_TEX_FRAMEBUFFER_ATTACHMENT 0x200 +# define VG_TEX_PRIVATE 0x400 /* used on renderbuffers... */ -u32 vg_query_qoi_storage_size( const qoi_desc *desc ); -bool vg_encode_qoi2( const u8 *pixels, const qoi_desc *desc, u8 *out_bytes, int *out_len ); +VG_API u32 vg_tex_name( GLuint target, vg_tex *tex ); +VG_API void vg_tex_bind( GLuint target, vg_tex *tex, u32 slot ); +VG_API void vg_tex_delete( vg_tex *tex ); +VG_API void _vg_tex_load( vg_tex *out_tex, const c8 *path, u32 flags ); +VG_API bool _vg_tex_load_stream( vg_tex *out_tex, vg_stream *in_stream, u32 flags ); +# endif +#endif diff --git a/vg_thirdparty.c b/vg_thirdparty.c new file mode 100644 index 0000000..ac24e53 --- /dev/null +++ b/vg_thirdparty.c @@ -0,0 +1,2 @@ +#define VG_THIRDPARTY +#include "vg/vg.h" diff --git a/vg_tower.c b/vg_tower.c index 84f6ed0..9b877c2 100644 --- a/vg_tower.c +++ b/vg_tower.c @@ -1,6 +1,3 @@ -#pragma once -#include "vg_tower.h" - struct _vg_tower _vg_tower; vg_signal_id _vg_tower_create_signal( const char *name ) diff --git a/vg_tower.h b/vg_tower.h index b3b9cae..988c56e 100644 --- a/vg_tower.h +++ b/vg_tower.h @@ -1,5 +1,6 @@ -#pragma once -#include "vg_async2.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_tower.c" +#else typedef u8 vg_signal_id; @@ -28,3 +29,5 @@ void _vg_tower_register_trigger( u64 mask, void(*fn)( vg_signal_id id, bool stat void _vg_tower_set_flag( vg_signal_id id, bool state ); bool _vg_tower_clearence( u64 mask ); u64 _vg_tower_mask( vg_signal_id id ); + +#endif diff --git a/vg_ui/console.c b/vg_ui/console.c new file mode 100644 index 0000000..3639d05 --- /dev/null +++ b/vg_ui/console.c @@ -0,0 +1,408 @@ +u32 str_lev_distance( const char *s1, const char *s2 ) +{ + u32 m = strlen( s1 ), + n = strlen( s2 ); + + if( m==0 ) return n; + if( n==0 ) return m; + + u32 costs[ 256 ]; + + for( u32 k=0; k<=n; k++ ) + costs[k] = k; + + u32 i = 0; + for( u32 i=0; i=0; j -- ) + if( score > vg_console.suggestions[j].lev_score ) + best_pos = j; + + /* insert if good score */ + if( best_pos < VG_ARRAY_LEN( vg_console.suggestions ) ) + { + int start = VG_MIN( vg_console.suggestion_count, VG_ARRAY_LEN( vg_console.suggestions )-1 ); + for( int j=start; j>best_pos; j -- ) + vg_console.suggestions[j] = vg_console.suggestions[j-1]; + + vg_console.suggestions[ best_pos ].str = str; + vg_console.suggestions[ best_pos ].len = strlen( str ); + vg_console.suggestions[ best_pos ].lev_score = score; + + if( vg_console.suggestion_count < VG_ARRAY_LEN( vg_console.suggestions ) ) + vg_console.suggestion_count ++; + } +} + +static void console_update_suggestions( ui_context *ctx ) +{ + if( ctx->focused_control_type != k_ui_control_textbox || ctx->textbuf != vg_console.input ) + return; + + vg_console.suggestion_count = 0; + vg_console.suggestion_select = -1; + vg_console.suggestion_maxlen = 0; + + /* + * - must be typing something + * - must be at the end + * - prev char must not be a whitespace + * - cursors should match + */ + + if( ctx->textbox.cursor_pos == 0 ) return; + if( ctx->textbox.cursor_pos != ctx->textbox.cursor_user ) return; + if( vg_console.input[ ctx->textbox.cursor_pos ] != '\0' ) return; + + if( (vg_console.input[ ctx->textbox.cursor_pos -1 ] == ' ') || + (vg_console.input[ ctx->textbox.cursor_pos -1 ] == '\t') ) + return; + + char temp[128]; + const char *args[32]; + + int token_count = vg_console_tokenize( vg_console.input, temp, args ); + if( !token_count ) return; + vg_console.suggestion_pastepos = args[token_count-1]-temp; + + /* Score all our commands and cvars */ + if( token_count == 1 ) + { + for( int i=0; iname, args[0], 1 ); + } + + for( int i=0; iname, args[0], 1 ); + } + } + else + { + vg_cmd *cmd = vg_console_match_cmd( args[0] ); + vg_var *var = vg_console_match_var( args[0] ); + + if( cmd ) + if( cmd->poll_suggest ) + cmd->poll_suggest( token_count-1, &args[1] ); + } + + /* some post processing */ + for( int i=0; itextbox.cursor_user, + &ctx->textbox.cursor_pos, 10000, 1 ); + } + else + { + strncpy( target, + vg_console.suggestions[ vg_console.suggestion_select ].str, + VG_ARRAY_LEN( vg_console.input )-1 ); + + _ui_textbox_move_cursor( ctx, + &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, 10000, 1 ); + _ui_textbox_put_char( ctx, ' ' ); + } +} + +static void _console_suggest_store_normal(void) +{ + if( vg_console.suggestion_select == -1 ) + { + char *target = &vg_console.input[ vg_console.suggestion_pastepos ]; + strcpy( vg_console.input_copy, target ); + } +} + +void console_suggest_next( ui_context *ctx ) +{ + if( vg_console.suggestion_count ) + { + _console_suggest_store_normal(); + + vg_console.suggestion_select ++; + + if( vg_console.suggestion_select >= vg_console.suggestion_count ) + vg_console.suggestion_select = -1; + + _console_fetch_suggestion( ctx ); + } +} + +void console_suggest_prev( ui_context *ctx ) +{ + if( vg_console.suggestion_count ) + { + _console_suggest_store_normal(); + + vg_console.suggestion_select --; + + if( vg_console.suggestion_select < -1 ) + vg_console.suggestion_select = vg_console.suggestion_count-1; + + _console_fetch_suggestion( ctx ); + } +} + +static void _vg_console_on_update( ui_context *ctx, char *buf, u32 len, void *userdata ) +{ + if( buf == vg_console.input ) + { + console_update_suggestions( ctx ); + } +} + +static void console_history_get( char* buf, int entry_num ) +{ + if( !vg_console.history_count ) + return; + + int offset = VG_MIN( entry_num, vg_console.history_count -1 ), + pick = (vg_console.history_last - offset) % + VG_ARRAY_LEN( vg_console.history ); + strcpy( buf, vg_console.history[ pick ] ); +} + +static void _vg_console_on_up( ui_context *ctx, char *buf, u32 len, void *userdata ) +{ + if( buf == vg_console.input ) + { + vg_console.history_pos = + VG_MAX + ( + 0, + VG_MIN + ( + vg_console.history_pos+1, + VG_MIN + ( + VG_ARRAY_LEN( vg_console.history ), + vg_console.history_count - 1 + ) + ) + ); + + console_history_get( vg_console.input, vg_console.history_pos ); + _ui_textbox_move_cursor( ctx, + &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, + VG_ARRAY_LEN(vg_console.input)-1, 1 ); + } +} + +static void _vg_console_on_down( ui_context *ctx, char *buf, u32 len, void *userdata ) +{ + if( buf == vg_console.input ) + { + vg_console.history_pos = VG_MAX( 0, vg_console.history_pos-1 ); + console_history_get( vg_console.input, vg_console.history_pos ); + + _ui_textbox_move_cursor( ctx, + &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, + VG_ARRAY_LEN(vg_console.input)-1, 1 ); + } +} + +static void _vg_console_on_enter( ui_context *ctx, char *buf, u32 len, void *userdata ) +{ + if( buf == vg_console.input ) + { + if( !strlen( vg_console.input ) ) + return; + + vg_info( "%s\n", vg_console.input ); + + if( strcmp( vg_console.input, vg_console.history[ vg_console.history_last ]) ) + { + vg_console.history_last = ( vg_console.history_last + 1) % VG_ARRAY_LEN(vg_console.history ); + vg_console.history_count = VG_MIN( VG_ARRAY_LEN( vg_console.history ), vg_console.history_count + 1 ); + strcpy( vg_console.history[ vg_console.history_last ], vg_console.input ); + } + + vg_console.history_pos = -1; + vg_execute_console_input( vg_console.input, 0, 0 ); + _ui_textbox_move_cursor( ctx, &ctx->textbox.cursor_user, &ctx->textbox.cursor_pos, -10000, 1 ); + vg_console.input[0] = '\0'; + console_update_suggestions( ctx ); + } + + vg_console.auto_focus = 1; +} + +void vg_console_draw( ui_context *ctx ) +{ + if( !vg_console.enabled ) + return; + VG_MUTEX_LOCK( vg_log.lock ); + + int ptr = vg_log.log_line_current; + int const fh = ctx->font->sy, log_lines = 32; + int console_lines = VG_MIN( log_lines, vg_log.log_line_count ); + ui_rect rect_log = { 0, 0, ctx->area[0], log_lines*fh }, + rect_input = { 0, log_lines*fh + 1, ctx->area[0], fh*2 }, + rect_line = { 0, 0, ctx->area[0], fh }; + + /* + * log + */ + u32 bg_colour = (ui_colour( ctx, k_ui_bg )&0x00ffffff)|0x9f000000; + + ui_fill( ctx, rect_log, bg_colour ); + rect_line[1] = rect_log[1]+rect_log[3]-fh; + + for( int i=0; ifont->sx*vg_console.suggestion_pastepos; + rect_suggest[1] += rect_input[3]; + rect_suggest[2] = ctx->font->sx * vg_console.suggestion_maxlen; + rect_suggest[3] = vg_console.suggestion_count * fh; + + ui_fill( ctx, rect_suggest, bg_colour ); + + rect_suggest[3] = fh; + + for( int i=0; i -#include -#endif - -#include "vg/submodules/rax/rax.h" -#include "vg/submodules/rax/rax.c" - void vg_filebrowser_init( struct vg_filebrowser *browser ) { browser->open_result = k_dir_open_none; diff --git a/vg_ui/filebrowser.h b/vg_ui/filebrowser.h index 9b0ec2c..c5e4f1d 100644 --- a/vg_ui/filebrowser.h +++ b/vg_ui/filebrowser.h @@ -1,5 +1,6 @@ -#pragma once -#include "vg/vg_ui/imgui.h" +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_ui/filebrowser.c" +#else struct vg_filebrowser_entry { @@ -52,3 +53,5 @@ void vg_filebrowser_populate( struct vg_filebrowser *browser ); void vg_filebrowser_free_entries( struct vg_filebrowser *browser ); void vg_filebrowser_set_path_to_home( struct vg_filebrowser *browser ); + +#endif diff --git a/vg_ui/imgui.c b/vg_ui/imgui.c index ecfb7d9..160f76b 100644 --- a/vg_ui/imgui.c +++ b/vg_ui/imgui.c @@ -1,11 +1,5 @@ -#pragma once - -#include "vg_ui/imgui.h" -#include "vg_platform.h" -#include "vg_m.h" -#include "vg_font.h" -#include "vg_log.h" -#include +// TODO move this to the hconf! :D +#include "vg/vg_default_font.gc" void ui_init( ui_context *ctx, ui_vert *verts_buf, u32 verts_max, u16 *indices_buf, u32 indices_max ) { @@ -659,17 +653,14 @@ void ui_spacer( ui_context *ctx, ui_rect inout_panel ) ui_fill( ctx, inner, ui_colour( ctx, k_ui_bg+6 ) ); } -void ui_image( ui_context *ctx, ui_rect rect, void *resource, bool flip ) +void ui_image( ui_context *ctx, ui_rect rect, vg_tex *tex, bool flip ) { ui_flush( ctx, k_ui_shader_colour, NULL ); - if( flip ) ui_fill_rect( ctx, rect, 0xffffffff, (ui_px[4]){ 0,0,256,256 } ); else ui_fill_rect( ctx, rect, 0xffffffff, (ui_px[4]){ 0,256,256,0 } ); - - struct ui_batch_shader_data_image inf = { .resource = resource }; - ui_flush( ctx, k_ui_shader_image, &inf ); + ui_flush( ctx, k_ui_shader_image, tex ); } void ui_defocus_all( ui_context *ctx ) diff --git a/vg_ui/imgui.h b/vg_ui/imgui.h index 8ce4782..ed840e0 100644 --- a/vg_ui/imgui.h +++ b/vg_ui/imgui.h @@ -1,10 +1,6 @@ -#pragma once - -#include "vg_platform.h" -#include "vg_m.h" -#include "vg_font.h" -#include "vg_log.h" -#include +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_ui/imgui.c" +#else typedef i16 ui_px; typedef ui_px ui_rect[4]; @@ -237,7 +233,7 @@ struct ui_batch_shader_data_image struct ui_batch_shader_data_image_gradient { - void *resource; + vg_tex *tex; bool log; f32 scale; }; @@ -315,7 +311,7 @@ void ui_panel( ui_context *ctx, ui_rect in_rect, ui_rect out_panel ); void ui_label( ui_context *ctx, ui_rect rect, const char *text, ui_px size, ui_px gap, ui_rect r ); void ui_info( ui_context *ctx, ui_rect inout_panel, const char *text ); void ui_spacer( ui_context *ctx, ui_rect inout_panel ); -void ui_image( ui_context *ctx, ui_rect rect, void *resource, bool flip ); +void ui_image( ui_context *ctx, ui_rect rect, vg_tex *tex, bool flip ); enum ui_button_state ui_button_base( ui_context *ctx, ui_rect rect ); @@ -386,3 +382,5 @@ void ui_dev_colourview( ui_context *ctx ); extern vg_font_sheet vg_default_font_sheet; extern vg_font_face vgf_default_small, vgf_default_large, vgf_default_title; + +#endif diff --git a/vg_ui/imgui_impl_opengl.c b/vg_ui/imgui_impl_opengl.c index 69d1187..3d2ba35 100644 --- a/vg_ui/imgui_impl_opengl.c +++ b/vg_ui/imgui_impl_opengl.c @@ -1,8 +1,3 @@ -#include "vg_opengl.h" -#include "vg_shader.h" -#include "vg_ui/imgui.h" -#include "vg_engine.h" - struct vg_ui vg_ui = { .ctx = @@ -53,227 +48,14 @@ struct vg_ui vg_ui = .bg_inverse_ratio = {1,1}, }; -static struct vg_shader _shader_ui ={ - .name = "[vg] ui - transparent", - .vs = { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - "uniform vec2 uBGInverseRatio;" - "uniform vec2 uInverseFontSheet;" - "" - "out vec4 aTexCoords;" - "out vec4 aColour;" - "" - "void main(){" - "vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "gl_Position = proj_pos;" - "aTexCoords = vec4( a_uv * uInverseFontSheet, " - " (proj_pos.xy*0.5+0.5) * uBGInverseRatio );" - "aColour = a_colour;" - "}", - }, - .fs = { - .orig_file = NULL, - .static_src = - "uniform sampler2D uTexGlyphs;" - "uniform sampler2D uTexBG;" - "uniform vec4 uColour;" - "uniform float uSpread;" - "out vec4 FragColor;" - "" - "in vec4 aTexCoords;" - "in vec4 aColour;" - "" - "vec2 rand_hash22( vec2 p ){" - "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);" - "p3 += dot(p3, p3.yzx+19.19);" - "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));" - "}" - "" - "void main(){" - "vec4 diffuse = aColour;" - - "vec4 avg = vec4(0.0);" - - "if( aColour.a == 0.0 ){" - "avg = aColour;" - "avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;" - "}" - "else{" - "if( uSpread > 0.0001 ){" - "for( int i=0; i<4; i ++ ){" - "vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));" - "avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );" - "}" - "avg *= 0.25;" - "avg.a = 1.0;" - "avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );" - "}" - "else{" - "avg = aColour;" - "}" - "}" - - "FragColor = avg * uColour;" - "}" - } -}; - -static struct vg_shader _shader_ui_image = { - .name = "[vg] ui_image", - .vs = - { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - - "out vec2 aTexCoords;" - "out vec4 aColour;" - "out vec2 aWsp;" - - "void main()" - "{" - "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "aTexCoords = a_uv * 0.00390625;" - "aColour = a_colour;" - - "aWsp = a_co;" - "}", - }, - .fs = - { - .orig_file = NULL, - .static_src = - "uniform sampler2D uTexImage;" - "uniform vec4 uColour;" - "out vec4 FragColor;" - - "in vec2 aTexCoords;" - "in vec4 aColour;" - "in vec2 aWsp;" - - "void main()" - "{" - "vec4 colour = texture( uTexImage, aTexCoords );" - "FragColor = colour * aColour * uColour;" - "}" - } -}; - -static struct vg_shader _shader_ui_image_grad = { - .name = "[vg] ui_image (gradient)", - .vs = - { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - - "out vec2 aTexCoords;" - "out vec4 aColour;" - "out vec2 aWsp;" - - "void main()" - "{" - "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "aTexCoords = a_uv * 0.00390625;" - "aColour = a_colour;" - - "aWsp = a_co;" - "}", - }, - .fs = - { - .orig_file = NULL, - .static_src = - "uniform sampler2D uTexImage;" - "uniform vec4 uColour;" - "uniform int uLog;" - "uniform float uScale;" - - "out vec4 FragColor;" - - "in vec2 aTexCoords;" - "in vec4 aColour;" - "in vec2 aWsp;" - - "void main()" - "{" - "vec4 colour = texture( uTexImage, aTexCoords );" - "float v = colour.r;" - "if( uLog == 1 ){" - "v = log(v);" - "}" - "v *= uScale;" - "FragColor = vec4(vec3(v)*uColour.rgb,1.0);" - "}" - } -}; - -static struct vg_shader _shader_ui_hsv = { - .name = "[vg] ui_hsv", - .vs = { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - - "out vec2 aTexCoords;" - "out vec4 aColour;" - "out vec2 aWsp;" - - "void main()" - "{" - "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "aTexCoords = a_uv * 0.00390625;" - "aColour = a_colour;" - - "aWsp = a_co;" - "}", - }, - .fs = - { - .orig_file = NULL, - .static_src = - "uniform float uHue;" - "out vec4 FragColor;" - - "in vec2 aTexCoords;" - "in vec4 aColour;" - "in vec2 aWsp;" - - "void main()" - "{" - "vec3 c = vec3( uHue, aTexCoords );" - "vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);" - "vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);" - "vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);" - "FragColor = vec4( colour, 1.0 );" - "}" - } -}; - -void vg_ui_set_screen( i32 width, i32 height ) +VG_API void vg_ui_set_screen( i32 width, i32 height ) { ui_set_area( &vg_ui.ctx, width, height ); m3x3_identity( vg_ui.pv ); m3x3_translate( vg_ui.pv, (v3f){ -1.0f, 1.0f, 0.0f } ); - m3x3_scale( vg_ui.pv, (v3f){ 1.0f/((f32)width*0.5f), - -1.0f/((f32)height*0.5f), 1.0f } ); + m3x3_scale( vg_ui.pv, (v3f){ 1.0f/((f32)width*0.5f), -1.0f/((f32)height*0.5f), 1.0f } ); } -void ui_impl_render_batch( ui_context *ctx, ui_batch *batch, - enum ui_shader shader, void *shader_data ) +void ui_impl_render_batch( ui_context *ctx, ui_batch *batch, enum ui_shader shader, void *shader_data ) { glBindVertexArray( vg_ui.vao ); glBindBuffer( GL_ARRAY_BUFFER, vg_ui.vbo ); @@ -296,86 +78,63 @@ void ui_impl_render_batch( ui_context *ctx, ui_batch *batch, if( shader == k_ui_shader_colour ) { - glUseProgram( _shader_ui.id ); - glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1, GL_FALSE, (float *)vg_ui.pv ); - glUniform4fv( glGetUniformLocation( _shader_ui.id, "uColour" ), 1, vg_ui.colour ); - + shader_vgui_use(); + shader_vgui_uPv( vg_ui.pv ); + shader_vgui_uColour( vg_ui.colour ); + glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, vg_ui.tex_glyphs ); - glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 ); - - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, vg_ui.tex_bg ); - glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexBG" ), 1 ); - glUniform1f( glGetUniformLocation( _shader_ui.id, "uSpread" ), vg_ui.frosting ); - glUniform2fv( glGetUniformLocation( _shader_ui.id, "uBGInverseRatio" ),1, vg_ui.bg_inverse_ratio ); - glUniform2fv( glGetUniformLocation( _shader_ui.id, "uInverseFontSheet" ), 1, vg_ui.inverse_font_sheet ); + shader_vgui_uTexGlyphs( 0 ); + + vg_tex_bind( GL_TEXTURE_2D, vg_ui.tex_bg, 1 ); + shader_vgui_uTexBG( 1 ); + shader_vgui_uSpread( vg_ui.frosting ); + shader_vgui_uBGInverseRatio( vg_ui.bg_inverse_ratio ); + shader_vgui_uInverseFontSheet( vg_ui.inverse_font_sheet ); } else if( shader == k_ui_shader_image ) { - glUseProgram( _shader_ui_image.id ); - glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1, - GL_FALSE, (float *)vg_ui.pv ); - glUniform1i( glGetUniformLocation(_shader_ui_image.id,"uTexImage"), 0 ); - glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1, - vg_ui.colour ); - - struct ui_batch_shader_data_image *inf = shader_data; - GLuint *image = inf->resource; - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, *image ); + shader_vgui_image_use(); + shader_vgui_image_uPv( vg_ui.pv ); + shader_vgui_image_uTexImage( 0 ); + shader_vgui_image_uColour( vg_ui.colour ); + vg_tex_bind( GL_TEXTURE_2D, shader_data, 0 ); } else if( shader == k_ui_shader_grad ) { - glUseProgram( _shader_ui_image_grad.id ); - glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image_grad.id, "uPv" ), 1, GL_FALSE, (float *)vg_ui.pv ); - glUniform1i( glGetUniformLocation(_shader_ui_image_grad.id,"uTexImage"), 0 ); - glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1, vg_ui.colour ); + shader_vgui_image_grad_use(); + shader_vgui_image_grad_uPv( vg_ui.pv ); + shader_vgui_image_grad_uTexImage( 0 ); + shader_vgui_image_grad_uColour( vg_ui.colour ); struct ui_batch_shader_data_image_gradient *inf = shader_data; - glUniform1i( glGetUniformLocation(_shader_ui_image_grad.id,"uLog"), inf->log ); - glUniform1f( glGetUniformLocation(_shader_ui_image_grad.id,"uScale"), inf->scale ); - - GLuint *image = inf->resource; - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, *image ); + shader_vgui_image_grad_uLog( inf->log ); + shader_vgui_image_grad_uScale( inf->scale ); + vg_tex_bind( GL_TEXTURE_2D, inf->tex, 0 ); } else if( shader == k_ui_shader_hsv ) { struct ui_batch_shader_data_hsv *inf = shader_data; - glUseProgram( _shader_ui_hsv.id ); - glUniformMatrix3fv( glGetUniformLocation( _shader_ui_hsv.id, "uPv" ), 1, GL_FALSE, (float *)vg_ui.pv ); - glUniform1f( glGetUniformLocation(_shader_ui_hsv.id,"uHue"), inf->hue ); + shader_vgui_image_hsv_use(); + shader_vgui_image_hsv_uPv( vg_ui.pv ); + shader_vgui_image_hsv_uHue( inf->hue ); } else vg_fatal_error( "Invalid UI shader (%d)\n", shader ); - glDrawElements( GL_TRIANGLES, batch->indice_count, GL_UNSIGNED_SHORT, - (void *)((size_t)batch->indice_offset) ); + glDrawElements( GL_TRIANGLES, batch->indice_count, GL_UNSIGNED_SHORT, (void *)((size_t)batch->indice_offset) ); } -void vg_ui_post_update(void) +VG_API void vg_ui_post_update(void) { - if( vg_ui.ctx.wants_mouse ) - { - SDL_SetWindowGrab( vg.window, SDL_FALSE ); - SDL_SetRelativeMouseMode( SDL_FALSE ); - } - else - { - SDL_SetWindowGrab( vg.window, SDL_TRUE ); - SDL_SetRelativeMouseMode( SDL_TRUE ); - } - + _vg_window_set_fpv_mouse( !vg_ui.ctx.wants_mouse ); SDL_SetCursor( vg_ui.cursor_map[ vg_ui.ctx.cursor ] ); SDL_ShowCursor(1); } void vg_ui_set_mouse_pos( ui_px x, ui_px y ) { - SDL_WarpMouseInWindow( vg.window, x, y ); + _vg_window_set_mouse( x, y ); vg_ui.ctx.mouse[0] = x; vg_ui.ctx.mouse[1] = y; vg_ui.ctx.mouse_pos_overriden = 1; @@ -458,15 +217,12 @@ bool _wrap_sdl_hasclipboard_text(void) return SDL_HasClipboardText(); } -void vg_ui_init(void) +VG_API void _vg_ui_init(void) { - vg_compile_shader( &_shader_ui ); - vg_compile_shader( &_shader_ui_hsv ); - vg_compile_shader( &_shader_ui_image ); - vg_compile_shader( &_shader_ui_image_grad ); + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) ); u32 verts = 30000, indices = 20000; - ui_init( &vg_ui.ctx, malloc( sizeof(ui_vert)*verts ), verts, malloc( sizeof(u16)*indices ), indices ); + ui_init( &vg_ui.ctx, vg_malloc( sizeof(ui_vert)*verts ), verts, vg_malloc( sizeof(u16)*indices ), indices ); /* callbacks */ vg_ui.ctx.render_batch = ui_impl_render_batch; @@ -521,9 +277,7 @@ void vg_ui_init(void) data = 0; /* This is fince since its before the load thread even starts */ - vg_stack_clear( &vg.scratch ); - u8 *image = vg_stack_allocate( &vg.scratch, total, 8, NULL ); - + u8 image[512*512]; while( pixels < total ) { for( int b = 31; b >= 0; b-- ) @@ -554,10 +308,6 @@ void vg_ui_init(void) void ui_free(void) { - vg_free_shader( &_shader_ui ); - vg_free_shader( &_shader_ui_hsv ); - vg_free_shader( &_shader_ui_image ); - vg_free_shader( &_shader_ui_image_grad ); free( vg_ui.ctx.indice_buffer ); free( vg_ui.ctx.vertex_buffer ); } diff --git a/vg_ui/imgui_impl_opengl.h b/vg_ui/imgui_impl_opengl.h new file mode 100644 index 0000000..5cc4cd4 --- /dev/null +++ b/vg_ui/imgui_impl_opengl.h @@ -0,0 +1,28 @@ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_ui/imgui_impl_opengl.c" +#else + +struct vg_ui +{ + GLuint vao, vbo, ebo; + m3x3f pv; + ui_context ctx; + GLuint tex_glyphs; + v2f inverse_font_sheet; + + SDL_Cursor *cursor_map[ k_ui_cursor_max ]; + + /* at some point this should be implementation specific? */ + v4f colour; + f32 frosting; + v2f bg_inverse_ratio; + + vg_tex *tex_bg; +} +extern vg_ui; + +VG_API void _vg_ui_init(void); +VG_API void vg_ui_post_update(void); +VG_API void vg_ui_set_screen( i32 width, i32 height ); + +#endif diff --git a/vg_vorbis.c b/vg_vorbis.c new file mode 100644 index 0000000..266a167 --- /dev/null +++ b/vg_vorbis.c @@ -0,0 +1,69 @@ +/* + * adapted from stb_vorbis.h, since the original does not handle mono->stereo + */ +int stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, int len ) +{ + int n = 0, c = 1; + if( f->channels < 2 ) c = 0; + + while( n < len ) { + int k = f->channel_buffer_end - f->channel_buffer_start; + + if( n+k >= len ) + k = len - n; + + for( int j=0; j < k; ++j ) { + *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j]; + *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j]; + } + + n += k; + f->channel_buffer_start += k; + + if( n == len ) + break; + + if( !stb_vorbis_get_frame_float( f, NULL, NULL )) + break; + } + + return n; +} + +/* + * ........ more wrecked code sorry! + */ +int stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, signed short *buffer, int len ) +{ + int n = 0, c = 1; + if( f->channels < 2 ) c = 0; + + while( n < len ) { + int k = f->channel_buffer_end - f->channel_buffer_start; + + if( n+k >= len ) + k = len - n; + + for( int j=0; j < k; ++j ) { + float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j], + sr = f->channel_buffers[ c ][f->channel_buffer_start+j], + s = 0.5f*(sl+sr); + + if( s < -1.0f ) s = -1.0f; + if( s > 1.0f ) s = 1.0f; + + *buffer++ = s * 32767.0f; + } + + n += k; + f->channel_buffer_start += k; + + if( n == len ) + break; + + if( !stb_vorbis_get_frame_float( f, NULL, NULL )) + break; + } + + return n; +} diff --git a/vg_vorbis.h b/vg_vorbis.h index b27bf4d..f1e60e9 100644 --- a/vg_vorbis.h +++ b/vg_vorbis.h @@ -1,12 +1,4 @@ -#define STB_VORBIS_HEADER_ONLY -#define STB_VORBIS_MAX_CHANNELS 2 -#include "submodules/stb/stb_vorbis.c" -#include "vg_platform.h" -#include "vg_m.h" +/* This is just an extension to submodules/stb/stb_vorbis.c, so that we don't have to patch the file directly. */ -/* extended by vg */ -int -stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, - int len ); -int -stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len ); +int stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, int len ); +int stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, signed short *buffer, int len ); diff --git a/vg_window.c b/vg_window.c new file mode 100644 index 0000000..fc4d50f --- /dev/null +++ b/vg_window.c @@ -0,0 +1,255 @@ +struct _vg_window _vg_window; + +void GLAPIENTRY MessageCallback( GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam ) +{ + if( severity == GL_DEBUG_SEVERITY_HIGH ) + { + vg_error( "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + } + else + { + vg_low( "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + } +} + +VG_API void _vg_window_register(void) +{ + vg_console_reg_var( "vg_screen_mode", &_vg_window.display_mode, k_var_dtype_i32, VG_VAR_PERSISTENT ); + vg_console_reg_var( "vg_vsync", &_vg_window.vsync, k_var_dtype_i32, VG_VAR_PERSISTENT ); +} + +VG_API void _vg_window_init(void) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL|VG_THREAD_OWNS_OPENGL ) ); + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); +#if 0 + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 ); +#endif + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR, SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH ); + 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_STENCIL_SIZE, 0 ); + + /* + * Get monitor information + */ + vg_info( "Getting display count\n" ); + i32 display_count = 0, + display_index = 0, + mode_index = 0; + + SDL_DisplayMode video_mode; + if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) ) + { + vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() ); + SDL_Quit(); + exit(0); + } + + _vg_window.monitor_refresh_rate = video_mode.refresh_rate; + _vg_window.w = video_mode.w; + _vg_window.h = video_mode.h; + if( _vg_window.display_mode == k_vg_window_windowed ) + { + _vg_window.w = 1280; + _vg_window.h = 720; + } + +#ifndef _WIN32 + SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" ); + SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" ); + SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" ); +#endif + + u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_GRABBED | + SDL_WINDOW_RESIZABLE; + + if( _vg_window.display_mode == k_vg_window_fullscreen ) + flags |= SDL_WINDOW_FULLSCREEN; + else if( _vg_window.display_mode == k_vg_window_fullscreen_desktop ) + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + + vg_info( "CreateWindow( %d %d %u )\n", _vg_window.w, _vg_window.h, flags ); + if(( _vg_window.sdl_hwindow = SDL_CreateWindow( "Voyager Game Engine", 0, 0, _vg_window.w, _vg_window.h, flags ))) + { + if( _vg_window.display_mode == k_vg_window_windowed ) + SDL_SetWindowPosition( _vg_window.sdl_hwindow, video_mode.w-_vg_window.w, 0 ); + } + else + { + vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() ); + exit(0); + } + + SDL_RaiseWindow( _vg_window.sdl_hwindow ); + SDL_SetWindowMinimumSize( _vg_window.sdl_hwindow, 1280, 720 ); + SDL_SetWindowMaximumSize( _vg_window.sdl_hwindow, 4096, 4096 ); + + vg_info( "CreateContext\n" ); + + /* ????? */ + if( SDL_IsTextInputActive() ) + SDL_StopTextInput(); + + /* + * OpenGL loading + */ + if( ( _vg_window.sdl_hopengl = SDL_GL_CreateContext( _vg_window.sdl_hwindow ) )) + { + SDL_GL_GetDrawableSize( _vg_window.sdl_hwindow, &_vg_window.w, &_vg_window.h ); + vg_success( "Window created (%dx%d)\n", _vg_window.w, _vg_window.h ); + } + else + { + vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() ); + SDL_Quit(); + exit(0); + } + + if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) + { + vg_error( "Glad Failed to initialize\n" ); + SDL_GL_DeleteContext( _vg_window.sdl_hopengl ); + SDL_Quit(); + exit(0); + } + + const unsigned char* glver = glGetString( GL_VERSION ); + vg_success( "Load setup complete, OpenGL version: %s\n", glver ); + + + glEnable ( GL_DEBUG_OUTPUT ); + glDebugMessageCallback( MessageCallback, 0 ); + + SDL_GL_SetSwapInterval(0); /* disable vsync while loading */ + SDL_DisplayMode dispmode; + if( !SDL_GetWindowDisplayMode( _vg_window.sdl_hwindow, &dispmode ) ) + if( dispmode.refresh_rate ) + _vg_window.monitor_refresh_rate = dispmode.refresh_rate; + + if( _vg_window.monitor_refresh_rate < 25 || _vg_window.monitor_refresh_rate > 300 ) + _vg_window.monitor_refresh_rate = 60; + vg_info( "Display refresh rate: %d, we chose: %d\n", dispmode.refresh_rate, _vg_window.monitor_refresh_rate ); + + SDL_SetRelativeMouseMode(1); +} + +VG_API void _vg_window_swap(void) +{ + /* Vsync switching + * ----------------------------- */ + if( _vg_window.vsync && (_vg_window.vsync_feature != k_vsync_feature_error) ) + { + /* turn on vsync if not enabled */ + + enum vsync_feature requested = k_vsync_feature_enabled; + if( _vg_window.vsync < 0 ) + requested = k_vsync_feature_enabled_adaptive; + + if( _vg_window.vsync_feature != requested ) + { + vg_info( "Setting swap interval\n" ); + int swap_interval = 1; + if( requested == k_vsync_feature_enabled_adaptive ) + swap_interval = -1; + + if( SDL_GL_SetSwapInterval( swap_interval ) == -1 ) + { + if( requested == k_vsync_feature_enabled ) + { + ui_start_modal( &vg_ui.ctx, "Vsync not supported on this system.\n\n" + "You may be overriding it in your" + " graphics \ncontrol panel.\n", + NULL, UI_MODAL_BAD, NULL ); + } + else + { + ui_start_modal( &vg_ui.ctx, "Adaptive Vsync is not supported by your system\n\n" + "You may be overriding it in your" + " graphics \ncontrol panel.\n", + NULL, UI_MODAL_BAD, NULL ); + } + _vg_window.vsync_feature = k_vsync_feature_error; + _vg_window.vsync = 0; + } + else + { + vg_success( "Vsync enabled (%d)\n", requested ); + _vg_window.vsync_feature = requested; + } + } + } + else + { + if( _vg_window.vsync_feature != k_vsync_feature_disabled ) + { + SDL_GL_SetSwapInterval( 0 ); + _vg_window.vsync_feature = k_vsync_feature_disabled; + } + } + + _vg_profiler_enter_block( vg.profiler, "vg_engine:swap" ); + SDL_GL_SwapWindow( _vg_window.sdl_hwindow ); + _vg_profiler_exit_block( vg.profiler ); +} + +VG_API void _vg_window_size_changed(void) +{ + i32 w, h; + SDL_GL_GetDrawableSize( _vg_window.sdl_hwindow, &w, &h ); + + if( !w || !h ) + { + vg_warn( "Got a invalid framebuffer size: %dx%d... ignoring\n", w, h ); + } + else + { + + i32 delta[2] = { w - _vg_window.w, h - _vg_window.h }; + _vg_magi_area_change( delta ); + _vg_window.w = w; + _vg_window.h = h; + + vg_framebuffer_update_sizes(); + } +} + +VG_API void _vg_window_set_fpv_mouse( bool yes ) +{ + if( yes ) + { + SDL_SetWindowGrab( _vg_window.sdl_hwindow, SDL_TRUE ); + SDL_SetRelativeMouseMode( SDL_TRUE ); + } + else + { + SDL_SetWindowGrab( _vg_window.sdl_hwindow, SDL_FALSE ); + SDL_SetRelativeMouseMode( SDL_FALSE ); + } +} + +VG_API void _vg_window_set_mouse( i32 x, i32 y ) +{ + SDL_WarpMouseInWindow( _vg_window.sdl_hwindow, x, y ); +} + +VG_API void _vg_window_shutdown(void) +{ + SDL_GL_DeleteContext( _vg_window.sdl_hopengl ); +} diff --git a/vg_window.h b/vg_window.h new file mode 100644 index 0000000..f1b99b2 --- /dev/null +++ b/vg_window.h @@ -0,0 +1,42 @@ +#if defined( VG_IMPLEMENTATION ) +# include "vg/vg_window.c" +#else + +enum vg_window_display_mode +{ + k_vg_window_fullscreen_desktop = 0, + k_vg_window_fullscreen = 1, + k_vg_window_windowed = 2 +}; + +struct _vg_window +{ + SDL_Window *sdl_hwindow; + SDL_GLContext *sdl_hopengl; + i32 w, h, monitor_refresh_rate, vsync, display_index; + + i32 display_mode; + + enum vsync_feature + { + k_vsync_feature_disabled=0, + k_vsync_feature_enabled=1, + k_vsync_feature_enabled_adaptive=2, + k_vsync_feature_error=3 + } + vsync_feature; + + u32 swap_profiler; +} +extern _vg_window; + +VG_API void _vg_window_register(void); +VG_API void _vg_window_init(void); + +VG_API void _vg_window_shutdown(void); +VG_API void _vg_window_swap(void); +VG_API void _vg_window_size_changed(void); +VG_API void _vg_window_set_fpv_mouse( bool yes ); +VG_API void _vg_window_set_mouse( i32 x, i32 y ); + +#endif