--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Copyright (C) 2021 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+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);
+}
--- /dev/null
+out vec4 FragColor;
+
+in vec4 s_colour;
+
+void main()
+{
+ FragColor = s_colour;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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);
+}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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);
+}
--- /dev/null
+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 );
+}
--- /dev/null
+#include "vg/vg.hconf"
+
+#define VG_IMPLEMENTATION
+#include "vg/vg.hconf"
+#undef VG_IMPLEMENTATION
--- /dev/null
+#define VG_THIRDPARTY
+#include "vg/vg.hconf"
--- /dev/null
+/* Dependence (Standard)
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+#if !defined( VG_THIRDPARTY )
+# if defined(_WIN32)
+# include <windows.h>
+# include <dbghelp.h>
+# include <fileapi.h>
+# include <shlobj.h>
+# else
+# include <execinfo.h>
+# include <dirent.h>
+# include <sys/stat.h>
+# endif
+# include <fcntl.h>
+# include <unistd.h>
+
+# include <stdlib.h>
+# include <stdint.h>
+# include <stdalign.h>
+# include <stdio.h>
+# include <stddef.h>
+# include <stdarg.h>
+# include <string.h>
+# include <malloc.h>
+# include <time.h>
+# include <errno.h>
+# include <math.h>
+
+# if defined( VG_MULTITHREAD )
+# if !defined( VG_ENGINE )
+# include <pthread.h>
+# include <semaphore.h>
+# 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
-#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 )
{
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 )
" 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 )
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 );
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 );
-}
-#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
{
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
-#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 <string.h>
-
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)
/* 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; i<count; i++ )
- audio_clip_load( &arr[i], stack );
+ u32 succeeded = 0;
+ for( u32 i=0; i<count; i++ )
+ succeeded += (u32)vg_audio_clip_load( &arr[i], stack );
+ return succeeded;
}
/*
static void audio_channel_get_samples( audio_channel_id id, struct audio_channel_controls *controls,
u32 count, f32 *out_stereo )
{
- vg_profile_begin( &_vg_prof_audio_decode );
+ _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:decode" );
u32 remaining = count;
u32 buffer_pos = 0;
remaining --;
}
- vg_profile_end( &_vg_prof_audio_decode );
+ _vg_profiler_exit_block( _vg_audio.profiler );
}
static f32 audio_lfo_get_sample( audio_channel_id lfo_id, struct audio_lfo_controls *controls )
f32 samples[ AUDIO_MIX_FRAME_SIZE*2 * 2 ];
audio_channel_get_samples( id, controls, buffer_length, samples );
- vg_profile_begin( &_vg_prof_audio_mix );
+ _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:mixing" );
/* TODO: Slew this */
f64 master_volume = (f64)_vg_audio.state.volume / (f64)AUDIO_VOLUME_100;
inout_buffer[ j*2+1 ] += s_r * v_r;
}
- vg_profile_end( &_vg_prof_audio_mix );
+ _vg_profiler_exit_block( _vg_audio.profiler );
}
static void _vg_audio_mixer( void *user, u8 *stream, int byte_count )
{
+ _vg_profiler_tick( _vg_audio.profiler );
+
int sample_count = byte_count/(2*sizeof(f32));
f32 *output_stereo = (f32 *)stream;
{
if( !dry_layer )
{
- vg_profile_begin( &_vg_prof_dsp );
+ _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:dsp/effects" );
for( int i=0; i<sample_count; i++ )
vg_dsp_process( output_stereo + i*2, output_stereo + i*2 );
- vg_profile_end( &_vg_prof_dsp );
+ _vg_profiler_exit_block( _vg_audio.profiler );
}
}
else break;
}
}
- /* Profiling information
- * ----------------------------------------------- */
- vg_profile_increment( &_vg_prof_audio_decode );
- vg_profile_increment( &_vg_prof_audio_mix );
- vg_profile_increment( &_vg_prof_dsp );
-
- if( _vg_audio.inspector_open )
- {
- _vg_prof_audio_mix_ui = _vg_prof_audio_mix;
- _vg_prof_audio_decode_ui = _vg_prof_audio_decode;
- _vg_prof_audio_dsp_ui = _vg_prof_dsp;
- }
-
_vg_audio.samples_written_last_audio_frame = sample_count;
vg_audio_unlock();
}
char buf[256];
vg_str str;
vg_strnull( &str, buf, sizeof(buf) );
- vg_strcati32r( &str, id, 2, ' ' );
+ vg_strcati64r( &str, id, 10, 2, ' ' );
if( channel->group )
{
vg_strcat( &str, " grp" );
- vg_strcati32r( &str, channel->group, 6, ' ' );
+ vg_strcati64r( &str, channel->group, 10, 6, ' ' );
}
else
vg_strcat( &str, " " );
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, " " );
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 );
}
}
-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;
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)
-/* 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
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;
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);
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
-#include "vg_audio_dsp.h"
-#include "vg_mem.h"
-
struct vg_dsp vg_dsp;
float *dsp_allocate( u32 samples )
#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 );
-#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;
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 );
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
-#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
{
u32 total = 0;
- for( int i=0; i<bird->settings.pattern_length; i ++ ){
- struct synth_bird_signature *sig = &bird->settings.pattern[i];
-
+ for( int i=0; i<bird->settings->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;
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);
}
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;
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 )
{
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;
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] );
}
#ifdef SYNTH_BIRD_STDLIB
-#include "stdio.h"
-
#define KNRM "\x1B[00m"
#define KRED "\x1B[31m"
#define KGRN "\x1B[32m"
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_audio_synth_bird.c"
+#else
#ifndef BIRD_SAMPLE_RATE
#define BIRD_SAMPLE_RATE 44100
struct synth_bird
{
- struct{
+ struct
+ {
int osc_main[4],
osc_lfo;
float volume[4];
}
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 */
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
-#include "vg/vg_binstr.h"
-
void vg_str_bin( const void *txt, void *bin, int size )
{
const u8 *src = txt;
-#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
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <time.h>
-#include <stdarg.h>
-
-#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
{
}
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
};
* 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"
* OS & file tools
* -------------------------------------------------------------------------- */
-void vg_syscall( const char *fmt, ... )
+void vg_syscall( const c8 *fmt, ... )
{
va_list args;
va_start( args, 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 );
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 );
}
/*
* 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 )
{
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 */
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"
);
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" );
log_source_info,
steam_api,
custom_game_settings,
- custom_shaders,
- multiplayer;
+ multiplayer,
+ release_mode;
i32 fixed_update_hz;
}
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
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 );
/* 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" );
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};
//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 )
{
}
/* 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 )
{
{
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
-#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);
u32 buff = 0;
for( int b = 31; b >= 0; b-- )
{
- buff |= data[pixel*4]>128?0x1<<b:0;
+ buff |= (data[pixel*4] > 128)? (0x1u<<(u32)b): 0;
pixel++;
if( pixel >= pixel_max )
fclose( fp );
}
+#endif
-#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; j<VG_ARRAY_LEN(glsl_defs); j ++ )
+ {
+ if( !strncmp( full+t_start, glsl_defs[j][0], t_length ) )
+ {
+ state = k_name;
+ type_index = j;
+ break;
+ }
+ }
+
+ if( type_index == 9999 )
+ {
+ /* TODO: Yes, this is scuffed, un-conformant parsering. But it's okay, we'll be alright. */
+ if( c=='\n' )
+ {
+ vg_strcat( &_shadercomp.c_enum, " k_uniform_block_" );
+ vg_strcat( &_shadercomp.c_enum, shader_name );
+ vg_strcat( &_shadercomp.c_enum, "_" );
+ vg_strcat_limit( &_shadercomp.c_enum, full+t_start, t_length );
+ vg_strcat( &_shadercomp.c_enum, " = " );
+ vg_strcatu64( &_shadercomp.c_enum, _shadercomp.name_count, 10 );
+ vg_strcat( &_shadercomp.c_enum, ",\n" );
+
+ _shadercomp.name_count ++;
+ vg_strcat( &_shadercomp.names.str, "$" );
+ vg_strcat_limit( &_shadercomp.names.str, full+t_start, t_length );
+ vg_strcat( &_shadercomp.names.str, "\\0" );
+ _shadercomp.names.ghost_count ++;
+
+ }
+ state = k_nothing;
+ }
}
-
- vg_strcatf( str, "\\n\"\n" );
- start = cur+1;
+ else if( state == k_name )
+ {
+ bool is_array = c == '[';
+ vg_strcat( &_shadercomp.c_funcs, "static inline void shader_" );
+ vg_strcat( &_shadercomp.c_funcs, shader_name );
+ vg_strcat( &_shadercomp.c_funcs, "_" );
+ vg_strcat_limit( &_shadercomp.c_funcs, full+t_start, t_length );
+ vg_strcat( &_shadercomp.c_funcs, "(" );
+ vg_strcat( &_shadercomp.c_funcs, glsl_defs[type_index][is_array? 4: 1] );
+ vg_strcat( &_shadercomp.c_funcs, ") { " );
+ vg_strcat( &_shadercomp.c_funcs, glsl_defs[type_index][2] );
+ vg_strcat( &_shadercomp.c_funcs, "_vg_shader_names[" );
+ vg_strcatu64( &_shadercomp.c_funcs, _shadercomp.name_count, 10 );
+ vg_strcat( &_shadercomp.c_funcs, "] " );
+ vg_strcat( &_shadercomp.c_funcs, glsl_defs[type_index][is_array? 5: 3] );
+ vg_strcat( &_shadercomp.c_funcs, "}\n" );
+
+ vg_strcat( &_shadercomp.c_enum, " k_uniform_" );
+ vg_strcat( &_shadercomp.c_enum, shader_name );
+ vg_strcat( &_shadercomp.c_enum, "_" );
+ vg_strcat_limit( &_shadercomp.c_enum, full+t_start, t_length );
+ vg_strcat( &_shadercomp.c_enum, " = " );
+ vg_strcatu64( &_shadercomp.c_enum, _shadercomp.name_count, 10 );
+ vg_strcat( &_shadercomp.c_enum, ",\n" );
+
+ _shadercomp.name_count ++;
+ _shadercomp_pstr( &_shadercomp.names, NULL, full+t_start, t_length );
+
+ state = k_nothing;
+ }
+ t_length = 0;
+ }
+ }
+ else
+ {
+ if( t_length )
+ t_length ++;
+ else
+ {
+ t_length = 1;
+ t_start = i;
}
- cur ++;
}
-
- vg_strcatf( str, "}," );
}
+ vg_strcat( &_shadercomp.glsl.str, "\\0\"\n" );
+ _shadercomp.glsl.ghost_count += 3; //TODO these are kind of ugly, we should have a 'propper' C source builder.
free( full );
return 1;
}
-void vg_build_shader_impl( char *path )
-{
- FILE *fp = fopen( path, "w" );
-
- if( vg_shaderbuild.code_function_body.buffer )
- fputs( vg_shaderbuild.code_function_body.buffer, fp );
- fputs( "\n\n", fp );
-
- fputs( "void vg_auto_shader_link(void)\n{\n", fp );
- if( vg_shaderbuild.code_link.buffer )
- fputs( vg_shaderbuild.code_link.buffer, fp );
- fputs( "}\n\n", fp );
-
- fputs( "void vg_auto_shader_register(void)\n{\n", fp );
- if( vg_shaderbuild.code_register.buffer )
- fputs( vg_shaderbuild.code_register.buffer, fp );
- fputs( "}\n\n", fp );
-
- fclose( fp );
-}
-
-#define _S( NAME, VS, FS ) \
- vg_build_shader( "shaders/" VS, "shaders/" FS, NULL, "shaders", NAME )
int vg_build_shader( char *src_vert, /* path/to/vert.vs */
char *src_frag, /* path/to/frag.fs */
char *src_geo, /* unused currently */
- char *dst_h, /* folder where .h go */
char *name /* shader name */ )
{
- if( !vg_shaderbuild.init )
+ if( !_shadercomp.init )
{
- vg_strnull( &vg_shaderbuild.code_link, NULL, 0 );
- vg_strnull( &vg_shaderbuild.code_register, NULL, 0 );
- vg_strnull( &vg_shaderbuild.code_function_body, NULL, 0 );
- vg_shaderbuild.init = 1;
+ vg_strnull( &_shadercomp.c_structs, NULL, 0 );
+ vg_strnull( &_shadercomp.names.str, NULL, 0 );
+ vg_strnull( &_shadercomp.infos.str, NULL, 0 );
+ vg_strnull( &_shadercomp.c_funcs, NULL, 0 );
+ vg_strnull( &_shadercomp.c_enum, NULL, 0 );
+ vg_strnull( &_shadercomp.glsl.str, NULL, 0 );
+ _shadercomp.init = 1;
}
- vg_str *c_link = &vg_shaderbuild.code_link,
- *c_reg = &vg_shaderbuild.code_register,
- *c_body = &vg_shaderbuild.code_function_body;
+ vg_low( "Compiling shader '%s'\n", name );
- char path_header[260];
+ u32 u_start = _shadercomp.name_count,
+ u_offset = _shadercomp.names.str.i - _shadercomp.names.ghost_count;
- strcpy( vg_shaderbuild.current_shader_name, name );
- strcpy( path_header, dst_h );
- strcat( path_header, "/" );
- strcat( path_header, name );
- strcat( path_header, ".h" );
-
- vg_low( "Compiling shader called '%s'\r", name );
-
- FILE *header = fopen( path_header, "w" );
- if( !header )
- vg_fatal_error( "Could not open '%s'\n", path_header );
-
- fprintf( header, "#pragma once\n" );
- fprintf( header, "#include \"vg/vg_engine.h\"\n" );
- vg_strcatf( c_body, "#include \"%s\"\n", path_header );
- fprintf( header, "extern struct vg_shader _shader_%s;\n", name );
- vg_strcatf( c_body, "struct vg_shader _shader_%s = {\n"
- " .name = \"%s\",\n"
- " .vs = \n", name, name, name );
-
- char pcpath[ 260 ];
- if( vg_shaderbuild.preprocessed_dir )
- snprintf( pcpath, sizeof(pcpath), "%s/%s.vert", vg_shaderbuild.preprocessed_dir, name );
-
- vg_shaderbuild.uniform_count = 0;
- if( !compile_subshader( c_body, src_vert, vg_shaderbuild.preprocessed_dir? pcpath: NULL ) )
- {
- fclose( header );
- vg_fatal_error( "Failed to assemble vertex source code" );
- }
-
- if( vg_shaderbuild.preprocessed_dir )
- snprintf( pcpath, sizeof(pcpath), "%s/%s.frag", vg_shaderbuild.preprocessed_dir, name );
-
- vg_strcatf( c_body, "\n .fs = \n" );
- if( !compile_subshader( c_body, src_frag, vg_shaderbuild.preprocessed_dir? pcpath: NULL ) )
- {
- fclose( header );
- vg_fatal_error( "Failed to assemble fragment source code" );
- }
-
- vg_strcatf( c_body, "\n};\n\n" );
+ /* [shader] _use() */
+ vg_strcat( &_shadercomp.c_enum, " k_shader_" );
+ vg_strcat( &_shadercomp.c_enum, name );
+ vg_strcat( &_shadercomp.c_enum, " = " );
+ vg_strcatu64( &_shadercomp.c_enum, _shadercomp.name_count, 10 );
+ vg_strcat( &_shadercomp.c_enum, ",\n" );
+
+ vg_strcat( &_shadercomp.c_funcs, "static inline void shader_" );
+ vg_strcat( &_shadercomp.c_funcs, name );
+ vg_strcat( &_shadercomp.c_funcs, "_use(void){ glUseProgram( _vg_shader_names[" );
+ vg_strcatu64( &_shadercomp.c_funcs, _shadercomp.name_count, 10 );
+ vg_strcat( &_shadercomp.c_funcs, "] ); }\n" );
+ _shadercomp.name_count ++;
- for( int i=0; i<vg_shaderbuild.uniform_count; i++ )
- {
- struct uniform *uf = &vg_shaderbuild.uniform_buffer[i];
- fprintf( header, "extern GLuint %s;\n", uf->uniform_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; i<vg_shaderbuild.uniform_count; i++ )
- {
- struct uniform *uf = &vg_shaderbuild.uniform_buffer[i];
- if( uf->array )
- 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; j<VG_ARRAY_LEN(types); j ++ )
- {
- struct type_info *inf = &types[j];
+static void vg_shader_set_include_dir( char *dir )
+{
+ strcpy( _shadercomp.shader_dir, dir );
+}
- if( !strcmp( inf->glsl_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; i<vg_shaderbuild.uniform_count; i++ )
+ FILE *fp = fopen( h_path, "w" );
+ if( fp )
{
- struct uniform *uf = &vg_shaderbuild.uniform_buffer[i];
- vg_strcatf( c_link,
- " _uniform_%s_%s = "
- "glGetUniformLocation( _shader_%s.id, \"%s\" );\n",
- name, uf->name,
- 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
-#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; i<node->count; i++ ){
+ for( u32 i=0; i<node->count; 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;
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;
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;
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 );
/*
* 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;
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;
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 */
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;
}
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; i<inode->count; i++ ){
+ f32 node_dist = v3_dist2( pos, p1 );
+ if( node_dist < max_dist )
+ {
+ if( inode->count )
+ {
+ for( i32 i=0; i<inode->count; 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;
depth --;
}
- else{
+ else
+ {
queue[depth] = inode->il;
queue[depth+1] = inode->ir;
-
depth ++;
}
}
-#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:
* 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;
}
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
-#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 );
-#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
* 4) [projection matrix, view matrix] -> previous pv, new pv
*/
void vg_camera_finalize( vg_camera *cam );
+
+#endif
-#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 <string.h>
-
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) );
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) );
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[] )
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 )
{
fclose( fp );
}
-
-#ifdef VG_ENGINE
-static void _vg_console_free(void)
-{
- vg_console_write_persistent();
-}
#endif
/*
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<m; i++ ){
- costs[0] = i+1;
-
- u32 corner = i;
-
- for( u32 j=0; j<n; j++ ){
- u32 upper = costs[j+1];
-
- if( s1[i] == s2[j] )
- costs[ j+1 ] = corner;
- else{
- u32 t = (upper < corner)? upper: corner;
- costs[j+1] = ((costs[j] < t)? costs[j]: t) + 1;
- }
-
- corner = upper;
- }
- }
-
- return costs[n];
-}
-
-u32 str_lcs( const char *s1, const char *s2 )
-{
- u32 m = VG_MIN( 31, strlen( s1 ) ),
- n = VG_MIN( 31, strlen( s2 ) );
-
- int suff[32][32],
- result = 0;
-
- for( int i=0; i<=m; i++ )
- {
- for( int j=0; j<=n; j++ )
- {
- if( i == 0 || j == 0 )
- suff[i][j] = 0;
- else if( s1[i-1] == s2[j-1] )
- {
- suff[i][j] = suff[i-1][j-1] + 1;
- result = VG_MAX( result, suff[i][j] );
- }
- else
- suff[i][j] = 0;
- }
- }
-
- return result;
-}
-
-/* str must not fuckoff ever! */
-void console_suggest_score_text( const char *str, const char *input, int minscore )
-{
- if( !str )
- return;
-
- /* filter duplicates */
- for( int i=0; i<vg_console.suggestion_count; i++ )
- if( !strcmp( vg_console.suggestions[i].str, str ) )
- return;
-
- /* calc score */
- u32 score = str_lcs( str, input );
-
- if( score < minscore )
- return;
-
- int best_pos = vg_console.suggestion_count;
- for( int j=best_pos-1; j>=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; i<vg_console.var_count; i++ )
- {
- vg_var *cvar = &vg_console.vars[i];
- console_suggest_score_text( cvar->name, args[0], 1 );
- }
-
- for( int i=0; i<vg_console.function_count; i++ )
- {
- vg_cmd *cmd = &vg_console.functions[i];
- console_suggest_score_text( cmd->name, 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; i<vg_console.suggestion_count; i++ )
- {
- vg_console.suggestion_maxlen = VG_MAX( vg_console.suggestion_maxlen,
- vg_console.suggestions[i].len );
-
- if( vg_console.suggestions[i].lev_score <
- vg_console.suggestions[0].lev_score/2 )
- {
- vg_console.suggestion_count = i;
- return;
- }
- }
-}
-
-/*
- * Suggestion controls
- */
-static void _console_fetch_suggestion( ui_context *ctx )
-{
- char *target = &vg_console.input[ vg_console.suggestion_pastepos ];
-
- if( vg_console.suggestion_select == -1 )
- {
- strcpy( target, vg_console.input_copy );
- _ui_textbox_move_cursor( ctx,
- &ctx->textbox.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 )
}
-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; i<console_lines; i ++ )
- {
- ptr --;
-
- if( ptr < 0 ) ptr = VG_ARRAY_LEN( vg_log.log )-1;
-
- ui_text( ctx, rect_line, vg_log.log[ptr], 1, k_ui_align_left, 0 );
- rect_line[1] -= fh;
- }
-
- /*
- * Input area
- */
- struct ui_textbox_callbacks callbacks =
- {
- .up = _vg_console_on_up,
- .down = _vg_console_on_down,
- .change = _vg_console_on_update,
- .enter = _vg_console_on_enter,
- };
-
- u32 flags = 0;
- if( vg_console.auto_focus ) flags |= UI_TEXTBOX_AUTOFOCUS;
- ui_textbox( ctx, rect_input, NULL,
- vg_console.input, VG_ARRAY_LEN(vg_console.input), 1,
- flags, &callbacks );
- vg_console.auto_focus = 0;
-
- /*
- * suggestions
- */
- if( vg_console.suggestion_count ){
- ui_rect rect_suggest;
- rect_copy( rect_input, rect_suggest );
-
- rect_suggest[0] += 6 + ctx->font->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<vg_console.suggestion_count; i ++ )
- {
- u32 text_colour;
- if( i == vg_console.suggestion_select )
- {
- ui_fill( ctx, rect_suggest, ui_colour( ctx, k_ui_orange ) );
- text_colour = ui_colourcont( ctx, k_ui_orange );
- }
- else text_colour = ui_colourcont( ctx, k_ui_bg );
-
- ui_text( ctx, rect_suggest, vg_console.suggestions[i].str, 1, k_ui_align_left, text_colour );
-
- rect_suggest[1] += fh;
- }
- }
-
- if( SDL_UnlockMutex( vg_log.mutex ) )
- vg_fatal_error( "" );
-}
+ _vg_add_exit_function( vg_console_write_persistent );
#endif
+}
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#pragma once
-#include "vg_platform.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_console.c"
+#else
#define VG_VAR_F32( NAME, ... ) \
{ u32 flags=0x00; __VA_ARGS__ ;\
}
functions[ 32 ];
-#ifdef VG_ENGINE
+#if defined( VG_ENGINE )
struct {
const char *str;
int len;
u32 var_count, function_count;
-#ifdef VG_ENGINE
+#if defined( VG_ENGINE )
char input[96],
input_copy[96];
char history[32][96];
}
extern vg_console;
+VG_API void _vg_console_register(void);
+VG_API void _vg_console_init(void);
+
void vg_console_reg_var( const char *alias, void *ptr, enum vg_var_dtype type, u32 flags );
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
-void vg_console_load_autos(void);
-void vg_console_draw( ui_context *ctx );
-static void vg_console_write_persistent(void);
-void console_suggest_score_text( const char *str, const char *input, int minscore );
-void console_suggest_next( ui_context *ctx );
-void console_suggest_prev( ui_context *ctx );
-#endif
-
-void vg_console_init(void);
void vg_execute_console_input( const char *cmd, bool silent, bool cheat_override );
+
+#endif
-#include "vg_db.h"
-#include <stddef.h>
-#include <stdarg.h>
-
static void vg_db_touch( vg_db *db, u16 cache_id );
/* util
-#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<<VG_PAGE_BITS)
void vg_db_skipper_unplace( vg_db *db, vg_skipper_context *ctx, u16 item_index, void *comparand );
void vg_db_skipper_iter_start( vg_db *db, vg_skipper_context *ctx );
bool vg_db_skipper_iter( vg_db *db, vg_skipper_context *ctx, u16 *out_index );
+
+#endif
-#include "vg_engine.h"
-#include "vg_async2.h"
-#include "vg_steam2.h"
-
struct vg_engine vg =
{
.time_rate = 1.0,
.time_fixed_delta = VG_TIMESTEP_FIXED,
- .main_tasks =
- {
- .buffer_size = 20*1024*1024
- },
- .loader_tasks =
+ .thread_tasks = { [0] = { .buffer_size = VG_MB(20) },
+ [1] = { .buffer_size = VG_MB(20) } },
+ .exit_tasks = { .buffer_size = VG_KB(16) },
+
+ .thread_contexts =
{
- .buffer_size = 20*1024*1024
+ [0] = { .log_prefix = KMAG "1" },
+ [1] = { .log_prefix = KCYN "2" },
+ [2] = { .log_prefix = KGRN "3" }
}
};
-u32 _thread_purpose_main = 0x01;
-u32 _thread_purpose_loader = 0x01;
+VG_API bool _vg_thread_has_flags( u32 flags )
+{
+ struct vg_thread_context *context = SDL_TLSGet( vg.thread_tls );
+ if( context )
+ return (context->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 <string.h>
-#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 <windows.h>
+#include <processthreadsapi.h>
+#endif
+
+#include <signal.h>
+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;
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
{
vg_console.enabled = 1;
}
else
- {
vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym );
- }
}
}
else if( event.type == SDL_MOUSEWHEEL )
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 )
{
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 <windows.h>
-#include <processthreadsapi.h>
-#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 <signal.h>
-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<len; i ++ )
- {
- if( ((buf[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; i<vg_settings.audio_devices.option_count; i ++ )
- {
- oi = &vg_settings.audio_devices.options[i];
-
- if( oi->value == 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; i<count; i ++ ){
- struct ui_enum_opt *oi = &options[i+1];
-
- const char *device_name = SDL_GetAudioDeviceName( i, 0 );
- int len = strlen(device_name);
-
- oi->alias = 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; i<count; i ++ )
- {
- struct ui_enum_opt *oi = &options[i+1];
- if( !strcmp( oi->alias, _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
-#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;
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
{
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
-#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;
i16 w, h;
u32 bitmap[];
};
-
+#endif
-#include "vg_framebuffer.h"
-#include "vg_platform.h"
-
struct
{
vg_framebuffer *list[16];
}
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
{
}
}
-void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse )
+VG_API void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse )
{
if( fb )
{
}
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 );
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];
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 );
}
/*
/*
* 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 );
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;
* Check result
*/
GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER );
-
if( result == GL_FRAMEBUFFER_COMPLETE )
{
vg_success( " status: complete\n" );
}
}
-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 );
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++ )
{
}
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;
frame[0] += w;
- if( (frame[0] + w) > vg.window_x )
+ if( (frame[0] + w) > _vg_window.w )
{
frame[0] = 0;
frame[1] += h;
}
}
-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 );
}
/*
}
}
-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];
-/*
- * 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,
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
* 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
-/* 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 = {
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; i<VG_MAX_CONTROLLERS; i++ )
+ {
+ struct vg_controller *controller = &vg_input.controllers[i];
+
+ if( controller->handle )
+ {
+ 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" );
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; i<VG_MAX_CONTROLLERS; i++ )
- {
- struct vg_controller *controller = &vg_input.controllers[i];
-
- if( controller->handle )
- {
- SDL_GameControllerClose( controller->handle );
- controller->handle = NULL;
- }
- }
+ _vg_add_exit_function( vg_input_free );
}
struct vg_controller *vg_active_controller(void)
vg_strcat( str, special_glyphs? "\x96 ": "down" );
else {
vg_strcat( str, "keyboard key #" );
- vg_strcati32( str, key );
+ vg_strcati64( str, key, 10 );
}
}
vg_strcat( str, special_glyphs? "\x9c": "middle mouse" );
else{
vg_strcat( str, "mouse button #" );
- vg_strcati32( str, button );
+ vg_strcati64( str, button, 10 );
}
}
vg_strcat( str, special_glyphs?"\x8d":"right stick vertical" );
else{
vg_strcat( str, "axis " );
- vg_strcati32( str, axis );
+ vg_strcati64( str, axis, 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;
}
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
-/* 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 <string.h>
-#include <errno.h>
-
const char *dir_open_result_str[] =
{
[k_dir_open_none] = "None",
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; i<read_length; i ++ )
+ ((u8 *)buffer)[i] = ((u8 *)stream->buffer)[stream->byte_count + i];
}
+
+ for( u32 i=read_length; i<length; i ++ )
+ ((u8 *)buffer)[ i ] = 0;
+
+ stream->byte_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; i<write_length; i ++ )
+ ((u8 *)stream->buffer)[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;
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;
-/* 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 <dirent.h>
- #include <sys/stat.h>
- #include <unistd.h>
- struct vg_dir{
- DIR *h;
- struct dirent *data;
- u32 index;
- };
-#else
- #include <windows.h>
- #include <fileapi.h>
- 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
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
-#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;
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;
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 );
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; i<buffer_length; i ++ )
+ c8 c;
+ while( vg_stream_read( in_stream, &c, 1 ) )
{
parser->stat_source_characters ++;
- char c = buffer[i];
if( c == '\0' )
break;
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 )
{
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;
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 );
}
}
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; i<key_length; i ++ )
fputc( ' ', w->fp );
}
-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;
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 )
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 );
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 );
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 );
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; i<len; i ++ )
+ {
+ u64 value = 0;
+ if( vg_strp_u64( &s, &value ) == k_vg_strp_ok ) out_value[ i ] = (u32)value;
+ else
+ {
+ good = 0;
+ if( default_value ) out_value[ i ] = default_value[ i ];
+ else out_value[ i ] = 0;
+ }
+ }
+ return good;
+}
+
+bool vg_kv_read_vf32( vg_kvs *kvs, u32 root_offset, const c8 *key, f32 *default_value, f32 *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; i<len; i ++ )
+ {
+ f64 value = 0.0;
+ if( vg_strp_f64( &s, &value ) == k_vg_strp_ok ) out_value[ i ] = (f32)value;
+ else
+ {
+ good = 0;
+ if( default_value ) out_value[ i ] = default_value[ i ];
+ else out_value[ i ] = 0.0f;
+ }
+ }
+ return good;
+}
+
+u32 vg_kv_append_vu32( vg_kvs *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len )
+{
+ c8 formatted[ 1024 ];
+ vg_str value_str;
+ vg_strnull( &value_str, formatted, sizeof(formatted) );
+
+ for( u32 i=0; i<len; i++ )
+ {
+ vg_strcatu64( &value_str, values[i], 10 );
+ if( i+1!=len )
+ vg_strcatch( &value_str, ' ' );
+ }
+
+ if( !vg_strgood( &value_str ) )
+ return 0;
+
+ return vg_kv_append( kvs, parent_offset, key, formatted );
+}
+
+u32 vg_kv_append_vf32( vg_kvs *kvs, u32 parent_offset, const c8 *key, f32 *values, u32 len )
+{
+ c8 formatted[ 1024 ];
+ vg_str value_str;
+ vg_strnull( &value_str, formatted, sizeof(formatted) );
+
+ for( u32 i=0; i<len; i++ )
+ {
+ vg_strcatf64( &value_str, values[i], 10, 5 );
+ if( i+1!=len )
+ vg_strcatch( &value_str, ' ' );
+ }
+
+ if( !vg_strgood( &value_str ) )
+ return 0;
+
+ return vg_kv_append( kvs, parent_offset, key, formatted );
+}
-#pragma once
-#include "vg_platform.h"
-#include "vg_mem.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_kv.c"
+#else
#define VG_KV_PAGE_COUNT 32
/* Initialize KV parser ready to accept buffer fragments
* out_kvs must be initialized
- *
- * vg_kv_parse_buffer takes a text buffer, if its null terminated, buffer_length can be 0
*/
void vg_kv_parser_init( vg_kv_parser *parser, vg_kvs *out_kvs, u32 root_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 );
void vg_kv_link( vg_kv_parser *parser, u32 offset, u32 depth );
/* returns the type of this KV.
u32 vg_kv_type( vg_kvs *kvs, u32 kv_offset );
/* get key / values associated with KV pair or only key for a frame. */
-c8 *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length );
-c8 *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length );
+const c8 *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length );
+const c8 *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length );
/* get the child KV at index, returns 0 if out of range */
u32 vg_kv_child( vg_kvs *kvs, u32 root_offset, u32 index );
u32 vg_kv_next( vg_kvs *kvs, u32 kv_offset );
-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 );
+
+bool vg_kv_read_vu32( vg_kvs *kvs, u32 root_offset, const c8 *key, u32 *default_value, u32 *out_value, u32 len );
+bool vg_kv_read_vf32( vg_kvs *kvs, u32 root_offset, const c8 *key, f32 *default_value, f32 *out_value, u32 len );
/* editing kvs
* if value is NULL, it appends a named frame
* if both key and value are set, it appends a KV pair
* returns the new KV offset
*/
-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 );
+u32 vg_kv_append_vu32( vg_kvs *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len );
+u32 vg_kv_append_vf32( vg_kvs *kvs, u32 parent_offset, const c8 *key, f32 *values, u32 len );
/* Writing KV files. w should be initialized with depth 0, and fp to a valid C stream pointer */
-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 );
void vg_kv_end_block( vg_kv_write *w );
-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 );
/* Just for analysis */
void vg_kv_parser_print_info( vg_kv_parser *parser );
FILE *fp;
u32 depth;
};
+
+#endif
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#pragma once
-#include "vg_lines.h"
-#include "vg_shader.h"
-#include "vg_engine.h"
-
struct vg_lines vg_lines;
-/*
- * FIXME: The line buffer sometimes overflows. Low priority
- */
-
-static struct vg_shader _shader_lines = {
- .name = "[vg] lines",
- .vs = {
- .orig_file = NULL,
- .static_src =
-
- "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;"
- "}"
- },
- .fs = {
- .orig_file = NULL,
- .static_src =
-
- "out vec4 FragColor;"
- ""
- "in vec4 s_colour;"
- ""
- "void main()"
- "{"
- " FragColor = s_colour;"
- "}"
- }
-};
-
#define VG_LINES_MAX_VERTS 50000
-static void async_vg_lines_init( void *_ )
+VG_API void _vg_lines_register(void)
+{
+ vg_console_reg_var( "vg_lines", &vg_lines.render, k_var_dtype_i32, VG_VAR_CHEAT );
+}
+
+VG_API void _vg_lines_init(void)
{
- THREAD_0;
+ VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
+ vg_lines.vertex_buffer = vg_malloc( VG_LINES_MAX_VERTS*sizeof(struct vg_lines_vert) );
glGenVertexArrays( 1, &vg_lines.vao );
glGenBuffers( 1, &vg_lines.vbo );
glBindVertexArray( vg_lines.vao );
glEnableVertexAttribArray( 1 );
}
-void vg_lines_register(void)
-{
- vg_console_reg_var( "vg_lines", &vg_lines.render, k_var_dtype_i32, VG_VAR_CHEAT );
-}
-
-void vg_lines_init(void)
-{
- THREAD_1;
- vg_lines.vertex_buffer = vg_stack_allocate( &vg.rtmem, VG_LINES_MAX_VERTS*sizeof(struct vg_lines_vert), 8, "Debugging Lines" );
-
- vg_async_call( &vg.main_tasks, async_vg_lines_init, NULL );
- vg_shader_register( &_shader_lines );
-}
-
void vg_lines_drawall( void )
{
- glUseProgram( _shader_lines.id );
- glUniformMatrix4fv( glGetUniformLocation( _shader_lines.id, "uPv" ), 1, GL_FALSE, (f32 *)vg.pv );
+ shader_debug_lines_use();
+ shader_debug_lines_uPv( vg.pv );
glBindVertexArray( vg_lines.vao );
glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo );
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#pragma once
-#include "vg_platform.h"
-#include "vg_engine.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_lines.c"
+#else
typedef v3f line_co;
u32 enabled,
render;
+ vg_stack_allocator vertex_stack;
struct vg_lines_vert
{
v3f co;
}
extern vg_lines;
+VG_API void _vg_lines_register(void);
+VG_API void _vg_lines_init(void);
+
void vg_line_capsule( m4x3f m, float radius, float h, u32 colour );
void vg_line_sphere( m4x3f m, float radius, u32 colour );
void vg_line_point( v3f pt, float size, u32 colour );
void vg_line( line_co from, line_co to, u32 colour );
void vg_line2( line_co from, line_co to, u32 fc, u32 tc );
void vg_lines_drawall( void );
-void vg_lines_register(void);
-void vg_lines_init(void);
+
+#endif
-#include "vg_engine.h"
-#include "vg_loader.h"
-#include "vg_shader.h"
-#include "vg_async2.h"
-
struct vg_loader vg_loader;
-static struct vg_shader _shader_loader =
-{
- .name = "[vg] loader",
-
- /* This is the new foreground shader */
- .vs =
- {
- .orig_file = NULL,
- .static_src = ""
- "layout (location=0) in vec2 a_co;"
- "out vec2 aUv;"
- "void main()"
- "{"
- "gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);"
- "aUv = a_co;"
- "}"
- },
- .fs =
- {
- .orig_file = NULL,
- .static_src =
-
- "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);"
- "}"
- }
-};
-
-void vg_loader_init(void)
+VG_API void _vg_loader_init(void)
{
- float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
+ VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
+ f32 quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
glGenVertexArrays( 1, &vg_loader.vao );
glGenBuffers( 1, &vg_loader.vbo );
glBindBuffer( GL_ARRAY_BUFFER, vg_loader.vbo );
glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
glBindVertexArray( vg_loader.vao );
- glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(float)*2, (void*)0 );
+ glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(f32)*2, (void*)0 );
glEnableVertexAttribArray( 0 );
-
- vg_compile_shader( &_shader_loader );
-}
-
-void vg_loader_free(void)
-{
- glDeleteVertexArrays( 1, &vg_loader.vao );
- glDeleteBuffers( 1, &vg_loader.vbo );
-}
-
-void vg_loader_atexit(void)
-{
- for( int i=0; i<vg_loader.step_count; i++ )
- {
- struct loader_free_step *step = &vg_loader.step_buffer[vg_loader.step_count -1 -i];
- step->fn_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);
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;
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;
- }
-}
-
-
-/*
- * 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
}
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
-#include <stdarg.h>
-#include <string.h>
-#include <malloc.h>
-#include <time.h>
-#include "vg_platform.h"
-#include "vg_log.h"
-#include "vg_string.h"
-
-#ifdef VG_MULTITHREAD
- #ifndef VG_ENGINE
- #include <pthread.h>
- #endif
-#endif
-
struct vg_log vg_log;
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;
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,
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 );
}
#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<VG_ARRAY_LEN(buffer); i ++ )
{
{
if( location )
{
-#ifdef VG_MULTITHREAD
- #ifdef VG_ENGINE
- const char *thread_colours[] = { KGRN, KMAG, KCYN, KYEL, KBLU };
- const char *colour = thread_colours[(vg_thread_purpose() % VG_ARRAY_LEN( thread_colours ))];
- fprintf( file, "%s%u|"KNRM"%.32s", colour, vg_thread_purpose(), location );
- #else
+#if defined( VG_MULTITHREAD )
+# if defined( VG_ENGINE )
+ fprintf( file, "%s|"KNRM"%.32s", _vg_thread_prefix(), location );
+# else
fprintf( file, KNRM "%.32s", location );
- #endif
+# endif
#endif
}
_vg_log_append_line( line );
fputs( line, file );
- line_length = snprintf( line, 90, "%s " KNRM "%c%s ", colour, marker, colour );
+ line_length = snprintf( line, 90, "%s " KNRM "%c%s ", colour, marker, colour );
}
if( c == '\0' ) break;
if( buffer[i+1] == '\0' ) break;
}
-#ifdef VG_MULTITHREAD
- #ifdef VG_ENGINE
- if( SDL_UnlockMutex( vg_log.mutex ) )
- vg_fatal_error( "" );
- #else
- pthread_mutex_unlock( &vg_log.lock );
- #endif
-#endif
+ VG_MUTEX_UNLOCK( vg_log.lock );
}
void vg_logx( FILE *file,
va_list args;
va_start( args, fmt );
_vg_logx_va( file,
-#ifdef VG_LOG_SOURCE_INFO
+#if defined( VG_LOG_SOURCE_INFO )
location,
#else
NULL,
va_end( args );
}
-/* FIXME: THIS NEEDS ITS OWN FILE, VG_PLATFORM.C */
-
void vg_fatal_error( const char *fmt, ... )
{
va_list args;
vg_fatal_exit( "VG Assertion (check STDOUT, or the text logfile if enabled)" );
}
-#ifdef _WIN32
- #include <windows.h>
- #include <dbghelp.h>
-#else
- #include <execinfo.h>
-#endif
-#include <fcntl.h>
-#include <unistd.h>
-#include <time.h>
-
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;
backtrace_symbols_fd( functions, count, fd );
#endif
+#if defined( VG_RELEASE_MODE )
vg_strcat( &line, "\n\nVG Console log\n"
"-----------------------------------\n" );
}
close( fd );
+#endif
+
exit(-1);
}
-#pragma once
-
-#include "vg_platform.h"
-#include <stdio.h>
+#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
" "
#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"
#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];
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,
const char *location, const char *prefix,
const char *colour,
const char *fmt, va_list args );
+
+#endif
-/* 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
* 6.a Random numbers
*/
-#pragma once
-
-#include "vg_platform.h"
-#include <math.h>
-#include <stdlib.h>
-
#define VG_PIf 3.14159265358979323846264338327950288f
#define VG_TAUf 6.28318530717958647692528676655900576f
hsv[0] = vg_fractf( hsv[0] * (60.0f/360.0f) );
}
+
+#endif
-#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 )
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];
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 );
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 );
}
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_magi.c"
+#else
+
#define VG_MAGI_MAX_PANELS 8
#define VG_MAGI_RESIZEABLE 0x1
}
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
-#pragma once
-
-#include "vg_platform.h"
-#include "vg_log.h"
-#include "vg_mem.h"
-
-#include <stdlib.h>
-#include <malloc.h>
-#include <stdio.h>
-
void *vg_malloc( u64 size )
{
void *buf = malloc( size );
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 )
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;
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] )
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
-#pragma once
-#include "vg_platform.h"
-#include <stdalign.h>
-#include <stdio.h>
+#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 );
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
struct vg_stack_allocator
{
u32 capacity; /* bytes */
- u32 used;
- u32 last_allocation_totalsize;
+ u32 offset;
u16 flags;
- u16 unused0;
-
+ u16 unused0, unused1;
void *data;
};
*/
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
-#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 );
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<count; i ++ )
-#pragma once
-#include "vg_platform.h"
-#include "vg_mem.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_mem_pool.c"
+#else
/*
* This is most straightforwardly a way to maintain stable ID's for various other things, it does not directly manage
void vg_pool_init( vg_pool *pool, vg_pool_chain *chain, u16 count, vg_stack_allocator *stack );
u16 vg_pool_next( vg_pool *pool, u16 pool_id, bool right );
void vg_pool_switch( vg_pool *pool, vg_pool_chain *source, vg_pool_chain *dest, u16 which );
+
+#endif
-#include "vg_platform.h"
-#include "vg_mem.h"
-#include "vg_mem_queue.h"
-#include <stddef.h>
-#include <string.h>
-
void vg_queue_memcpy( vg_queue *q, void *dst, u32 start, u32 size )
{
if( start + size > q->size )
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 )
}
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;
}
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_mem_queue.c"
+#else
#define VG_MEM_QUEUE_INVALID 0xffffffff
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 );
u32 vg_queue_usage( vg_queue *q );
void vg_queue_clear( vg_queue *q );
+
+#endif
-#include "vg_ui/imgui.h"
-
struct mem_view_data
{
void *base_buffer;
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,
mv->route[ mv->route_depth ] = context.highlight_id;
}
}
+#endif
}
}
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 )
return 0;
}
+#if 0
for( u32 i=0; i<VG_ARRAY_LEN( _vg_mem_named_buffers ); i ++ )
{
if( !strcmp( _vg_mem_named_buffers[i].name, argv[0] ) )
mv->infos[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;
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:{
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;
}
return 1;
}
}
+#endif
vg_error( "No named buffer '%s'\n", argv[0] );
return 0;
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 );
}
-#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
-#include "vg_msg.h"
-#include "vg_platform.h"
-#include "vg_string.h"
-#include <string.h>
-#include <stdio.h>
-
-#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 )
{
}
}
-#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 )
{
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 )
{
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 )
{
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;
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 )
{
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
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
}
#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 )
{
#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 );
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 ) )
-#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 */
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 );
/*
* 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
-#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 <pthread.h>
-#include <semaphore.h>
-#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
-
-/*
- * 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 <stdlib.h>
-
/*
* Supported:
* short flags | -abc
-/*
- * 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);
/* Example: get regular_thing */
const char *vg_arg( u32 index );
+
+#endif
-#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,
-#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
-#pragma once
+#if !defined( VG_IMPLEMENTATION )
-#include <stdlib.h>
-#include <stdint.h>
+/*
+ * 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;
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 );
#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
-#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; i<PROFILER_ROW_MAX; i ++ )
+ {
+ profiler->history_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; i<profiler->row_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; i<count; i ++ )
- {
- avgs[i] = 0.0;
- colours[i] = ui_colour( ctx, k_ui_red + ((i*3)&0xe) );
- }
-
- f64 rate_mul = 1000.0 / (f64)SDL_GetPerformanceFrequency();
+ u32 colours[ PROFILER_ROW_MAX ];
+ for( u32 i=0; i<profiler->row_length; i ++ )
+ colours[i] = vg_strdjb2( profiler->row_names[i] ) | 0xff000000;
- for( i32 i=0; i<VG_PROFILE_SAMPLE_COUNT; i++ )
+ for( i32 i=0; i<PROFILER_HISTORY_LENGTH; i++ )
{
- f64 total = 0.0;
+ f32 total_ms = 0.0f;
- if( normalize )
+ for( u32 j=0; j<profiler->row_length; j ++ )
{
- budget = 0.0;
- for( u32 j=0; j<count; j++ )
- budget += (f64)profiles[j]->samples[i] * rate_mul;
- }
-
- for( int j=0; j<count; j++ )
- {
- f64 sample = (f64)profiles[j]->samples[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; i<count; i++ )
{
- snprintf( infbuf, 64, "%.4fms %s",
- avgs[i] * (1.0f/(VG_PROFILE_SAMPLE_COUNT-1)),
- profiles[i]->name );
-
- 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;
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 );
}
-#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
-#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
.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,
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)
.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)
.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)
.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
}
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);
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 );
}
-#pragma once
-#include "vg_engine.h"
-#include "vg_framebuffer.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_render.c"
+#else
struct vg_postprocess
{
{
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
-#include "vg_console.h"
-#include "vg_m.h"
-#include "vg_rigidbody.h"
-#include "vg_platform.h"
-#include "vg_engine.h"
-#include <math.h>
-
static float
k_limit_bias = 0.02f,
k_joint_correction = 0.01f,
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 );
-#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
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
*/
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
-#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];
-#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
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
-#pragma once
-#include "vg_rigidbody.h"
-#include "vg_rigidbody_constraints.h"
-#include "vg_m.h"
-#include "vg_lines.h"
-
/*
* -----------------------------------------------------------------------------
* Constraints
}
}
-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; i<len; i++ ){
rb_constr_swingtwist *st = &buf[i];
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_rigidbody_constraints.c"
+#else
typedef struct rb_constr_pos rb_constr_pos;
typedef struct rb_constr_swingtwist rb_constr_swingtwist;
* [ 0.0 <= amt <= 1.0 ]: the correction amount
*/
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 );
+
+#endif
-#pragma once
-#include "vg_platform.h"
-#include "vg_rigidbody.h"
-#include "vg_shader.h"
-#include "vg_engine.h"
-
-static struct vg_shader _shader_rigidbody =
-{
- .name = "[vg] rigidbody",
- .vs = {
- .orig_file = NULL,
- .static_src =
-
- "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;"
- "}"
- },
- .fs = {
- .orig_file = NULL,
- .static_src =
-
- "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;"
- "}"
- }
-};
-
#pragma pack(push,1)
struct rb_view_vert {
v4f co;
typedef struct rb_view_vert rb_view_vert;
-struct {
+struct
+{
GLuint vao, vbo, ebo;
u32 sphere_start, sphere_count,
box_start, box_count;
}
static vg_rb_view;
-struct vg_rb_mesh_init {
- u32 verts_size, tris_size;
- rb_view_vert *verts;
- u16 *tris;
-};
-
-static void async_vg_rb_view_init( vg_async_task *task )
+VG_API void _vg_rb_view_init(void)
{
- struct vg_rb_mesh_init *inf = (void *)task->data;
-
- 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,
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;
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 );
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<H; x ++ ){
+ for( u32 x=0; x<H; x ++ )
+ {
tris[tri_index*3+0] = base+0;
tris[tri_index*3+1] = base+1+x;
tris[tri_index*3+2] = base+1+((x+1)%H);
tri_index += 1;
}
- for( u32 y=1; y<V-1; y ++ ){
+ for( u32 y=1; y<V-1; y ++ )
+ {
f32 ty = ((f32)y/(f32)(V-1)) * VG_PIf;
u32 ybase = y-1;
- for( u32 x=0; x<H; x ++ ){
+ for( u32 x=0; x<H; x ++ )
+ {
f32 tx = ((f32)x/(f32)H) * VG_TAUf;
v4f co = { cosf(tx)*sinf(ty), -cosf(ty), sinf(tx)*sinf(ty), y>=(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);
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<H; x ++ ){
+ for( u32 x=0; x<H; x ++ )
+ {
tris[tri_index*3+0] = base + (H*(V-2) + 2)-1;
tris[tri_index*3+1] = base+1 + (V-3)*H+((x+1)%H);
tris[tri_index*3+2] = base+1 + (V-3)*H+x;
tri_index += 1;
}
- vg_async_task_dispatch( task, async_vg_rb_view_init );
+ 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, verts_count*sizeof(rb_view_vert), verts, GL_STATIC_DRAW );
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_rb_view.ebo );
+ glBufferData( GL_ELEMENT_ARRAY_BUFFER, tris_count*3*sizeof(u16), 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_bind(void)
glEnable( GL_CULL_FACE );
glEnable( GL_DEPTH_TEST );
- glUseProgram( _shader_rigidbody.id );
- glUniformMatrix4fv( glGetUniformLocation( _shader_rigidbody.id, "uPv" ),
- 1, GL_FALSE, (float *)vg.pv );
+ shader_debug_rigidbody_use();
+ shader_debug_rigidbody_uPv( vg.pv );
glBindVertexArray( vg_rb_view.vao );
}
v3_add( bbx[0], e, mmdl[3] );
m4x3_mul( mdl, mmdl, mmdl );
- glUniformMatrix4x3fv( glGetUniformLocation( _shader_rigidbody.id, "uMdl" ),
- 1,GL_FALSE,(float*)mmdl);
- glUniformMatrix4x3fv( glGetUniformLocation( _shader_rigidbody.id, "uMdl1" ),
- 1,GL_FALSE,(float*)mmdl);
- glUniform4fv( glGetUniformLocation( _shader_rigidbody.id, "uColour" ), 1,
- colour );
- glDrawElements( GL_TRIANGLES,
- vg_rb_view.box_count*3, GL_UNSIGNED_SHORT,
+ shader_debug_rigidbody_uMdl( mmdl );
+ shader_debug_rigidbody_uMdl1( mmdl );
+ shader_debug_rigidbody_uColour( colour );
+ glDrawElements( GL_TRIANGLES, vg_rb_view.box_count*3, GL_UNSIGNED_SHORT,
(void *)(vg_rb_view.box_start*3*sizeof(u16)) );
}
m4x3f mmdl;
m4x3_copy( mdl, mmdl );
m3x3_scalef( mmdl, r );
- glUniformMatrix4x3fv( glGetUniformLocation( _shader_rigidbody.id, "uMdl" ),
- 1,GL_FALSE,(float*)mmdl);
- glUniformMatrix4x3fv( glGetUniformLocation( _shader_rigidbody.id, "uMdl1" ),
- 1,GL_FALSE,(float*)mmdl);
- glUniform4fv( glGetUniformLocation( _shader_rigidbody.id, "uColour" ), 1,
- colour );
- glDrawElements( GL_TRIANGLES,
- vg_rb_view.sphere_count*3, GL_UNSIGNED_SHORT,
+
+ shader_debug_rigidbody_uMdl( mmdl );
+ shader_debug_rigidbody_uMdl1( mmdl );
+ shader_debug_rigidbody_uColour( colour );
+ glDrawElements( GL_TRIANGLES, vg_rb_view.sphere_count*3, GL_UNSIGNED_SHORT,
(void *)(vg_rb_view.sphere_start*3*sizeof(u16)) );
}
m4x3_mul( mdl, mmdl0, mmdl0 );
m4x3_mul( mdl, mmdl1, mmdl1 );
- glUniformMatrix4x3fv( glGetUniformLocation( _shader_rigidbody.id, "uMdl" ),
- 1,GL_FALSE,(float*)mmdl0);
- glUniformMatrix4x3fv( glGetUniformLocation( _shader_rigidbody.id, "uMdl1" ),
- 1,GL_FALSE,(float*)mmdl1);
- glUniform4fv( glGetUniformLocation( _shader_rigidbody.id, "uColour" ), 1,
- colour );
- glDrawElements( GL_TRIANGLES,
- vg_rb_view.sphere_count*3, GL_UNSIGNED_SHORT,
+ shader_debug_rigidbody_uMdl( mmdl0 );
+ shader_debug_rigidbody_uMdl1( mmdl1 );
+ shader_debug_rigidbody_uColour( colour );
+ glDrawElements( GL_TRIANGLES, vg_rb_view.sphere_count*3, GL_UNSIGNED_SHORT,
(void *)(vg_rb_view.sphere_start*3*sizeof(u16)) );
}
-#pragma once
-#include "vg_rigidbody.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_rigidbody_view.c"
+#else
+
+VG_API void _vg_rb_view_init(void);
-void vg_rb_view_init(void);
void vg_rb_view_bind(void);
void vg_rb_view_box( m4x3f mdl, boxf bbx, v4f colour );
void vg_rb_view_sphere( m4x3f mdl, f32 r, v4f colour );
void vg_rb_view_capsule( m4x3f mdl, f32 r, f32 h, v4f colour );
+
+#endif
--- /dev/null
+static struct ui_enum_opt vg_settings_vsync_enum[] =
+{
+ { 0, "None" },
+ { 1, "On" },
+ {-1, "Adaptive" },
+};
+
+static struct ui_enum_opt vg_settings_quality_enum[] =
+{
+ { 0, "High Quality" },
+ { 1, "Faster" },
+ { 2, "Absolute Minimum" },
+};
+
+static struct ui_enum_opt vg_settings_screen_mode_enum[] =
+{
+ { 0, "Fullscreen (desktop)" },
+ { 1, "Fullscreen (native)" },
+ { 2, "Floating Window" }
+};
+
+static 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;
+ bool open;
+}
+static vg_settings =
+{
+ .fps_limit = { .label = "Fps Limit",
+ .min=24, .max=300, .actual_value = &vg.fps_limit },
+ .vsync = { .label = "Vsync",
+ .actual_value = &_vg_window.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_window.display_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<len; i ++ )
+ {
+ if( ((buf[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_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; i<vg_settings.audio_devices.option_count; i ++ )
+ {
+ oi = &vg_settings.audio_devices.options[i];
+
+ if( oi->value == 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; i<count; i ++ )
+ {
+ struct ui_enum_opt *oi = &options[i+1];
+
+ const char *device_name = SDL_GetAudioDeviceName( i, 0 );
+ int len = strlen(device_name);
+
+ oi->alias = 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; i<count; i ++ )
+ {
+ struct ui_enum_opt *oi = &options[i+1];
+ if( !strcmp( oi->alias, _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;
+}
--- /dev/null
+#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
-/* 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;
}
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;
}
}
{
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 );
glDeleteShader( vert );
glDeleteShader( frag );
- shader->id = program;
+ _vg_shader_names[ shader->names_start ] = program;
shader->compiled = 1;
}
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 );
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
{
{
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; i<shader->uniform_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; i<shader->uniform_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; i<vg_shaders.count; i ++ )
- vg_compile_shader( vg_shaders.shaders[i] );
-
-#ifdef VG_CUSTOM_SHADERS
- vg_auto_shader_link();
-#endif
+ for( int i=0; i<VG_ARRAY_LEN( _vg_shaders ); i ++ )
+ {
+ vg_compile_shader( &_vg_shaders[i] );
+ vg_shader_link( &_vg_shaders[i] );
+ }
}
int vg_shaders_live_recompile( int argc, const char *argv[] )
{
vg_info( "Recompiling shaders\n" );
- for( int i=0; i<vg_shaders.count; i ++ )
+ for( int i=0; i<VG_ARRAY_LEN( _vg_shaders ); i ++ )
{
- struct vg_shader *shader = vg_shaders.shaders[i];
- vg_recompile_shader( shader );
+ vg_recompile_shader( &_vg_shaders[i] );
+ vg_shader_link( &_vg_shaders[i] );
}
-
-#ifdef VG_CUSTOM_SHADERS
- vg_auto_shader_link();
-#endif
return 0;
}
-void vg_shader_register( struct vg_shader *shader )
+VG_API void _vg_shaders_register(void)
{
- VG_ASSERT( vg_shaders.count < VG_ARRAY_LEN(vg_shaders.shaders) );
-
- shader->compiled = 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 );
}
-#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
-#include "vg_steam2.h"
-#include "vg_log.h"
-#include "vg_string.h"
-#include "vg_mem.h"
-#include "vg_io.h"
-#include <stdio.h>
-#include <string.h>
-
struct vg_steam_api _steam_api;
static void cb_steam_warning( i32 severity, const c8 *pchMessage )
#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 )
/* Steamworks init step
* ---------------------------------------------------------------------------- */
#if defined( VG_ENGINE )
+ VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) );
const char *pszInternalCheckInterfaceVersions =
STEAMUTILS_INTERFACE_VERSION "\0"
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 )
}
}
-void vg_steam_shutdown(void)
+VG_API void _vg_steam_shutdown(void)
{
#if defined( VG_SERVER )
if( _steam_api.is_connected )
-#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"
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
-#include "vg_string.h"
-#include "vg_platform.h"
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <unistd.h>
-#include "submodules/anyascii/impl/c/anyascii.c"
-
i32 vg_str_storage( vg_str *str )
{
if( str->len == 0 )
* 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 );
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;
{
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;
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;
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 --;
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' );
}
/*
- * 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; i<str->i; i++ ){
if( str->buffer[i] == c )
ptr = str->buffer+i;
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 )
{
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 );
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 )
{
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 );
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;
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; k<VG_MIN(chars, length-1-j); k++ )
+ const c8 *match;
+ size_t c8s = anyascii( utf32_code, &match );
+ for( u32 k=0; k<VG_MIN(c8s, length-1-j); k++ )
buf[ j++ ] = (u8)match[k];
}
}
[k_vg_strp_whitespace] = "whitespace"
};
-static vg_strp_info vg_strp_char( vg_strp *p, c8 *c )
+vg_strp_info vg_strp_c8( vg_strp *p, c8 *c )
{
*c = 0;
if( p->buffer == NULL )
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;
}
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;
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;
{
if( c == '-' )
sign = -1;
- info = vg_strp_char( p, &c );
+ info = vg_strp_c8( p, &c );
}
bool got = 0;
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;
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;
{
if( c == '-' )
sign = -1.0;
- info = vg_strp_char( p, &c );
+ info = vg_strp_c8( p, &c );
}
bool got = 0, got_decimal = 0;
}
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:;
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<max_characters) )
+ while( value && (i<max_c8acters) )
{
reverse_buffer[ i ++ ] = digits[ (u32)(value % base) ];
value /= base;
}
}
-void vg_strcati64r( vg_str *str, i64 value, u64 base, u32 width, c8 blank_character )
+void vg_strcati64r( vg_str *str, i64 value, u64 base, u32 width, c8 blank_c8acter )
{
VG_ASSERT( base >= 2 );
padding = width - digits;
for( u32 i=0; i<padding; i ++ )
- vg_strcatch( str, blank_character );
+ vg_strcatch( str, blank_c8acter );
for( u32 i=0; i<digits; i ++ )
vg_strcatch( str, temp[ digits -1 -i ] );
-#pragma once
-#include "vg_platform.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_string.c"
+#else
/* string builder with optional dynamic memory or static buffer. */
struct vg_str
{
- char *buffer;
+ c8 *buffer;
i32 i, /* -1: error condition. otherwise, current cursor position */
len; /* -1: dynamically allocated. otherwise, buffer length */
};
* Reset string. If len is -1 (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 );
void vg_strfree( vg_str *str );
/*
* Append null terminated string to vg_str
*/
-void vg_strcat( vg_str *str, const char *append );
-void vg_strcatf( vg_str *str, const char *fmt, ... );
+void vg_strcat( vg_str *str, const c8 *append );
+void vg_strcatf( vg_str *str, const c8 *fmt, ... );
/*
- * Append character to vg_str
+ * Append c8acter to vg_str
*/
-void vg_strcatch( vg_str *str, char c );
+void vg_strcatch( vg_str *str, c8 c );
/* Print various data types onto vg_str */
-void vg_strcati64r( vg_str *str, i64 value, u64 base, u32 width, c8 blank_character );
+void vg_strcati64r( vg_str *str, i64 value, u64 base, u32 width, c8 blank_c8acter );
void vg_strcatu64( vg_str *str, u64 value, u64 base );
void vg_strcati64( vg_str *str, i64 value, u64 base );
void vg_strcatf64( vg_str *str, f64 value, u64 base, u32 decimal_places );
int vg_strgood( vg_str *str );
/*
- * 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 );
enum strncpy_behaviour
{
k_strncpy_overflow_fatal = 2
};
-u32 vg_strncpy( const char *src, char *dst, u32 len, enum strncpy_behaviour behaviour );
-u32 vg_strdjb2( const char *str );
-bool vg_strdjb2_eq( const char *s1, u32 h1, const char *s2, u32 h2 );
+u32 vg_strcpy( const c8 *src, c8 *dst );
+u32 vg_strlen( const c8 *src );
+u32 vg_strncpy( const c8 *src, c8 *dst, u32 len, enum strncpy_behaviour behaviour );
+u32 vg_strdjb2( const c8 *str );
+bool vg_strdjb2_eq( const c8 *s1, u32 h1, const c8 *s2, u32 h2 );
#define VG_STRDJB2_EQ( CS1, S2, H2 ) \
vg_strdjb2_eq( CS1, vg_strdjb2(CS1), S2, H2 )
-bool vg_str_eq( const char *s1, const char *s2 );
+bool vg_str_eq( const c8 *s1, const c8 *s2 );
bool vg_str_flushfd( vg_str *str, int fd );
-u32 str_utf8_collapse( const char *str, char *buf, u32 length );
+u32 str_utf8_collapse( const c8 *str, c8 *buf, u32 length );
typedef struct vg_strp vg_strp;
struct vg_strp
k_vg_strp_whitespace
};
+vg_strp_info vg_strp_c8( vg_strp *p, c8 *c );
vg_strp_info vg_strp_i64( vg_strp *p, i64 *value );
vg_strp_info vg_strp_u64( vg_strp *p, u64 *value );
vg_strp_info vg_strp_f64( vg_strp *p, f64 *value );
+
+vg_strp_info vg_strp_vecf32( vg_strp *p, f32 *vec, u32 count );
+
extern const c8 *vg_strp_info_str[];
void vg_strcatf64( vg_str *str, f64 value, u64 base, u32 decimal_places );
+
+#endif
-#include "vg_tex.h"
-#include "vg_engine.h"
-#include "vg_io.h"
-#include <string.h>
-
-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; y<desc->height; y ++ )
+ {
+ for( u32 x=0; x<desc->width; 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; y<desc->height; 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; x<desc->width; 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) );
}
-/* 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
--- /dev/null
+#define VG_THIRDPARTY
+#include "vg/vg.h"
-#pragma once
-#include "vg_tower.h"
-
struct _vg_tower _vg_tower;
vg_signal_id _vg_tower_create_signal( const char *name )
-#pragma once
-#include "vg_async2.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_tower.c"
+#else
typedef u8 vg_signal_id;
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
--- /dev/null
+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<m; i++ ){
+ costs[0] = i+1;
+
+ u32 corner = i;
+
+ for( u32 j=0; j<n; j++ ){
+ u32 upper = costs[j+1];
+
+ if( s1[i] == s2[j] )
+ costs[ j+1 ] = corner;
+ else{
+ u32 t = (upper < corner)? upper: corner;
+ costs[j+1] = ((costs[j] < t)? costs[j]: t) + 1;
+ }
+
+ corner = upper;
+ }
+ }
+
+ return costs[n];
+}
+
+u32 str_lcs( const char *s1, const char *s2 )
+{
+ u32 m = VG_MIN( 31, strlen( s1 ) ),
+ n = VG_MIN( 31, strlen( s2 ) );
+
+ int suff[32][32],
+ result = 0;
+
+ for( int i=0; i<=m; i++ )
+ {
+ for( int j=0; j<=n; j++ )
+ {
+ if( i == 0 || j == 0 )
+ suff[i][j] = 0;
+ else if( s1[i-1] == s2[j-1] )
+ {
+ suff[i][j] = suff[i-1][j-1] + 1;
+ result = VG_MAX( result, suff[i][j] );
+ }
+ else
+ suff[i][j] = 0;
+ }
+ }
+
+ return result;
+}
+
+/* str must not fuckoff ever! */
+void console_suggest_score_text( const char *str, const char *input, int minscore )
+{
+ if( !str )
+ return;
+
+ /* filter duplicates */
+ for( int i=0; i<vg_console.suggestion_count; i++ )
+ if( !strcmp( vg_console.suggestions[i].str, str ) )
+ return;
+
+ /* calc score */
+ u32 score = str_lcs( str, input );
+
+ if( score < minscore )
+ return;
+
+ int best_pos = vg_console.suggestion_count;
+ for( int j=best_pos-1; j>=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; i<vg_console.var_count; i++ )
+ {
+ vg_var *cvar = &vg_console.vars[i];
+ console_suggest_score_text( cvar->name, args[0], 1 );
+ }
+
+ for( int i=0; i<vg_console.function_count; i++ )
+ {
+ vg_cmd *cmd = &vg_console.functions[i];
+ console_suggest_score_text( cmd->name, 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; i<vg_console.suggestion_count; i++ )
+ {
+ vg_console.suggestion_maxlen = VG_MAX( vg_console.suggestion_maxlen,
+ vg_console.suggestions[i].len );
+
+ if( vg_console.suggestions[i].lev_score <
+ vg_console.suggestions[0].lev_score/2 )
+ {
+ vg_console.suggestion_count = i;
+ return;
+ }
+ }
+}
+
+/*
+ * Suggestion controls
+ */
+static void _console_fetch_suggestion( ui_context *ctx )
+{
+ char *target = &vg_console.input[ vg_console.suggestion_pastepos ];
+
+ if( vg_console.suggestion_select == -1 )
+ {
+ strcpy( target, vg_console.input_copy );
+ _ui_textbox_move_cursor( ctx,
+ &ctx->textbox.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; i<console_lines; i ++ )
+ {
+ ptr --;
+
+ if( ptr < 0 ) ptr = VG_ARRAY_LEN( vg_log.log )-1;
+
+ ui_text( ctx, rect_line, vg_log.log[ptr], 1, k_ui_align_left, 0 );
+ rect_line[1] -= fh;
+ }
+
+ /*
+ * Input area
+ */
+ struct ui_textbox_callbacks callbacks =
+ {
+ .up = _vg_console_on_up,
+ .down = _vg_console_on_down,
+ .change = _vg_console_on_update,
+ .enter = _vg_console_on_enter,
+ };
+
+ u32 flags = 0;
+ if( vg_console.auto_focus ) flags |= UI_TEXTBOX_AUTOFOCUS;
+ ui_textbox( ctx, rect_input, NULL,
+ vg_console.input, VG_ARRAY_LEN(vg_console.input), 1,
+ flags, &callbacks );
+ vg_console.auto_focus = 0;
+
+ /*
+ * suggestions
+ */
+ if( vg_console.suggestion_count )
+ {
+ ui_rect rect_suggest;
+ rect_copy( rect_input, rect_suggest );
+
+ rect_suggest[0] += 6 + ctx->font->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<vg_console.suggestion_count; i ++ )
+ {
+ u32 text_colour;
+ if( i == vg_console.suggestion_select )
+ {
+ ui_fill( ctx, rect_suggest, ui_colour( ctx, k_ui_orange ) );
+ text_colour = ui_colourcont( ctx, k_ui_orange );
+ }
+ else text_colour = ui_colourcont( ctx, k_ui_bg );
+
+ ui_text( ctx, rect_suggest, vg_console.suggestions[i].str, 1, k_ui_align_left, text_colour );
+
+ rect_suggest[1] += fh;
+ }
+ }
+
+ VG_MUTEX_UNLOCK( vg_log.lock );
+}
--- /dev/null
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_ui/console.c"
+#else
+
+void vg_console_draw( ui_context *ctx );
+void console_suggest_score_text( const char *str, const char *input, int minscore );
+void console_suggest_next( ui_context *ctx );
+void console_suggest_prev( ui_context *ctx );
+
+#endif
-#include "vg/vg_ui/filebrowser.h"
-#ifdef _WIN32
-#include <windows.h>
-#include <shlobj.h>
-#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;
-#pragma once
-#include "vg/vg_ui/imgui.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_ui/filebrowser.c"
+#else
struct vg_filebrowser_entry
{
void vg_filebrowser_free_entries( struct vg_filebrowser *browser );
void vg_filebrowser_set_path_to_home( struct vg_filebrowser *browser );
+
+#endif
-#pragma once
-
-#include "vg_ui/imgui.h"
-#include "vg_platform.h"
-#include "vg_m.h"
-#include "vg_font.h"
-#include "vg_log.h"
-#include <string.h>
+// 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 )
{
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 )
-#pragma once
-
-#include "vg_platform.h"
-#include "vg_m.h"
-#include "vg_font.h"
-#include "vg_log.h"
-#include <string.h>
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_ui/imgui.c"
+#else
typedef i16 ui_px;
typedef ui_px ui_rect[4];
struct ui_batch_shader_data_image_gradient
{
- void *resource;
+ vg_tex *tex;
bool log;
f32 scale;
};
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 );
extern vg_font_sheet vg_default_font_sheet;
extern vg_font_face vgf_default_small, vgf_default_large, vgf_default_title;
+
+#endif
-#include "vg_opengl.h"
-#include "vg_shader.h"
-#include "vg_ui/imgui.h"
-#include "vg_engine.h"
-
struct vg_ui vg_ui =
{
.ctx =
.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 );
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;
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;
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-- )
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 );
}
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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;
+}
-#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 );
--- /dev/null
+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 );
+}
--- /dev/null
+#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