commpleted wrecking vg4
authorhgn <hgodden00@gmail.com>
Mon, 11 Aug 2025 21:15:37 +0000 (21:15 +0000)
committerhgn <hgodden00@gmail.com>
Mon, 11 Aug 2025 21:15:37 +0000 (21:15 +0000)
109 files changed:
build-compat/workaround-27653.c [new file with mode: 0644]
shaders/debug_lines.fs [new file with mode: 0644]
shaders/debug_lines.vs [new file with mode: 0644]
shaders/loader.fs [new file with mode: 0644]
shaders/rigidbody_view.fs [new file with mode: 0644]
shaders/rigidbody_view.vs [new file with mode: 0644]
shaders/ui.fs [new file with mode: 0644]
shaders/ui.vs [new file with mode: 0644]
shaders/ui_image.fs [new file with mode: 0644]
shaders/ui_image.vs [new file with mode: 0644]
shaders/ui_image_grad.fs [new file with mode: 0644]
shaders/ui_image_hsv.fs [new file with mode: 0644]
unit_engine.c [new file with mode: 0644]
unit_thirdparty.c [new file with mode: 0644]
vg.hconf [new file with mode: 0644]
vg_async2.c
vg_async2.h
vg_audio.c
vg_audio.h
vg_audio_dsp.c
vg_audio_dsp.h
vg_audio_synth_bird.c
vg_audio_synth_bird.h
vg_binstr.c
vg_binstr.h
vg_build.h
vg_build_font.h
vg_build_utils_shader.h
vg_bvh.c
vg_bvh.h
vg_camera.c
vg_camera.h
vg_console.c
vg_console.h
vg_db.c
vg_db.h
vg_engine.c
vg_engine.h
vg_font.h
vg_framebuffer.c
vg_framebuffer.h
vg_input.c
vg_input.h
vg_io.c
vg_io.h
vg_kv.c
vg_kv.h
vg_lines.c
vg_lines.h
vg_loader.c
vg_loader.h
vg_log.c
vg_log.h
vg_m.h
vg_magi.c
vg_magi.h
vg_mem.c
vg_mem.h
vg_mem_pool.c
vg_mem_pool.h
vg_mem_queue.c
vg_mem_queue.h
vg_mem_view.c
vg_mem_view.h
vg_msg.c
vg_msg.h
vg_mutex.h
vg_opt.c
vg_opt.h
vg_perlin.c
vg_perlin.h
vg_platform.h
vg_profiler.c
vg_profiler.h
vg_render.c
vg_render.h
vg_rigidbody.c
vg_rigidbody.h
vg_rigidbody_collision.c
vg_rigidbody_collision.h
vg_rigidbody_constraints.c
vg_rigidbody_constraints.h
vg_rigidbody_view.c
vg_rigidbody_view.h
vg_settings.c [new file with mode: 0644]
vg_settings.h [new file with mode: 0644]
vg_shader.c
vg_shader.h
vg_steam2.c
vg_steam2.h
vg_string.c
vg_string.h
vg_tex.c
vg_tex.h
vg_thirdparty.c [new file with mode: 0644]
vg_tower.c
vg_tower.h
vg_ui/console.c [new file with mode: 0644]
vg_ui/console.h [new file with mode: 0644]
vg_ui/filebrowser.c
vg_ui/filebrowser.h
vg_ui/imgui.c
vg_ui/imgui.h
vg_ui/imgui_impl_opengl.c
vg_ui/imgui_impl_opengl.h [new file with mode: 0644]
vg_vorbis.c [new file with mode: 0644]
vg_vorbis.h
vg_window.c [new file with mode: 0644]
vg_window.h [new file with mode: 0644]

diff --git a/build-compat/workaround-27653.c b/build-compat/workaround-27653.c
new file mode 100644 (file)
index 0000000..e625b2c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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);
+}
diff --git a/shaders/debug_lines.fs b/shaders/debug_lines.fs
new file mode 100644 (file)
index 0000000..1fcc7ad
--- /dev/null
@@ -0,0 +1,8 @@
+out vec4 FragColor;
+
+in vec4 s_colour;
+
+void main()
+{
+   FragColor = s_colour;
+}
diff --git a/shaders/debug_lines.vs b/shaders/debug_lines.vs
new file mode 100644 (file)
index 0000000..2fa61e0
--- /dev/null
@@ -0,0 +1,12 @@
+uniform mat4 uPv;
+layout (location=0) in vec3 a_co;
+layout (location=1) in vec4 a_colour;
+
+out vec4 s_colour;
+
+void main()
+{
+   vec4 vert_pos = uPv * vec4( a_co, 1.0 );
+   s_colour = a_colour;
+   gl_Position = vert_pos;
+}
diff --git a/shaders/loader.fs b/shaders/loader.fs
new file mode 100644 (file)
index 0000000..dc265dc
--- /dev/null
@@ -0,0 +1,34 @@
+out vec4 FragColor;
+uniform float uTime;
+uniform float uRatio;
+uniform float uOpacity;
+in vec2 aUv;
+
+float eval_zero( vec2 uv )
+{
+   vec4 vsines = sin( (uTime+uv.y*80.0) * vec4(1.1,2.0234,3.73,2.444) );
+   float gradient = min( uv.y, 0.0 );
+   float offset = vsines.x*vsines.y*vsines.z*vsines.w*gradient;
+
+   vec2 vpos = uv + vec2( offset, 0.0 );
+   float dist = dot( vpos, vpos );
+
+   float fring = step(0.1*0.1,dist) * step(dist,0.15*0.15);
+   return max( 0.0, fring * 1.0+gradient*6.0 );
+}
+
+void main()
+{
+   vec3 col = 0.5+0.5*sin( uTime + aUv.xyx + vec3(0.0,2.0,4.0) );
+   
+   vec2 uvx = aUv - vec2( 0.5 );
+   uvx.x *= uRatio;
+   uvx.y *= 0.75;
+
+   float zero = eval_zero( uvx );
+
+   float dither=fract(dot(vec2(171.0,231.0),gl_FragCoord.xy)/71.0)-0.5;
+   float fmt1 = step( 0.5, zero*zero + dither )*0.8+0.2;
+
+   FragColor = vec4(vec3(fmt1),uOpacity);
+}
diff --git a/shaders/rigidbody_view.fs b/shaders/rigidbody_view.fs
new file mode 100644 (file)
index 0000000..a47829b
--- /dev/null
@@ -0,0 +1,48 @@
+out vec4 FragColor;
+uniform vec4 uColour;
+
+in vec3 aNorm;
+in vec3 aCo;
+// The MIT License
+// Copyright Â© 2017 Inigo Quilez
+// Permission is hereby granted, free of charge, to any person obtaining a 
+// copy of this software and associated documentation files (the Software),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+// and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions: 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software. THE SOFTWARE IS 
+// PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+// DEALINGS IN THE SOFTWARE.
+
+// Info: https://iquilezles.org/articles/filterableprocedurals
+//  
+// More filtered patterns:  https://www.shadertoy.com/playlist/l3KXR1
+
+vec3 tri( in vec3 x )
+{
+   return 1.0-abs(2.0*fract(x/2.0)-1.0);
+}
+
+float checkersTextureGrad( in vec3 p, in vec3 ddx, in vec3 ddy )
+{
+  vec3 w = max(abs(ddx), abs(ddy)) + 0.0001; // filter kernel
+  vec3 i = (tri(p+0.5*w)-tri(p-0.5*w))/w;    // analytical integral (box filter)
+  return 0.5 - 0.5*i.x*i.y*i.z;              // xor pattern
+}
+
+void main()
+{
+   vec3 uvw = aCo;
+   vec3 ddx_uvw = dFdx( uvw );
+   vec3 ddy_uvw = dFdy( uvw );
+   float diffuse = checkersTextureGrad( uvw, ddx_uvw, ddy_uvw )*0.5+0.4;
+   float light = dot( vec3(0.8017,0.5345,-0.2672), aNorm )*0.5 + 0.5;
+   FragColor = light * diffuse * uColour;
+}
diff --git a/shaders/rigidbody_view.vs b/shaders/rigidbody_view.vs
new file mode 100644 (file)
index 0000000..08fc744
--- /dev/null
@@ -0,0 +1,20 @@
+uniform mat4 uPv;
+uniform mat4x3 uMdl;
+uniform mat4x3 uMdl1;
+layout (location=0) in vec4 a_co;
+layout (location=1) in vec3 a_norm;
+out vec3 aNorm;
+out vec3 aCo;
+
+void main()
+{
+   vec3 world_pos0 = uMdl  * vec4( a_co.xyz, 1.0 );
+   vec3 world_pos1 = uMdl1 * vec4( a_co.xyz, 1.0 );
+   vec3 co = mix( world_pos0, world_pos1, a_co.w );
+   vec4 vert_pos = uPv * vec4( co, 1.0 );
+
+   gl_Position = vert_pos;
+   vec3 l = vec3(length(uMdl[0]),length(uMdl[1]),length(uMdl[2]));
+   aNorm = (mat3(uMdl) * a_norm)/l;
+   aCo = a_co.xyz*l;
+}
diff --git a/shaders/ui.fs b/shaders/ui.fs
new file mode 100644 (file)
index 0000000..bf8525d
--- /dev/null
@@ -0,0 +1,48 @@
+uniform sampler2D uTexGlyphs;
+uniform sampler2D uTexBG;
+uniform vec4 uColour;
+uniform float uSpread;
+out vec4 FragColor;
+
+in vec4 aTexCoords;
+in vec4 aColour;
+
+vec2 rand_hash22( vec2 p )
+{
+  vec3 p3 = fract(vec3(p.xyx) * 213.8976123);
+  p3 += dot(p3, p3.yzx+19.19);
+  return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));
+}
+
+void main()
+{
+  vec4 diffuse = aColour;
+  
+  vec4 avg = vec4(0.0);
+
+  if( aColour.a == 0.0 )
+  {
+     avg = aColour;
+       avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;
+  }
+  else
+  {
+     if( uSpread > 0.0001 )
+     {
+        for( int i=0; i<4; i ++ )
+        {
+           vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));
+           avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );
+        }
+        avg *= 0.25;
+        avg.a = 1.0;
+        avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );
+     }
+     else
+     {
+        avg = aColour;
+     }
+  }
+
+  FragColor = avg * uColour;
+}
diff --git a/shaders/ui.vs b/shaders/ui.vs
new file mode 100644 (file)
index 0000000..37f8b3b
--- /dev/null
@@ -0,0 +1,17 @@
+layout (location=0) in vec2 a_co;
+layout (location=1) in vec2 a_uv;
+layout (location=2) in vec4 a_colour;
+uniform mat3 uPv;
+uniform vec2 uBGInverseRatio;
+uniform vec2 uInverseFontSheet;
+
+out vec4 aTexCoords;
+out vec4 aColour;
+
+void main()
+{
+   vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );
+       gl_Position = proj_pos;
+       aTexCoords = vec4( a_uv * uInverseFontSheet, (proj_pos.xy*0.5+0.5) * uBGInverseRatio );
+       aColour = a_colour;
+}
diff --git a/shaders/ui_image.fs b/shaders/ui_image.fs
new file mode 100644 (file)
index 0000000..5d45c1d
--- /dev/null
@@ -0,0 +1,13 @@
+uniform sampler2D uTexImage;
+uniform vec4 uColour;
+out vec4 FragColor;
+
+in vec2 aTexCoords;
+in vec4 aColour;
+in vec2 aWsp;
+
+void main()
+{
+       vec4 colour = texture( uTexImage, aTexCoords );
+       FragColor = colour * aColour * uColour;
+}
diff --git a/shaders/ui_image.vs b/shaders/ui_image.vs
new file mode 100644 (file)
index 0000000..47f1704
--- /dev/null
@@ -0,0 +1,17 @@
+layout (location=0) in vec2 a_co;
+layout (location=1) in vec2 a_uv;
+layout (location=2) in vec4 a_colour;
+uniform mat3 uPv;
+
+out vec2 aTexCoords;
+out vec4 aColour;
+out vec2 aWsp;
+
+void main()
+{
+       gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );
+       aTexCoords = a_uv * 0.00390625;
+       aColour = a_colour;
+       
+       aWsp = a_co;
+}
diff --git a/shaders/ui_image_grad.fs b/shaders/ui_image_grad.fs
new file mode 100644 (file)
index 0000000..3201908
--- /dev/null
@@ -0,0 +1,22 @@
+uniform sampler2D uTexImage;
+uniform vec4 uColour;
+uniform int uLog;
+uniform float uScale;
+
+out vec4 FragColor;
+
+in vec2 aTexCoords;
+in vec4 aColour;
+in vec2 aWsp;
+
+void main()
+{
+       vec4 colour = texture( uTexImage, aTexCoords );
+   float v = colour.r;
+   if( uLog == 1 )
+   {
+      v = log(v);
+   }
+   v *= uScale;
+       FragColor = vec4(vec3(v)*uColour.rgb,1.0);
+}
diff --git a/shaders/ui_image_hsv.fs b/shaders/ui_image_hsv.fs
new file mode 100644 (file)
index 0000000..e98838c
--- /dev/null
@@ -0,0 +1,15 @@
+uniform float uHue;
+out vec4 FragColor;
+
+in vec2 aTexCoords;
+in vec4 aColour;
+in vec2 aWsp;
+
+void main()
+{
+   vec3 c = vec3( uHue, aTexCoords );
+   vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);
+   vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);
+   vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);
+       FragColor = vec4( colour, 1.0 );
+}
diff --git a/unit_engine.c b/unit_engine.c
new file mode 100644 (file)
index 0000000..1c01be4
--- /dev/null
@@ -0,0 +1,5 @@
+#include "vg/vg.hconf"
+
+#define VG_IMPLEMENTATION
+#include "vg/vg.hconf"
+#undef VG_IMPLEMENTATION
diff --git a/unit_thirdparty.c b/unit_thirdparty.c
new file mode 100644 (file)
index 0000000..17ff8c0
--- /dev/null
@@ -0,0 +1,2 @@
+#define VG_THIRDPARTY
+#include "vg/vg.hconf"
diff --git a/vg.hconf b/vg.hconf
new file mode 100644 (file)
index 0000000..f11f84b
--- /dev/null
+++ b/vg.hconf
@@ -0,0 +1,188 @@
+/* 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
index fedaf33fc2008a0abdc5e050a56dd5dab959b86f..cf6260bc22872bd0467aca39d2bbaba978a95f35 100644 (file)
@@ -1,4 +1,62 @@
-#include "vg/vg_async2.h"
+struct _vg_async
+{
+   i16 group_counts[ 16 ];
+   vg_mutex count_lock;
+}
+_vg_async;
+
+struct vg_async_task
+{
+   const c8 *alloc_debug_info;
+   vg_async_fn fn;
+   u32 buffer_size;
+   u16 groups, unused0;
+
+   union
+   {
+      u64 _force_8byte_align[];
+      u8 buffer[];
+   };
+};
+
+VG_API void _vg_async_init( void )
+{
+   VG_ASSERT( VG_MUTEX_INIT( _vg_async.count_lock ) );
+}
+
+static void _vg_async_group_increment( u16 groups, i16 dir )
+{
+   if( !groups )
+      return;
+
+   VG_MUTEX_LOCK( _vg_async.count_lock );
+   for( u16 i=0; i<16; i ++ )
+   {
+      if( (groups >> i) & 0x1 )
+      {
+         _vg_async.group_counts[i] += dir;
+
+         VG_ASSERT( _vg_async.group_counts[i] >= 0 );
+         VG_ASSERT( _vg_async.group_counts[i] <= 2048 );
+
+         vg_warn( "The task count for group %d has %s to %d\n", 
+                  i, dir>0? "increased": "decreased", _vg_async.group_counts[i] );
+      }
+   }
+   VG_MUTEX_UNLOCK( _vg_async.count_lock );
+}
+
+VG_API i16 _vg_async_group_count( u16 group )
+{
+   VG_ASSERT( group );
+   u32 index = __builtin_ctz( (u32)group );
+
+   VG_MUTEX_LOCK( _vg_async.count_lock );
+   i16 count = _vg_async.group_counts[ index ];
+   VG_MUTEX_UNLOCK( _vg_async.count_lock );
+
+   return count;
+}
 
 bool vg_init_async_queue( vg_async_queue *queue )
 {
@@ -40,9 +98,10 @@ bool vg_async_checksize( vg_async_queue *queue, u32 bytes )
    return total_size <= queue->queue.size;
 }
 
-vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking, const char *debug_info )
+VG_TIER_1 vg_async_task *vg_create_task( vg_async_queue *queue, u32 buffer_size, u32 async_flags, const c8 *debug_info )
 {
-   u32 total_size = sizeof(vg_async_task) + bytes;
+   vg_queue *ring = &queue->queue;
+   u32 total_size = sizeof(vg_async_task) + buffer_size;
    VG_ASSERT( total_size <= queue->queue.size );
    VG_MUTEX_LOCK( queue->data_lock );
    if( queue->allocating_task )
@@ -52,36 +111,56 @@ vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool b
                       "  Overlapping call at: %s\n", queue->allocating_task->alloc_debug_info, debug_info );
    }
    VG_MUTEX_LOCK( queue->lock );
-   vg_queue *ring = &queue->queue;
-   vg_async_task *task = vg_queue_alloc( ring, total_size, NULL );
 
-   while( blocking && !task )
+   vg_async_task *task = vg_queue_alloc( ring, total_size );
+   while( (async_flags & (VG_ASYNC_CRIT|VG_ASYNC_BLOCKING)) && !task )
    {
       VG_MUTEX_UNLOCK( queue->lock );
+
+      if( async_flags & VG_ASYNC_CRIT )
+         vg_fatal_error( "Too much tasks (critical)\n" );
+
       VG_SEMAPHORE_WAIT( queue->blocking_signal );
       VG_MUTEX_LOCK( queue->lock );
-      task = vg_queue_alloc( ring, total_size, NULL );
+      task = vg_queue_alloc( ring, total_size );
    }
 
    if( task )
    {
-      task->handler = NULL;
-      task->queue = queue;
-      task->alloc_debug_info = debug_info;
       queue->allocating_task = task;
+      task->alloc_debug_info = debug_info;
+      task->fn = NULL;
+      task->buffer_size = buffer_size;
+      task->groups = _vg_async_context_get_groups();
+      _vg_async_group_increment( task->groups, +1 );
+      VG_MUTEX_UNLOCK( queue->lock );
+      return task;
    }
+   else
+   {
+      VG_MUTEX_UNLOCK( queue->lock );
+      return NULL;
+   }
+}
 
-   VG_MUTEX_UNLOCK( queue->lock );
-   return task;
+void *vg_task_buffer( vg_async_queue *queue, vg_async_task *task )
+{
+   VG_ASSERT( task );
+   VG_ASSERT( queue->allocating_task == task );
+   return task->buffer;
 }
 
-void _vg_async_task_dispatch( vg_async_task *task, void (*handler)( vg_async_task *task ), const char *debug_info )
+void vg_task_send( vg_async_queue *queue, vg_async_task *task, vg_async_fn fn )
 {
-   task->queue->allocating_task = NULL;
-   VG_MUTEX_UNLOCK( task->queue->data_lock );
-   task->handler = handler;
-   task->alloc_debug_info = debug_info;
-   VG_SEMAPHORE_POST( task->queue->work_semaphore );
+   VG_ASSERT( task );
+   VG_ASSERT( queue->allocating_task == task );
+
+   if( fn ) task->fn = fn;
+   else     _vg_async_group_increment( task->groups, -1 );
+   queue->allocating_task = NULL;
+
+   VG_MUTEX_UNLOCK( queue->data_lock );
+   VG_SEMAPHORE_POST( queue->work_semaphore );
 }
 
 bool vg_async_has_work( vg_async_queue *queue )
@@ -111,10 +190,20 @@ bool vg_async_process_next_task( vg_async_queue *queue )
 
    if( task )
    {
-      if( !task->handler )
-         vg_fatal_error( "NO HANDLER ALLOCATED FROM %s\n", task->alloc_debug_info );
+      /* task can be NULL if it was cancelled (so this is a NOP). Makes code easier if we do this instead of 
+       * reverting the queue to cancel. */
+      if( task->fn )
+      {
+         vg_async_info info = 
+         {
+            .buffer_size = task->buffer_size,
+         };
 
-      task->handler( task );
+         _vg_async_context_push_groups( task->groups );
+         task->fn( (void *)task->buffer, &info );
+         _vg_async_context_pop_groups();
+         _vg_async_group_increment( task->groups, -1 );
+      }
 
       VG_MUTEX_LOCK( queue->lock );
       vg_queue_pop( &queue->queue );
@@ -134,121 +223,3 @@ void vg_async_queue_end( vg_async_queue *queue, enum async_quit quit )
    VG_MUTEX_UNLOCK( queue->lock );
    VG_SEMAPHORE_POST( queue->work_semaphore );
 }
-
-/* 
- * simple functions
- * ------------------------------------------------------------------------------------------------------------------ */
-struct simple_function_info
-{
-   void (*fn)(void *userdata);
-   void *userdata;
-};
-
-static void simple_function_call( vg_async_task *task )
-{
-   struct simple_function_info *info = (void *)task->data;
-   info->fn( info->userdata );
-}
-
-void vg_async_call( vg_async_queue *queue, void(*fn)(void *userdata), void *userdata )
-{
-   vg_async_task *task = vg_allocate_async_task( queue, sizeof(struct simple_function_info), 1 );
-   struct simple_function_info *info = (void *)task->data;
-   info->fn = fn;
-   info->userdata = userdata;
-   vg_async_task_dispatch( task, simple_function_call );
-}
-
-/*
- * Coroutines
- * ------------------------------------------------------------------------------------------------------------------ */
-
-bool co_begin( vg_coroutine *co )
-{
-   co->i = 0;
-   if( co->state == k_coroutine_state_none )
-   {
-      co->state = k_coroutine_state_init;
-      return 1;
-   }
-   else 
-   {
-      co->state = k_coroutine_state_run;
-      return 0;
-   }
-}
-
-void co_thread( vg_coroutine *co, i32 thread_id, vg_async_queue *queue )
-{
-   co->worker_queues[thread_id] = queue;
-}
-
-void *co_storage( vg_coroutine *co, u16 size )
-{
-   co->data_size = size;
-   co->task = vg_allocate_async_task( co->worker_queues[0], sizeof(vg_coroutine) + size, 1 );
-   vg_coroutine *next_co = (void *)co->task->data;
-   memcpy( next_co, co, sizeof(vg_coroutine) );
-   memset( next_co->data, 0, size );
-   return next_co->data;
-}
-
-static void co_internal_call( vg_async_task *task )
-{
-   vg_coroutine *co = (void *)task->data;
-   co->userdata = NULL;
-   co->task = NULL;
-   co->fn( co );
-}
-
-bool co_step( vg_coroutine *co, i32 thread_id )
-{
-   if( co->state == k_coroutine_state_run )
-   {
-      if( co->i == co->step )
-      {
-         if( thread_id == co->thread )
-         {
-            co->step ++;
-            co->i ++;
-            return 1;
-         }
-         else
-         {
-            co->thread = thread_id;
-            u32 size = sizeof(vg_coroutine) + co->data_size;
-            co->task = vg_allocate_async_task( co->worker_queues[ thread_id ], size, 1 );
-            vg_coroutine *co_copy = (void *)co->task->data;
-            memcpy( co_copy, co, size );
-            co->state = k_coroutine_state_orphan;
-            return 0;
-         }
-      }
-      else
-         co->i ++;
-   }
-
-   return 0;
-}
-
-void co_end( vg_coroutine *co )
-{
-   if( co->state == k_coroutine_state_init )
-   {
-      if( !co->task )
-         co_storage( co, 0 );
-   }
-
-   if( co->task )
-      vg_async_task_dispatch( co->task, co_internal_call );
-}
-
-void co_run( void(*fn)(vg_coroutine *co), void *userdata )
-{
-   vg_coroutine bootstrap = {0};
-   bootstrap.userdata = userdata;
-   bootstrap.state = k_coroutine_state_none;
-   bootstrap.thread = 0xffff;
-   bootstrap.fn = fn;
-   fn( &bootstrap );
-}
index 8cf2d2dffaf3e63a8f9d8f4f89baa5c864c5b182..77d9f60b295afb36b06a4948b771ed79954a48ed 100644 (file)
@@ -1,11 +1,23 @@
-#pragma once
-#include "vg/vg_mem.h"
-#include "vg/vg_mem_queue.h"
-#include "vg/vg_mutex.h"
+/* VG Async 2
+ *  Type: Library
+ *  Depends on: vg_platform, vg_mem, vg_mutex
+ */
+
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_async2.c"
+#else
 
 typedef struct vg_async_queue vg_async_queue;
 typedef struct vg_async_task vg_async_task;
-typedef struct vg_coroutine vg_coroutine;
+typedef struct vg_async_data vg_async_data;
+typedef struct vg_async_info vg_async_info;
+
+typedef void (*vg_async_fn)( void *user, vg_async_info *info );
+
+struct vg_async_info
+{
+   u32 buffer_size;
+};
 
 struct vg_async_queue
 {
@@ -28,55 +40,60 @@ struct vg_async_queue
    quit;
 };
 
-struct vg_async_task
-{
-   vg_async_queue *queue;
-   const char *alloc_debug_info;
-   void (*handler)( vg_async_task *task );
-   u8 data[];
-};
+VG_API void _vg_async_init( void );
 
-struct vg_coroutine
-{
-   void *userdata;
-   vg_async_task *task;
-   vg_async_queue *worker_queues[2];
-   void (*fn)(vg_coroutine *co);
+VG_API u32  _vg_async_push_grouping( u32 group_ids );
+VG_API void _vg_async_pop_grouping(void);
+VG_API i16 _vg_async_group_count( u16 group );
 
-   u16 step, i, thread;
+bool vg_init_async_queue( vg_async_queue *queue );
+void vg_free_async_queue( vg_async_queue *queue );
 
-   enum
-   {
-      k_coroutine_state_none,
-      k_coroutine_state_init,
-      k_coroutine_state_run,
-      k_coroutine_state_orphan
-   }
-   state;
+#define VG_ASYNC_CANCEL   0
+#define VG_ASYNC_OK       1
 
-   u16 data_size;
-   u8 data[];
-};
+/* If task would exceed queue capacity, crash the program */
+#define VG_ASYNC_CRIT     0x4
 
-void *co_storage( vg_coroutine *co, u16 size );
-bool co_begin( vg_coroutine *co );
-void co_thread( vg_coroutine *co, i32 thread_id, vg_async_queue *queue );
-bool co_step( vg_coroutine *co, i32 thread_id );
-void co_run( void(*fn)(vg_coroutine *co), void *userdata );
-void co_end( vg_coroutine *co );
+/* If task currently would exceeed queue capacity, block until there is space */
+#define VG_ASYNC_BLOCKING 0x2
 
-bool vg_init_async_queue( vg_async_queue *queue );
-void vg_free_async_queue( vg_async_queue *queue );
+/* If queue is full, immediately return and don't conduct task */
+#define VG_ASYNC_NONBLOCK 0x1
 
-/* returns NULL if out of memory, or if blocking is set, wait for memory to become availible */
-#define vg_allocate_async_task( Q,B,BL ) _vg_allocate_async_task( Q,B,BL, VG_LOG_WHERE )
-vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking, const char *debug_info );
+/* Any asynchronous flag. NOTE: if you try to create a task that exceeds the queue capacity, regardless of which 
+ *                              strategy you chose, the program will crash. */
+#define VG_ASYNC          (VG_ASYNC_CRIT|VG_ASYNC_BLOCKING|VG_ASYNC_NONBLOCK)
+
+/* Used for clarity in other functions that you DONT wan't it to run asynchronously */
+#define VG_SYNC           0x0
+
+#define VG_ASYNC_GROUP_OPENGL    0x1
+#define VG_ASYNC_GROUP_INIT      0x2
+#define VG_ASYNC_GROUP_RESERVED2 0x4
+#define VG_ASYNC_GROUP_RESERVED3 0x8
+#define VG_ASYNC_GROUP_RESERVED4 0x10
+#define VG_ASYNC_GROUP_RESERVED5 0x20
+#define VG_ASYNC_GROUP_RESERVED6 0x40
+#define VG_ASYNC_GROUP_RESERVED7 0x80
+
+#define VG_ASYNC_GROUP_CLIENT0 0x100
+#define VG_ASYNC_GROUP_CLIENT1 0x200
+#define VG_ASYNC_GROUP_CLIENT2 0x400
+#define VG_ASYNC_GROUP_CLIENT3 0x800
+#define VG_ASYNC_GROUP_CLIENT4 0x1000
+#define VG_ASYNC_GROUP_CLIENT5 0x2000
+#define VG_ASYNC_GROUP_CLIENT6 0x4000
+#define VG_ASYNC_GROUP_CLIENT7 0x8000
+
+VG_TIER_1 vg_async_task *vg_create_task( vg_async_queue *queue, u32 buffer_size, u32 async_flags, const c8 *debug_info );
+void *vg_task_buffer( vg_async_queue *queue, vg_async_task *task );
+void  vg_task_send( vg_async_queue *queue, vg_async_task *task, vg_async_fn fn );
 
-#define vg_async_task_dispatch( T,H ) _vg_async_task_dispatch( T,H, VG_LOG_WHERE )
-void _vg_async_task_dispatch( vg_async_task *task, void (*handler)( vg_async_task *task ), const char *debug_info );
 void vg_async_queue_end( vg_async_queue *queue, enum async_quit quit );
 
 bool vg_async_has_work( vg_async_queue *queue );
 bool vg_async_process_next_task( vg_async_queue *queue );
-void vg_async_call( vg_async_queue *queue, void(*fn)(void *userdata), void *userdata );
 bool vg_async_checksize( vg_async_queue *queue, u32 bytes );
+
+#endif
index 41f913614d31d9ca6f20345baf17b4c5ecccfdb6..56aee244c2272163e41ec132f109f669a8c75602 100644 (file)
@@ -1,47 +1,9 @@
-#include "vg_audio.h"
-#include "vg_audio_dsp.h"
-#include "vg_platform.h"
-#include "vg_io.h"
-#include "vg_m.h"
-#include "vg_console.h"
-#include "vg_profiler.h"
-#include "vg_audio_synth_bird.h"
-#include "vg_vorbis.h"
-#include <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)
@@ -68,114 +30,105 @@ static void vg_audio_assert_lock(void)
 /* clip loading from disk
  * -------------------------------------------------------------------------------
  */
-void audio_clip_load( audio_clip *clip, vg_stack_allocator *stack )
+VG_TIER_2 bool vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack )
 {
-   THREAD_1;
-
-   if( stack == NULL )
-      stack = _vg_audio.permanent_stack;
-
-   if( _vg_audio.always_keep_clips_compressed )
-   {
-      if( (clip->flags & AUDIO_FLAG_FORMAT) != k_audio_format_bird )
-      {
-         clip->flags &= ~AUDIO_FLAG_FORMAT;
-         clip->flags |= k_audio_format_vorbis;
-      }
-   }
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) );
 
    /* load in directly */
    u32 format = clip->flags & AUDIO_FLAG_FORMAT;
-
-   /* TODO: This contains audio_lock() and unlock, but i don't know why
-    *       can probably remove them. Low priority to check this */
-
-   /* TODO: packed files for vorbis etc, should take from data if its not not 
-    *       NULL when we get the clip
-    */
-
    if( format == k_audio_format_vorbis )
    {
-      if( !clip->path )
+      if( clip->path )
+      {
+         clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 );
+         if( clip->any_data )
+         {
+            float mb = (float)(clip->size) / (1024.0f*1024.0f);
+            vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
+            return 1;
+         }
+         else
+         {
+            vg_error( "Audio failed to load '%s'\n", clip->path );
+            return 0;
+         }
+      }
+      else
+      {
          vg_error( "No path specified, embeded vorbis unsupported\n" );
-
-      vg_audio_lock();
-      clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 );
-      vg_audio_unlock();
-
-      if( !clip->any_data )
-         vg_error( "Audio failed to load\n" );
-
-      float mb = (float)(clip->size) / (1024.0f*1024.0f);
-      vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
+         return 0;
+      }
    }
    else if( format == k_audio_format_stereo )
    {
       vg_error( "Unsupported format (Stereo uncompressed)\n" );
+      return 0;
    }
    else if( format == k_audio_format_bird )
    {
       if( !clip->any_data )
       {
          vg_error( "No data, external birdsynth unsupported\n" );
+         return 0;
       }
 
-      u32 total_size  = clip->size + sizeof(struct synth_bird);
-          total_size -= sizeof(struct synth_bird_settings);
-          total_size  = vg_align8( total_size );
-
-      if( total_size > AUDIO_DECODE_SIZE )
-         vg_error( "Bird coding too long, and exceeds maximum decode size\n" );
-
-      struct synth_bird *bird = vg_stack_allocate( stack, total_size, 8, NULL );
-      memcpy( &bird->settings, clip->any_data, clip->size );
-
+      struct synth_bird *bird = vg_stack_allocate( stack, sizeof(struct synth_bird), 8, NULL );
+      bird->settings = clip->any_data;
       clip->any_data = bird;
-      clip->size = total_size;
-
-      vg_info( "Loaded bird synthesis pattern (%u bytes)\n", total_size );
+      vg_info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size );
+      return 1;
    }
    else
    {
       if( !clip->path )
+      {
          vg_error( "No path specified, embeded mono unsupported\n" );
+         return 0;
+      }
 
-      vg_stack_clear( &vg.scratch );
-      u32 fsize;
-
-      stb_vorbis_alloc alloc = {
-         .alloc_buffer = vg_stack_allocate( &vg.scratch, AUDIO_DECODE_SIZE, 8, NULL ),
+      u32 temp_frame = _vg_start_temp_frame();
+      stb_vorbis_alloc alloc = 
+      {
+         .alloc_buffer = vg_stack_allocate( _vg_temp_stack(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ),
          .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
       };
 
-      void *filedata = vg_file_read( &vg.scratch, clip->path, &fsize, 0 );
-
+      u32 fsize;
+      void *filedata = vg_file_read( _vg_temp_stack(), clip->path, &fsize, 0 );
       int err;
       stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc );
-
       if( !decoder )
-         vg_fatal_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err );
+      {
+         vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err );
+         _vg_end_temp_frame( temp_frame );
+         return 0;
+      }
 
       /* only mono is supported in uncompressed */
       u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ),
           data_size      = length_samples * sizeof(i16);
 
-      vg_audio_lock();
       clip->any_data = vg_stack_allocate( stack, data_size, 8, NULL );
       clip->size = length_samples;
-      vg_audio_unlock();
-
       int read_samples = stb_vorbis_get_samples_i16_downmixed( decoder, clip->any_data, length_samples );
+      _vg_end_temp_frame( temp_frame );
 
-      if( read_samples != length_samples )
+      if( read_samples == length_samples )
+         return 1;
+      else
+      {
          vg_error( "Decode error, read_samples did not match length_samples\n" );
+         return 0;
+      }
    }
 }
 
-void audio_clip_loadn( audio_clip *arr, int count, vg_stack_allocator *stack )
+VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack )
 {
-   for( int i=0; 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;
 }
 
 /* 
@@ -654,7 +607,7 @@ void vg_audio_start_lfo( audio_channel_id lfo_id )
 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;
@@ -736,7 +689,7 @@ static void audio_channel_get_samples( audio_channel_id id, struct audio_channel
       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 )
@@ -860,7 +813,7 @@ static void audio_channel_mix( audio_channel_id id,
    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;
@@ -921,12 +874,14 @@ static void audio_channel_mix( audio_channel_id id,
       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;
@@ -1123,10 +1078,10 @@ static void _vg_audio_mixer( void *user, u8 *stream, int byte_count )
       {
          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;
@@ -1160,19 +1115,6 @@ static void _vg_audio_mixer( void *user, u8 *stream, int byte_count )
       }
    }
 
-   /* 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();
 }
@@ -1230,12 +1172,12 @@ static void cb_vg_audio_view( ui_context *ctx, ui_rect rect, struct vg_magi_pane
             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, "          " );
@@ -1285,7 +1227,7 @@ static void cb_vg_audio_view( ui_context *ctx, ui_rect rect, struct vg_magi_pane
 
             vg_strcat( &str, " " );
             f32 volume = (f32)channel->ui_volume / (f32)AUDIO_VOLUME_100;
-            vg_strcati32r( &str, volume * 100.0f, 3, ' ' );
+            vg_strcati64r( &str, volume * 100.0f, 10, 3, ' ' );
             vg_strcatch( &str, '%' );
 
             vg_strcat( &str, " " );
@@ -1424,7 +1366,7 @@ static int cmd_vg_audio( int argc, const char *argv[] )
    return 1;
 }
 
-void vg_audio_register(void)
+VG_API void _vg_audio_register(void)
 {
    vg_console_reg_cmd( "vg_audio", cmd_vg_audio, NULL );
    vg_console_reg_var( "volume", &_vg_audio.master_volume_ui, k_var_dtype_f32, VG_VAR_PERSISTENT );
@@ -1469,15 +1411,21 @@ void vg_audio_device_init(void)
    }
 }
 
-void vg_audio_init(void)
+static void vg_audio_free(void)
 {
-   _vg_profile_reg_set( &_vg_prof_audio );
-   _vg_audio.permanent_stack = vg_stack_make_substack( &vg.rtmem, VG_MB(32), "Permanent audio data" );
-   vg_stack_set_flags( _vg_audio.permanent_stack, VG_STACK_ALLOCATOR_METADATA );
+   SDL_CloseAudioDevice( _vg_audio.sdl_output_device );
+   _vg_audio.sdl_output_device = 0;
+}
+
+VG_API void _vg_audio_init(void)
+{
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) );
+
+   _vg_audio.profiler = _vg_profiler_create( "vg.audio", 1000.0f/20.0f );
 
    /* fixed */
    u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
-   _vg_audio.decoding_buffer = vg_stack_allocate( &vg.rtmem, decode_size, 8, "Decoding buffers" );
+   _vg_audio.decoding_buffer = vg_stack_allocate( NULL, decode_size, 8, "Decoding buffers" );
 
    struct audio_master_controls *master_controls = &_vg_audio.controls;
    master_controls->dsp_enabled = _vg_audio.dsp_enabled_ui;
@@ -1485,21 +1433,14 @@ void vg_audio_init(void)
    v3_copy( (v3f){1,0,0}, master_controls->listener_right_ear_direction );
    v3_zero( master_controls->listener_velocity );
    v3_zero( master_controls->listener_position );
-   vg_dsp_init();
-}
+   _vg_dsp_init();
 
-void vg_audio_begin(void)
-{
    _vg_audio.mutex = SDL_CreateMutex();
    if( _vg_audio.mutex == NULL )
       vg_fatal_error( "SDL2: %s\n", SDL_GetError() );
    vg_audio_device_init();
-}
 
-void vg_audio_free(void)
-{
-   SDL_CloseAudioDevice( _vg_audio.sdl_output_device );
-   _vg_audio.sdl_output_device = 0;
+   _vg_add_exit_function( vg_audio_free );
 }
 
 void vg_audio_preupdate(void)
index 8be4c690c0e38d0fb695c6e96aa85ff05cc8187c..ef0acf9097b0fd3518e202e33af0aa82562f16cd 100644 (file)
@@ -1,12 +1,6 @@
-/* Copyright (C) 2021-2025 Mt.Zero Software - All Rights Reserved */
-
-#pragma once
-
-#include "vg_platform.h"
-#include "vg_engine.h"
-#include "vg_string.h"
-#include "vg_vorbis.h"
-
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_audio.c"
+#else
 #define AUDIO_FRAME_SIZE 512
 #define AUDIO_MIX_FRAME_SIZE 256
 
@@ -182,7 +176,6 @@ struct vg_audio
    SDL_AudioDeviceID sdl_output_device;
    vg_str device_choice; /* buffer is null? use default from OS */
 
-   vg_stack_allocator *permanent_stack;
    void *decoding_buffer;
 
    SDL_mutex *mutex;
@@ -210,23 +203,24 @@ struct vg_audio
       i32 volume, volume_at_frame_start;
    }
    state;
-   bool always_keep_clips_compressed;
 
    f32 master_volume_ui;
    i32 dsp_enabled_ui;
 
    bool working;
+
+   u32 profiler;
 }
 extern _vg_audio;
 
-void vg_audio_register(void);
+VG_API void _vg_audio_register(void);
+VG_API void _vg_audio_init(void);
+
 void vg_audio_device_init(void);
 void vg_audio_begin(void);
-void vg_audio_init(void);
-void vg_audio_free(void);
 
-void audio_clip_load( audio_clip *clip, vg_stack_allocator *stack );
-void audio_clip_loadn( audio_clip *arr, int count, vg_stack_allocator *stack );
+VG_TIER_2 bool vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack );
+VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack );
 
 void vg_audio_lock(void);
 void vg_audio_unlock(void);
@@ -265,3 +259,4 @@ void vg_audio_fadeout_flagged_audio( u32 flag, f32 length );
 bool vg_audio_flagged_stopped( u32 flag );
 bool vg_audio_is_channel_using_clip( audio_channel_id id, audio_clip *clip );
 void vg_audio_set_flagged_pause( u32 flag, bool pause );
+#endif
index 7d6bd1af8293c44313061b684916a6b5867caf21..e30a43112478c331c151f2033e98618d97181526 100644 (file)
@@ -1,6 +1,3 @@
-#include "vg_audio_dsp.h"
-#include "vg_mem.h"
-
 struct vg_dsp vg_dsp;
 
 float *dsp_allocate( u32 samples )
@@ -130,10 +127,10 @@ static struct dsp_lpf   __echos_lpf[8];
 #endif
 static struct dsp_schroeder __diffusion_chain[8];
 
-void vg_dsp_init( void )
+VG_API void _vg_dsp_init(void)
 {
    vg_rand_seed( &vg_dsp.rand, 461 );
-   vg_dsp.buffer = vg_stack_allocate( &vg.rtmem, VG_MB(1), 8, "Audio DSP Buffer" );
+   vg_dsp.buffer = vg_stack_allocate( NULL, VG_MB(1), 8, "Audio DSP Buffer" );
 
    /* temporary global design */
    dsp_init_lpf( &__lpf_mud_free, 125.0f );
index 4a1e9ae560e537e1d3c804f28cd96c1ac25960ac..219b971bc76718720a9816fc155d6a3040ffedff 100644 (file)
@@ -1,11 +1,9 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_audio_dsp.c"
+#else
 
 //#define VG_ECHO_LPF_BUTTERWORTH
 
-#include "vg_platform.h"
-#include "vg_opengl.h"
-#include "vg_m.h"
-
 struct vg_dsp
 {
    float     *buffer;
@@ -44,8 +42,7 @@ struct dsp_biquad
        xnz1, xnz2, ynz1, ynz2, offset;
 };
 
-void vg_dsp_init( void );
-void vg_dsp_free( void );
+VG_API void _vg_dsp_init(void);
 void dsp_update_tunings(void);
 void vg_dsp_process( float *stereo_in, float *stereo_out );
 
@@ -59,5 +56,6 @@ void dsp_init_lpf( struct dsp_lpf *lpf, float freq );
 void dsp_write_lpf( struct dsp_lpf *lpf, float *s );
 void dsp_read_lpf( struct dsp_lpf *lpf, float *s );
 void dsp_init_schroeder( struct dsp_schroeder *sch, float length, float gain );
-void dsp_process_schroeder( struct dsp_schroeder *sch,
-                            float *input, float *output );
+void dsp_process_schroeder( struct dsp_schroeder *sch, float *input, float *output );
+
+#endif
index eecad3bd5d414a9e459995153dbad5eee96cd148..71d8519dd659b03d41fa637b4d85ac18b114a14f 100644 (file)
@@ -1,6 +1,3 @@
-#include "vg_audio_synth_bird.h"
-#include "vg_binstr.h"
-
 #define DEFAULT_VOL 1.0f,0.5f,0.2f,0.125f
 #define DEFAULT_TONES { {1,1}, {6,5}, {8,7}, {13,12} }
 #define DEFAULT_RISE 0.00090702947f
@@ -151,9 +148,9 @@ u32 synth_bird_get_length_in_samples( struct synth_bird *bird )
 {
    u32 total = 0;
 
-   for( int i=0; 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;
 
@@ -178,20 +175,20 @@ void synth_bird_reset( struct synth_bird *bird )
 
    bird->rt.fundamental = 0.0f;
    bird->rt.x = 0;
-   bird->rt.length = bird->settings.pattern[0].length * (float)BIRD_SAMPLE_RATE;
+   bird->rt.length = bird->settings->pattern[0].length * (float)BIRD_SAMPLE_RATE;
    bird->rt.gate = 1;
    bird->rt.adsr = 0;
    bird->rt.frame = 0;
    bird->rt.lfo_hz = 0;
    bird->rt.fm_depth = 0.0f;
 
-   bird->rt.adsr_rise = bird->settings.adsr_rise * (float)BIRD_SAMPLE_RATE;
-   bird->rt.adsr_fall = bird->settings.adsr_fall * (float)BIRD_SAMPLE_RATE;
+   bird->rt.adsr_rise = bird->settings->adsr_rise * (float)BIRD_SAMPLE_RATE;
+   bird->rt.adsr_fall = bird->settings->adsr_fall * (float)BIRD_SAMPLE_RATE;
 }
 
 static u32 synth_bird_save_size( struct synth_bird *bird )
 {
-   return sizeof(struct synth_bird_signature) * bird->settings.pattern_length +
+   return sizeof(struct synth_bird_signature) * bird->settings->pattern_length +
           sizeof(struct synth_bird_settings);
 }
 
@@ -232,8 +229,8 @@ static struct synth_bird *synth_bird_create(
 
    bird->settings = *settings;
    
-   memcpy( bird->settings.pattern, pattern, pattern_size );
-   bird->settings.pattern_length = pattern_length;
+   memcpy( bird->settings->pattern, pattern, pattern_size );
+   bird->settings->pattern_length = pattern_length;
 
    synth_bird_reset( bird );
    return bird;
@@ -243,7 +240,7 @@ static struct synth_bird *synth_bird_create(
 
 static void synth_bird_think( struct synth_bird *bird )
 {
-   struct synth_bird_signature *sig = &bird->settings.pattern[ bird->rt.frame ];
+   struct synth_bird_signature *sig = &bird->settings->pattern[ bird->rt.frame ];
 
    bird->rt.x ++;
    if( bird->rt.x >= bird->rt.length )
@@ -257,10 +254,10 @@ static void synth_bird_think( struct synth_bird *bird )
       {
          bird->rt.frame ++;
 
-         if( bird->rt.frame >= bird->settings.pattern_length )
+         if( bird->rt.frame >= bird->settings->pattern_length )
             bird->rt.frame = 0;
 
-         sig = &bird->settings.pattern[ bird->rt.frame ];
+         sig = &bird->settings->pattern[ bird->rt.frame ];
          
          bird->rt.gate = 1;
          bird->rt.length = sig->length * (float)BIRD_SAMPLE_RATE;
@@ -323,10 +320,10 @@ void synth_bird_generate_samples( struct synth_bird *bird,
       int freq = bird->rt.fundamental + fm;
       int hz[4] =
       {
-         (freq * bird->settings.tones[0][0]) / bird->settings.tones[0][1],
-         (freq * bird->settings.tones[1][0]) / bird->settings.tones[1][1],
-         (freq * bird->settings.tones[2][0]) / bird->settings.tones[2][1],
-         (freq * bird->settings.tones[3][0]) / bird->settings.tones[3][1],
+         (freq * bird->settings->tones[0][0]) / bird->settings->tones[0][1],
+         (freq * bird->settings->tones[1][0]) / bird->settings->tones[1][1],
+         (freq * bird->settings->tones[2][0]) / bird->settings->tones[2][1],
+         (freq * bird->settings->tones[3][0]) / bird->settings->tones[3][1],
       };
 
       int_add_mod( bird->rt.osc_main + 0, hz[0] );
@@ -348,8 +345,6 @@ void synth_bird_generate_samples( struct synth_bird *bird,
 }
 
 #ifdef SYNTH_BIRD_STDLIB
-#include "stdio.h"
-
 #define KNRM  "\x1B[00m"
 #define KRED  "\x1B[31m"
 #define KGRN  "\x1B[32m"
index 0a2f418a2ba4d373c5bbaf83e93be5b2e72fd194..ed1f973f7d0f68cfbf12db3116af84c4a31b6d11 100644 (file)
@@ -1,4 +1,6 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_audio_synth_bird.c"
+#else
 
 #ifndef BIRD_SAMPLE_RATE
  #define BIRD_SAMPLE_RATE 44100
@@ -16,7 +18,8 @@ struct synth_bird_signature
 
 struct synth_bird
 {
-   struct{
+   struct
+   {
       int osc_main[4],
           osc_lfo;
       float volume[4];
@@ -34,7 +37,8 @@ struct synth_bird
    }
    rt;
 
-   struct synth_bird_settings{
+   struct synth_bird_settings
+   {
       int tones[4][2];        /* fraction of the fundemental tone
                                  for each oscillator */
       float adsr_rise,        /* rise/fall in seconds */
@@ -49,10 +53,11 @@ struct synth_bird
       int pattern_length;
       struct synth_bird_signature pattern[];
    } 
-   settings;
+   *settings;
 };
 
 void synth_bird_reset( struct synth_bird *bird );
 u32 synth_bird_get_length_in_samples( struct synth_bird *bird );
-void synth_bird_generate_samples( struct synth_bird *bird,
-                                  float *stereo_buffer, int samples );
+void synth_bird_generate_samples( struct synth_bird *bird, float *stereo_buffer, int samples );
+
+#endif
index 7d9ac83575e0d5636f00c2d15fad353e313176e7..7047f15e92950074e785ef9f5e2bb599f2aea1bc 100644 (file)
@@ -1,5 +1,3 @@
-#include "vg/vg_binstr.h"
-
 void vg_str_bin( const void *txt, void *bin, int size )
 {
    const u8 *src = txt;
index 6e662fe3f33dd11f04e74b813fdd6297fd2ff675..5a2ec1e9f6d8d97b62abd098e18ad518be76e2d6 100644 (file)
@@ -1,10 +1,11 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_binstr.c"
+#else
 
 /* dead simple.. 4 bits/character encoding */
-
-#include "vg_platform.h"
-
 #define VG_BINSTR_BASECHAR 0x41
 
 void vg_str_bin( const void *txt, void *bin, int size );
 void vg_bin_str( const void *bin, void *txt, u32 size );
+
+#endif
index ccf93e96fd4b56a3e8fce66756804c7258deb3d4..86705fcc2987f100566fb219a8e0eccfeef3369b 100644 (file)
@@ -1,16 +1,6 @@
-#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
 {
@@ -55,23 +45,13 @@ struct vg_compiler_env
    }
    libc;
 }
-vg_test_env = 
+_vg_common_env =
 {
    .optimization = 0,
    .debug_asan = 1,
    .thread_san = 0,
    .platform = k_platform_linux,
    .arch = k_architecture_x86_64,
-   .compiler = k_compiler_clang,
-   .libc = k_libc_version_native
-},
-vg_release_env = 
-{
-   .optimization = 3,
-   .debug_asan = 0,
-   .thread_san = 0,
-   .platform = k_platform_anyplatform,
-   .arch = k_architecture_x86_64,
    .compiler = k_compiler_zigcc,
    .libc = k_libc_version_2_23
 };
@@ -97,28 +77,28 @@ enum obj_type
  * string tables 
  * -------------------------------------------------------------------------- */
 
-static const char *platform_names[] = 
+static const c8 *platform_names[] = 
 {
    [k_platform_anyplatform] = "anyplatform",
    [k_platform_windows]     = "windows",
    [k_platform_linux]       = "linux",
 };
 
-static const char *architecture_names[] = 
+static const c8 *architecture_names[] = 
 {
    [k_architecture_anyarch] = "anyarch",
    [k_architecture_i386]    = "i386",
    [k_architecture_x86_64]  = "x86_64",
 };
 
-static const char *compiler_names[] = 
+static const c8 *compiler_names[] = 
 {
    [k_compiler_blob]    = "blob",
    [k_compiler_clang]   = "clang",
    [k_compiler_zigcc]   = "zig-cc",
 };
 
-static const char *libc_names[] = 
+static const c8 *libc_names[] = 
 {
    [k_libc_version_native]  = "",
    [k_libc_version_2_23]    = ".2.23"
@@ -128,7 +108,7 @@ static const char *libc_names[] =
  * OS & file tools
  * -------------------------------------------------------------------------- */
 
-void vg_syscall( const char *fmt, ... )
+void vg_syscall( const c8 *fmt, ... )
 {
    va_list args;
    va_start( args, fmt );
@@ -142,13 +122,12 @@ void vg_syscall( const char *fmt, ... )
       exit(1);
 }
 
-void vg_add_blob( struct vg_project *proj, const char *blob, const char *dest )
+void vg_add_blob( struct vg_project *proj, const c8 *blob, const c8 *dest )
 {
    vg_syscall( "cp %s %s/%s", blob, proj->bin_folder.buffer, dest );
 }
 
-void vg_symlink( struct vg_project *proj,
-                 const char *folder, const char *bin_name )
+void vg_symlink( struct vg_project *proj, const c8 *folder, const c8 *bin_name )
 {
    char dest[512];
    snprintf( dest, 512, "%s/%s", proj->bin_folder.buffer, bin_name );
@@ -159,14 +138,7 @@ void vg_symlink( struct vg_project *proj,
 
 void vg_tarball_project( struct vg_project *proj )
 {
-   vg_syscall( "tar -chzvf dist/%s-%u.tar.gz %s/",
-               proj->uid.buffer, time(NULL), proj->bin_folder.buffer );
-}
-
-bool vg_platform_posix( enum platform p )
-{
-   if( p == k_platform_linux ) return 1;
-   else return 0;
+   vg_syscall( "tar -chzvf dist/%s-%u.tar.gz %s/", proj->uid.buffer, time(NULL), proj->bin_folder.buffer );
 }
 
 /*
@@ -178,8 +150,8 @@ bool vg_platform_posix( enum platform p )
  *  CLEAR IF fresh
  */
 void vg_project_init( struct vg_project *proj, 
-                      const char *folder,
-                      const char *name,
+                      const c8 *folder,
+                      const c8 *name,
                       struct vg_compiler_env *env,
                       bool fresh )
 {
@@ -217,14 +189,21 @@ struct compile_result
           rel_path;
 };
 
+bool compiler_supports_sanitizers( enum compiler compiler )
+{
+   if( compiler == k_compiler_clang ) return 1;
+   if( compiler == k_compiler_zigcc ) return 1;
+   return 0;
+}
+
 /* run a compiler.. return compiled object relative to project folder
  */
 struct compile_result 
 vg_compiler_run( struct vg_project *project, 
                  struct vg_compiler_env *env,
                  struct vg_compiler_conf *conf,
-                 const char *sources,
-                 const char *target_name,
+                 const c8 *sources,
+                 const c8 *target_name,
                  enum obj_type type )
 {
    /* check for problems in configuration */
@@ -295,16 +274,16 @@ vg_compiler_run( struct vg_project *project,
       vg_strcat( &cmd, "\\\n" );
    }
 
-   if( (env->compiler == k_compiler_clang) && env->debug_asan )
-      vg_strcat( &cmd, "  -rdynamic -fsanitize=undefined -fsanitize=address -fPIE -fstack-protector-strong " );
+   if( compiler_supports_sanitizers(env->compiler) && env->debug_asan )
+      vg_strcat( &cmd, "  -lasan -rdynamic -fsanitize=address -fPIE -fstack-protector-strong " );
 
-   if( (env->compiler == k_compiler_clang) && env->thread_san )
-      vg_strcat( &cmd, "  -rdynamic -fsanitize=thread -fPIE -fstack-protector-strong " );
+   if( compiler_supports_sanitizers(env->compiler) && env->thread_san )
+      vg_strcat( &cmd, "  -lasan -rdynamic -fsanitize=thread -fPIE -fstack-protector-strong " );
 
    vg_strcat( &cmd, conf->no_lto? " -fno-lto \\\n": " -flto \\\n" );
 
    /* want a lot of warnings but not useless ones */
-   vg_strcat( &cmd, "  -Wall -ferror-limit=10\\\n"
+   vg_strcat( &cmd, "  -Wall -ferror-limit=5000\\\n"
       "    -Wno-unused-function -Wno-unused-variable\\\n"
       "    -Wno-unused-command-line-argument -Wno-unused-but-set-variable\\\n"
    );
@@ -355,7 +334,7 @@ vg_compiler_run( struct vg_project *project,
          vg_strcat( &res.rel_path, ".obj" );
    }
 
-   if( vg_platform_posix( env->platform ) )
+   if( env->platform == k_platform_linux )
    {
       if( type == k_obj_type_shared )
          vg_strcat( &res.rel_path, ".so" );
@@ -396,8 +375,8 @@ struct vg_engine_config
         log_source_info, 
         steam_api,
         custom_game_settings,
-        custom_shaders,
-        multiplayer;
+        multiplayer,
+        release_mode;
    i32 fixed_update_hz;
 }
 vg_engine_default_config = 
@@ -408,8 +387,8 @@ vg_engine_default_config =
    .log_source_info = 1,
    .steam_api = 0,
    .custom_game_settings = 0,
-   .custom_shaders = 0,
-   .multiplayer = 0
+   .multiplayer = 0,
+   .release_mode = 0
 };
 
 struct compile_result
@@ -417,8 +396,8 @@ vg_make_app( struct vg_project *proj,
              struct vg_engine_config *vg_conf,
              struct vg_compiler_env *env,
              struct vg_compiler_conf *conf,
-             const char *sources,
-             const char *appname )
+             const c8 *sources,
+             const c8 *appname )
 {
    struct vg_project vg_proj;
    vg_project_init( &vg_proj, "bin", ".vg", env, 0 );
@@ -428,12 +407,13 @@ vg_make_app( struct vg_project *proj,
 
    /* add config defines to compiler config */
    if( !vg_conf ) vg_conf = &vg_engine_default_config;
+
    vg_strcat( &conf->defines, vg_conf->use_3d? "-DVG_3D \\\n": "-DVG_2D \\\n" );
-   vg_strcatf( &conf->defines, "-DVG_TIMESTEP_FIXED=\"(1.0/%d.0)\" \\\n",
-               vg_conf->fixed_update_hz );
+   vg_strcatf( &conf->defines, "-DVG_TIMESTEP_FIXED=\"(1.0/%d.0)\" \\\n", vg_conf->fixed_update_hz );
+   vg_strcat( &conf->defines, "-DVG_MULTITHREAD -DVG_CONSOLE -DVG_MATH \\\n" );
 
    if( vg_conf->legacy_support_vg_msg1 )
-      vg_strcat( &conf->defines, "-DVG_MSG_V1_SUPPORT \\\n" );
+      vg_strcat( &conf->defines, "-DVG_MSG_TO_KVS \\\n" );
 
    if( vg_conf->log_source_info )
       vg_strcat( &conf->defines, "-DVG_LOG_SOURCE_INFO \\\n" );
@@ -441,17 +421,20 @@ vg_make_app( struct vg_project *proj,
    if( vg_conf->custom_game_settings )
       vg_strcat( &conf->defines, "-DVG_GAME_SETTINGS \\\n" );
 
-   if( vg_conf->custom_shaders )
-      vg_strcat( &conf->defines, "-DVG_CUSTOM_SHADERS \\\n" );
-
    if( vg_conf->multiplayer )
       vg_strcat( &conf->defines, "-DVG_MULTIPLAYER \\\n" );
 
+   if( vg_conf->release_mode )
+      vg_strcat( &conf->defines, "-DVG_RELEASE_MODE \\\n" );
+
    vg_strcat( &conf->defines, "-DVG_ENGINE \\\n" );
 
    vg_strcat( &conf->defines, "\\\n" );
    vg_strcat( &conf->include, "-I. -I./vg -I./vg/dep " );
 
+   vg_strcat( &conf->library, "-L. -L/usr/lib/x86_64-linux-gnu " );
+   vg_strcat( &conf->link, "-lm " );
+
    /* compile all the components 
     * ----------------------------------------------------------------------- */
    vg_str components = {0};
@@ -461,25 +444,18 @@ vg_make_app( struct vg_project *proj,
    //denv.optimization = 3;
 
    /* external dependencies */
-   struct compile_result depencies = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_depencies.c",
-                                                      "vg_deps", k_obj_type_obj );
+   struct compile_result depencies = vg_compiler_run( &vg_proj, &denv, conf, "vg/unit_thirdparty.c", "vg_deps", k_obj_type_obj );
    vg_strcatf( &components, "%s ", depencies.path );
 
-   /* glad */
-   struct compile_result glad = vg_compiler_run( &vg_proj, &denv, conf, "vg/dep/glad/glad.c",
-                                                 "vg_glad", k_obj_type_obj );
-   vg_strcatf( &components, "%s ", glad.path );
-
    /* core engine */
-   struct compile_result vg = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_engine.c",
-                                               "vg_engine_core", k_obj_type_obj );
+   struct compile_result vg = vg_compiler_run( &vg_proj, &denv, conf, "vg/unit_engine.c", "vg_engine_core", k_obj_type_obj );
    vg_strcatf( &components, "%s ", vg.path );
 
    /* steamworks */
    if( vg_conf->steam_api )
    {
-      struct compile_result steam = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_steam2.c", "vg_steam", k_obj_type_obj );
-      vg_strcatf( &components, "%s ", steam.path );
+      //struct compile_result steam = vg_compiler_run( &vg_proj, &denv, conf, "vg/vg_steam2.c", "vg_steam", k_obj_type_obj );
+      //vg_strcatf( &components, "%s ", steam.path );
 
       if( env->platform == k_platform_linux )
       {
@@ -496,8 +472,8 @@ vg_make_app( struct vg_project *proj,
    }
 
    /* link */
-   vg_strcat( &conf->library, "-L. -L/usr/lib " );
-   vg_strcat( &conf->link, "-lm " );
+   /* TODO: Could be /usr/lib/x86_64-linux-gnu or /usr/lib 
+    */
 
    if( env->platform == k_platform_linux )
    {
@@ -525,3 +501,33 @@ void vg_add_controller_database( struct vg_project *proj )
 {
    vg_add_blob( proj, "vg/submodules/SDL_GameControllerDB/gamecontrollerdb.txt", "" );
 }
+
+void vg_build_scripts(void);
+int main( int argc, const c8 *argv[] )
+{
+   _vg_log_pre_init();
+   _vg_opt_init( argc, argv );
+
+   if( vg_long_opt( "windows", "Build for windows" ) )
+      _vg_common_env.platform = k_platform_windows;
+
+   if( vg_long_opt( "tsan", "Use Thread Sanitizer instead of Adress Sanitizer" ) )
+   {
+      _vg_common_env.debug_asan = 0;
+      _vg_common_env.thread_san = 1;
+   }
+
+   if( vg_opt( 'O', "Build with optimisations" ) )
+   {
+      _vg_common_env.optimization = 3;
+      _vg_common_env.debug_asan = 0;
+      _vg_common_env.thread_san = 0;
+   }
+
+   vg_build_scripts();
+
+   if( !_vg_opt_check() )
+      return 0;
+}
+
+#endif
index 84af4487fdbf16b3e73fc9c5c520b40c8b1efac3..6f27360f28512842df3aeba4c290be22294038ac 100644 (file)
@@ -1,10 +1,5 @@
-#include "vg_font.h"
-
-#define STB_IMAGE_IMPLEMENTATION
-#include "submodules/stb/stb_image.h"
-
-void vg_build_font_face_run( vg_font_face *face,
-                             char first, char last, i16 x, i16 y )
+#if defined( VG_IMPLEMENTATION )
+void vg_build_font_face_run( vg_font_face *face, char first, char last, i16 x, i16 y )
 {
    u32 uf = *((u8 *)&first),
        ul = *((u8 *)&last);
@@ -69,7 +64,7 @@ void vg_build_font_sheet( FILE *fp, char *name, const char *source )
       u32 buff = 0;
       for( int b = 31; b >= 0; b-- )
       {
-         buff |= data[pixel*4]>128?0x1<<b:0;
+         buff |= (data[pixel*4] > 128)? (0x1u<<(u32)b): 0;
          pixel++;
          
          if( pixel >= pixel_max )
@@ -158,3 +153,4 @@ void vg_build_default_font(void)
 
    fclose( fp );
 }
+#endif
index 8376490bc4a96d338d12be3cf2216ba60979e93d..dc452fb4c4ffe6f96523f964037124bb4bf09cb3 100644 (file)
-#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
index 9127be5bb1949aafc2aa24cb599a0f27cf5f16ed..9da3fad47366e3657efc35a161c001aacbed14c1 100644 (file)
--- a/vg_bvh.c
+++ b/vg_bvh.c
@@ -1,22 +1,17 @@
-#include "vg_bvh.h"
-#include "vg_mem.h"
-#include "vg_m.h"
-#include "vg_lines.h"
-#include "vg_log.h"
-
-void bh_update_bounds( bh_tree *bh, u32 inode ){
+static void bh_update_bounds( bh_tree *bh, u32 inode )
+{
    bh_node *node = &bh->nodes[ inode ];
-
    box_init_inf( node->bbx );
-   for( u32 i=0; 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;
 
@@ -40,26 +35,26 @@ void bh_subdivide( bh_tree *bh, u32 inode ){
    i32 i = node->start,
        j = i + node->count-1;
    
-   while( i <= j ){
+   while( i <= j )
+   {
       f32 centroid = bh->system->item_centroid( bh->user, i, axis );
-
       if( centroid < split )
          i ++;
-      else{
+      else
+      {
          bh->system->item_swap( bh->user, i, j );
          j --;
       }
    }
 
    u32 left_count = i - node->start;
-   if( left_count == 0 || left_count == node->count ) return;
+   if( left_count == 0 || left_count == node->count ) 
+      return;
 
    u32 il = bh->node_count ++,
        ir = bh->node_count ++;
-
    bh_node *lnode = &bh->nodes[il],
            *rnode = &bh->nodes[ir];
-
    lnode->start = node->start;
    lnode->count = left_count;
    rnode->start = i;
@@ -75,7 +70,7 @@ void bh_subdivide( bh_tree *bh, u32 inode ){
    bh_subdivide( bh, ir );
 }
 
-void bh_rebuild( bh_tree *bh, u32 item_count )
+static void bh_rebuild( bh_tree *bh, u32 item_count )
 {
    bh_node *root = &bh->nodes[0];
    bh->node_count = 1;
@@ -91,31 +86,25 @@ void bh_rebuild( bh_tree *bh, u32 item_count )
       bh_subdivide( bh, 0 );
 }
 
-bh_tree *bh_create( vg_stack_allocator *stack, bh_system *system, void *user, u32 item_count, u32 max_per_leaf )
+VG_TIER_1 void bh_create( bh_tree *bh, bh_system *system, void *user, u32 item_count, 
+                          u32 max_per_leaf, vg_stack_allocator *stack )
 {
-   u32 alloc_count = VG_MAX( 1, item_count );
+   vg_zero_mem( bh, sizeof(bh_tree) );
 
-   u32 totsize = sizeof(bh_tree) + sizeof(bh_node)*(alloc_count*2-1);
-   bh_tree *bh = stack? vg_stack_allocate( stack, totsize, 8, "BVH Tree" ): vg_malloc( totsize );
+   u32 alloc_count = VG_MAX( 1, item_count );
+   i32 max_size =  sizeof(bh_node)*(alloc_count*2-1);
+   bh->nodes = vg_stack_allocate( stack, max_size, 8, "BVH Tree" );
    bh->system = system;
    bh->user = user;
    bh->max_per_leaf = max_per_leaf;
    bh_rebuild( bh, item_count );
 
-   totsize = sizeof(bh_tree) + sizeof(bh_node) * bh->node_count;
-
-   if( stack ) bh = vg_stack_resize_last( stack, totsize );
-   else        bh = vg_realloc( bh, totsize );
-
+   i32 used_size = sizeof(bh_node) * bh->node_count;
+   vg_stack_extend_last( stack, used_size - max_size );
    vg_success( "BVH done, size: %u/%u\n", bh->node_count, (alloc_count*2-1) );
-   return bh;
 }
 
-/*
- * Draw items in this leaf node.
- * *item_debug() must be set!
- */
-void bh_debug_leaf( bh_tree *bh, bh_node *node )
+VG_TIER_0 void bh_debug_leaf( bh_tree *bh, bh_node *node )
 {
    vg_line_boxf( node->bbx, 0xff00ff00 );
 
@@ -132,27 +121,25 @@ void bh_debug_leaf( bh_tree *bh, bh_node *node )
 /*
  * Trace the bh tree all the way down to the leaf nodes where pos is inside
  */
-void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour )
+VG_TIER_0 void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour )
 {
    bh_node *node = &bh->nodes[ inode ];
-
    if( (pos[0] >= node->bbx[0][0] && pos[0] <= node->bbx[1][0]) &&
        (pos[2] >= node->bbx[0][2] && pos[2] <= node->bbx[1][2]) )
    {
-      if( !node->count ){
+      if( !node->count )
+      {
          vg_line_boxf( node->bbx, colour );
-
          bh_debug_trace( bh, node->il, pos, colour );
          bh_debug_trace( bh, node->ir, pos, colour );
       }
-      else{
+      else
          if( bh->system->item_debug )
             bh_debug_leaf( bh, node );
-      }
    }
 }
 
-void bh_iter_init_generic( i32 root, bh_iter *it )
+VG_TIER_0 void bh_iter_init_generic( i32 root, bh_iter *it )
 {
    it->stack[0].id = root;
    it->stack[0].depth = 0;
@@ -160,25 +147,23 @@ void bh_iter_init_generic( i32 root, bh_iter *it )
    it->i = 0;
 }
 
-void bh_iter_init_box( i32 root, bh_iter *it, boxf box )
+VG_TIER_0 void bh_iter_init_box( i32 root, bh_iter *it, boxf box )
 {
    bh_iter_init_generic( root, it );
    it->query = k_bh_query_box;
-
    box_copy( box, it->box.box );
 }
 
-void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist )
+VG_TIER_0 void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist )
 {
    bh_iter_init_generic( root, it );
    it->query = k_bh_query_ray;
-   
    v3_div( (v3f){1.0f,1.0f,1.0f}, dir, it->ray.inv_dir );
    v3_copy( co, it->ray.co );
    it->ray.max_dist = max_dist;
 }
 
-void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range )
+VG_TIER_0 void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range )
 {
    bh_iter_init_generic( root, it );
    it->query = k_bh_query_range;
@@ -187,14 +172,10 @@ void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range )
    it->range.dist_sqr = range*range;
 }
 
-/* NOTE: does not compute anything beyond the leaf level. element level tests
- *       should be implemented by the users code.
- *
- *       this is like a 'broad phase only' deal.
- */
-i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em )
+VG_TIER_0 i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em )
 {
-   while( it->depth >= 0 ){
+   while( it->depth >= 0 )
+   {
       bh_node *inode = &bh->nodes[ it->stack[it->depth].id ];
       
       /* Only process overlapping nodes */
@@ -202,39 +183,45 @@ i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em )
 
       if( it->i ) /* already checked */
          q = 1;
-      else{
+      else
+      {
          if( it->query == k_bh_query_box )
             q = box_overlap( inode->bbx, it->box.box );
          else if( it->query == k_bh_query_ray )
-            q = ray_aabb1( inode->bbx, it->ray.co, 
-                           it->ray.inv_dir, it->ray.max_dist );
-         else {
+            q = ray_aabb1( inode->bbx, it->ray.co, it->ray.inv_dir, it->ray.max_dist );
+         else 
+         {
             v3f nearest;
             closest_point_aabb( it->range.co, inode->bbx, nearest );
-
             if( v3_dist2( nearest, it->range.co ) <= it->range.dist_sqr )
                q = 1;
          }
       }
 
-      if( !q ){
+      if( !q )
+      {
          it->depth --;
          continue;
       }
 
-      if( inode->count ){
-         if( it->i < inode->count ){
+      if( inode->count )
+      {
+         if( it->i < inode->count )
+         {
             *em = inode->start+it->i;
             it->i ++;
             return 1;
          }
-         else{
+         else
+         {
             it->depth --;
             it->i = 0;
          }
       }
-      else{
-         if( it->depth+1 >= VG_ARRAY_LEN(it->stack) ){
+      else
+      {
+         if( it->depth+1 >= VG_ARRAY_LEN(it->stack) )
+         {
             vg_error( "Maximum stack reached!\n" );
             return 0;
          }
@@ -245,39 +232,43 @@ i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em )
          it->i = 0;
       }
    }
-
    return 0;
 }
 
-int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist )
+VG_TIER_0 i32 bh_closest_point( bh_tree *bh, v3f pos, v3f closest, f32 max_dist )
 {
    if( bh->node_count < 2 )
       return -1;
 
    max_dist = max_dist*max_dist;
 
-   int queue[ 128 ],
+   i32 queue[ 128 ],
        depth = 0,
        best_item = -1;
 
    queue[0] = 0;
 
-   while( depth >= 0 ){
+   while( depth >= 0 )
+   {
       bh_node *inode = &bh->nodes[ queue[depth] ];
 
       v3f p1;
       closest_point_aabb( pos, inode->bbx, p1 );
 
       /* branch into node if its closer than current best */
-      float node_dist = v3_dist2( pos, p1 );
-      if( node_dist < max_dist ){
-         if( inode->count ){
-            for( int i=0; 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;
@@ -286,10 +277,10 @@ int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist )
 
             depth --;
          }
-         else{
+         else
+         {
             queue[depth] = inode->il;
             queue[depth+1] = inode->ir;
-
             depth ++;
          }
       }
index 3592bc64e174851dc6bcd1e69200d75cf7c90adb..ec1bed34111c56b092a124a250234394a1726be1 100644 (file)
--- a/vg_bvh.h
+++ b/vg_bvh.h
@@ -1,68 +1,51 @@
-#pragma once
-#include "vg_platform.h"
-#include "vg_mem.h"
-#include "vg_m.h"
-#include "vg_lines.h"
-
-/*
- * Usage:
- *
- * create a bh_system with functions filled out for expand, centroid, and swap.
- * optionally include item_debug and cast_ray functions if needed, otherwise,
- *   set them to null
- *
- * create a bh_tree struct with:
- *   user: a pointer back the base of the data you are ordering
- *   system: the system we created above which will deal with the data
- *
- * call bh_create( bh_tree *bh, u32 item_count )
- * static int bh_ray( bh_tree *bh, u32 inode, v3f co, v3f dir, ray_hit *hit )
- * static int bh_select( bh_tree *bh, boxf box, u32 *buffer, int len )
- */
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_bvh.c"
+#else
 
 typedef struct bh_node bh_node;
 typedef struct bh_tree bh_tree;
 typedef struct bh_system bh_system;
 
 typedef struct ray_hit ray_hit;
-struct ray_hit{
-   float dist;
+struct ray_hit
+{
+   f32 dist;
    u32 *tri;
    v3f pos, normal;
 };
 
-struct bh_tree{
+struct bh_tree
+{
    u32 node_count;
 
    bh_system *system;
    void *user;
    u32 max_per_leaf;
 
-   struct bh_node{
+   struct bh_node
+   {
       boxf bbx;
 
       /* if il is 0, this is a leaf */
-      int il, count;
-      union{ int ir, start; };
+      i32 il, count;
+      union{ i32 ir, start; };
    } 
-   nodes[];
+   *nodes;
 };
 
-/*
- * TODO: This might get ugly, but it would make sense to do codegen here
- *
- *       eg.. #define BH_SYSTEM( FN_EXPAND, FN_CENTROID, FN_CLOSEST ... )
- *
- *       which macros out variants of the functions so we can give the compier
- *       a more solid thing to work with without function pointers inside 
- *       hot loops.
- */
+typedef void (*bh_expand_fn)  ( void *user, boxf bound, u32 item_index );
+typedef f32  (*bh_centroid_fn)( void *user, u32 item_index, i32 axis );
+typedef void (*bh_closest_fn) ( void *user, u32 item_index, v3f point, v3f closest );
+typedef void (*bh_swap_fn)    ( void *user, u32 ia, u32 ib );
+typedef void (*bh_debug_fn)   ( void *user, u32 item_index );
+typedef void (*bh_raycast_fn) ( void *user, u32 index, v3f co, v3f dir, ray_hit *hit );
 
-struct bh_system{
-   void  (*expand_bound)( void *user, boxf bound, u32 item_index );
-   float (*item_centroid)( void *user, u32 item_index, int axis );
-   void  (*item_closest)( void *user, u32 item_index, v3f point, v3f closest );
-   void  (*item_swap)( void *user, u32 ia, u32 ib );
+struct bh_system
+{
+   bh_expand_fn expand_bound;
+   bh_centroid_fn item_centroid;
+   bh_closest_fn  item_closest;
+   bh_swap_fn     item_swap;
 
    /*
     * Optional:
@@ -70,52 +53,53 @@ struct bh_system{
     *   cast_ray     - shoot a ray against the object, if this is not set,
     *                  raycasts will simply return the hit on the bvh node
     */
-
-   void  (*item_debug)( void *user, u32 item_index );
-   int   (*cast_ray)( void *user, u32 index, v3f co, v3f dir, ray_hit *hit );
+   bh_debug_fn item_debug;
+   bh_raycast_fn cast_ray;
 };
 
-void bh_update_bounds( bh_tree *bh, u32 inode );
-void bh_subdivide( bh_tree *bh, u32 inode );
-void bh_rebuild( bh_tree *bh, u32 item_count );
-
-bh_tree *bh_create( vg_stack_allocator *stack, bh_system *system, void *user, u32 item_count, u32 max_per_leaf );
-
-
-void bh_debug_leaf( bh_tree *bh, bh_node *node );
+VG_TIER_1 void bh_create( bh_tree *bh, bh_system *system, void *user, u32 item_count, 
+                          u32 max_per_leaf, vg_stack_allocator *stack );
+VG_TIER_0 void bh_debug_leaf( bh_tree *bh, bh_node *node );
 
 /*
  * Trace the bh tree all the way down to the leaf nodes where pos is inside
  */
-void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour );
+VG_TIER_0 void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour );
 
 typedef struct bh_iter bh_iter;
-struct bh_iter{
-   struct {
+struct bh_iter
+{
+   struct 
+   {
       i32 id, depth;
    }
    stack[64];
 
-   enum bh_query_type{
+   enum bh_query_type
+   {
       k_bh_query_box,
       k_bh_query_ray,
       k_bh_query_range
    }
    query;
 
-   union{
-      struct{
+   union
+   {
+      struct
+      {
          boxf box;
       }
       box;
 
-      struct{
+      struct
+      {
          v3f co, inv_dir;
          f32 max_dist;
       }
       ray;
 
-      struct {
+      struct 
+      {
          v3f co;
          f32 dist_sqr;
       }
@@ -125,9 +109,11 @@ struct bh_iter{
    i32 depth, i;
 };
 
-void bh_iter_init_generic( i32 root, bh_iter *it );
-void bh_iter_init_box( i32 root, bh_iter *it, boxf box );
-void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist );
-void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range );
-i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em );
-int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist );
+VG_TIER_0 void bh_iter_init_generic( i32 root, bh_iter *it );
+VG_TIER_0 void bh_iter_init_box( i32 root, bh_iter *it, boxf box );
+VG_TIER_0 void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, v3f dir, f32 max_dist );
+VG_TIER_0 void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range );
+VG_TIER_0 i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em );
+VG_TIER_0 int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist );
+
+#endif
index cb44a56794c2e9614c500df5e726354b90649ee5..5bb432f5937e1efa51f879ebdd6b521e737983b0 100644 (file)
@@ -1,7 +1,3 @@
-#include "vg_camera.h"
-#include "vg_m.h"
-#include "vg_engine.h"
-
 void vg_camera_lerp_angles( v3f a, v3f b, f32 t, v3f d )
 {
    d[0] = vg_alerpf( a[0], b[0], t );
index a0846b0fcc1ec4e4edb8783ba8bd30dea63d119b..c4f88f643af8e71263e0a84ac47796e90c8e8684 100644 (file)
@@ -1,6 +1,6 @@
-#pragma once
-
-#include "vg_m.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_camera.c"
+#else
 
 typedef struct vg_camera vg_camera;
 struct vg_camera
@@ -49,3 +49,5 @@ void vg_camera_update_projection( vg_camera *cam, f32 vw, f32 vh );
  * 4) [projection matrix, view matrix] -> previous pv, new pv 
  */
 void vg_camera_finalize( vg_camera *cam );
+
+#endif
index cd7d07c27b49026df92db61ea20995ac213e98a2..4454630666747e7c0a71c2873d637af87568c308 100644 (file)
@@ -1,20 +1,10 @@
-#ifdef VG_ENGINE
-#include "vg_engine.h"
-#include "vg_ui/imgui.h"
-#endif
-
-#include "vg_console.h"
-#include "vg_log.h"
-#include "vg_string.h"
-#include <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) );
 
@@ -23,15 +13,14 @@ void vg_console_reg_var( const char *alias, void *ptr, enum vg_var_dtype type, u
    var->data = ptr;
    var->data_type = type;
    var->flags = flags;
-
-   vg_info( "Console variable '%s' registered\n", alias );
 }
 
 void vg_console_reg_cmd( const char *alias, int (*function)(int argc, const char *argv[]),
                                             void (*poll_suggest)(int argc, const char *argv[]) )
 {
-#ifdef VG_ENGINE
+#if defined( VG_ENGINE )
    VG_ASSERT( vg_console.registration_blocked==0 );
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) );
 #endif
    VG_ASSERT( vg_console.function_count < VG_ARRAY_LEN(vg_console.functions) );
 
@@ -40,8 +29,6 @@ void vg_console_reg_cmd( const char *alias, int (*function)(int argc, const char
    cmd->function = function;
    cmd->poll_suggest = poll_suggest;
    cmd->name = alias;
-
-   vg_info( "Console function '%s' registered\n", alias );
 }
 
 static int _vg_console_list( int argc, char const *argv[] )
@@ -61,17 +48,10 @@ static int _vg_console_list( int argc, char const *argv[] )
        return 0;
 }
 
+#if defined( VG_ENGINE )
 static void vg_console_write_persistent(void)
 {
-#ifdef VG_ENGINE
-   enum engine_status status = SDL_AtomicGet( &vg.engine_status );
-   if( status < k_engine_status_running )
-   {
-      vg_low( "Not writing auto.conf due to early exit.\n" );
-      return;
-   }
-#endif
-
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) );
        FILE *fp = fopen( "cfg/auto.conf", "w" );
    if( !fp )
    {
@@ -102,12 +82,6 @@ static void vg_console_write_persistent(void)
 
        fclose( fp );
 }
-
-#ifdef VG_ENGINE
-static void _vg_console_free(void)
-{
-       vg_console_write_persistent();
-}
 #endif
 
 /*
@@ -286,338 +260,6 @@ void vg_execute_console_input( const char *cmd, bool silent, bool cheat_override
       vg_error( "No command/var named '%s'. Use 'list' to view all\n",args[0]);
 }
 
-#ifdef VG_ENGINE
-u32 str_lev_distance( const char *s1, const char *s2 )
-{
-   u32 m = strlen( s1 ),
-       n = strlen( s2 );
-
-   if( m==0 ) return n;
-   if( n==0 ) return m;
-
-   u32 costs[ 256 ];
-
-   for( u32 k=0; k<=n; k++ ) 
-      costs[k] = k;
-
-   u32 i = 0;
-   for( u32 i=0; i<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 ) 
@@ -657,112 +299,20 @@ static int vg_console_exec( int argc, const char *argv[] )
 }
 
 
-void vg_console_init(void)
+VG_API void _vg_console_register(void)
 {
    vg_console_reg_cmd( "list", _vg_console_list, NULL );
    vg_console_reg_cmd( "exec", vg_console_exec, NULL );
-#ifdef VG_ENGINE
-   vg_console_reg_var( "cheats", &vg_console.cheats, k_var_dtype_i32, 
-#ifdef VG_DEVWINDOW
-                       VG_VAR_PERSISTENT
-#else 
-                       0
-#endif
-                       );
+#if defined( VG_ENGINE )
+   vg_console_reg_var( "cheats", &vg_console.cheats, k_var_dtype_i32, 0 );
 #endif
 }
 
-#ifdef VG_ENGINE
-void vg_console_load_autos(void)
+VG_API void _vg_console_init(void)
 {
+#if defined( VG_ENGINE )
    vg_console.registration_blocked = 1;
    vg_console_exec( 2, (const char *[]){ "auto.conf", "silent" } );
-}
-
-void vg_console_draw( ui_context *ctx )
-{
-       if( !vg_console.enabled ) return;
-
-   if( SDL_LockMutex( vg_log.mutex ) )
-      vg_fatal_error( "" );
-
-       int ptr = vg_log.log_line_current;
-   int const fh = ctx->font->sy, log_lines = 32;
-   int console_lines = VG_MIN( log_lines, vg_log.log_line_count );
-
-   ui_rect rect_log   = { 0, 0,                vg.window_x, log_lines*fh },
-           rect_input = { 0, log_lines*fh + 1, vg.window_x, fh*2 },
-           rect_line  = { 0, 0,                vg.window_x, fh };
-       
-   /* 
-    * log
-    */
-   u32 bg_colour = (ui_colour( ctx, k_ui_bg )&0x00ffffff)|0x9f000000;
-
-   ui_fill( ctx, rect_log, bg_colour );
-   rect_line[1] = rect_log[1]+rect_log[3]-fh;
-
-   for( int i=0; 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
+}
index 0117fb0da97952e8adfb5fe1fb5a76955c6de574..bf4bdec34bd7bd43ff3f5e6246be32d046b72f5d 100644 (file)
@@ -1,7 +1,6 @@
-/* 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__ ;\
@@ -43,7 +42,7 @@ struct vg_console
        }
        functions[ 32 ];
 
-#ifdef VG_ENGINE
+#if defined( VG_ENGINE )
    struct {
       const char *str;
       int len;
@@ -59,7 +58,7 @@ struct vg_console
 
    u32 var_count, function_count;
 
-#ifdef VG_ENGINE
+#if defined( VG_ENGINE )
        char input[96],
         input_copy[96];
        char history[32][96];
@@ -73,19 +72,14 @@ struct vg_console
 }
 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
diff --git a/vg_db.c b/vg_db.c
index 85e72ecbb7d7a2e9c96be3fafc7689964d507b8c..1b1062c936d151b9be1d8808bb31e0b513c4e78d 100644 (file)
--- a/vg_db.c
+++ b/vg_db.c
@@ -1,7 +1,3 @@
-#include "vg_db.h"
-#include <stddef.h>
-#include <stdarg.h>
-
 static void vg_db_touch( vg_db *db, u16 cache_id );
 
 /* util
diff --git a/vg_db.h b/vg_db.h
index 5505610e1b8088c37d1aa7b3f928c1274f83902f..d4a9ab75224d48088b6930856899fd68c736f1b3 100644 (file)
--- a/vg_db.h
+++ b/vg_db.h
@@ -1,5 +1,6 @@
-#pragma once
-#include "vg_m.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_db.c"
+#else
 
 #define VG_PAGE_BITS 11
 #define VG_PAGE_SIZE (0x1lu<<VG_PAGE_BITS)
@@ -154,3 +155,5 @@ void vg_db_skipper_placement( vg_db *db, vg_skipper_context *ctx, u16 item_index
 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
index 7bf0b2cc5f4a6b9bc19c9b46225b4a46ab2b37f6..cdf31b6a341960105b192c11ac5b4f0a717b8e27 100644 (file)
-#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;
@@ -156,17 +410,11 @@ static void _vg_process_events(void)
                ui_defocus_all( &vg_ui.ctx );
             }
             else if( (event.key.keysym.mod & KMOD_CTRL) && (event.key.keysym.sym == SDLK_n) )
-            {
                console_suggest_next( &vg_ui.ctx );
-            }
             else if( (event.key.keysym.mod & KMOD_CTRL ) && (event.key.keysym.sym == SDLK_p) )
-            {
                console_suggest_prev( &vg_ui.ctx );
-            }
             else
-            {
                vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym );
-            }
          }
          else
          {
@@ -176,9 +424,7 @@ static void _vg_process_events(void)
                vg_console.enabled = 1;
             }
             else 
-            {
                vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym );
-            }
          }
       }
       else if( event.type == SDL_MOUSEWHEEL )
@@ -187,9 +433,7 @@ static void _vg_process_events(void)
          vg.mouse_wheel[1] += event.wheel.preciseY;
       }
       else if( (event.type == SDL_CONTROLLERDEVICEADDED) || (event.type == SDL_CONTROLLERDEVICEREMOVED) )
-      {
          vg_input_device_event( &event );
-      }
       else if( event.type == SDL_CONTROLLERAXISMOTION ||
                event.type == SDL_CONTROLLERBUTTONDOWN ||
                event.type == SDL_CONTROLLERBUTTONUP )
@@ -205,1102 +449,105 @@ static void _vg_process_events(void)
       {
          if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED )
          {
-            int w, h;
-            SDL_GL_GetDrawableSize( vg.window, &w, &h );
-
-            if( !w || !h )
-            {
-               vg_warn( "Got a invalid framebuffer size: %dx%d... ignoring\n", w, h );
-            }
-            else
-            {
-
-               i32 delta[2] = { w - vg.window_x, h - vg.window_y };
-               _vg_magi_area_change( delta );
-               vg.window_x = w;
-               vg.window_y = h;
-               
-               vg_framebuffer_update_sizes();
-               vg_framebuffer_resize( vg.window_x, vg.window_y );
-            }
+            _vg_window_size_changed();
+            callback_fn( (vg_event_info[]){{ .type = k_vg_event_framebuffer_resize, 
+                                             .framebuffer = { .w = _vg_window.w, .h = _vg_window.h } }} );
          }
          else if( event.window.event == SDL_WINDOWEVENT_CLOSE )
-         {
-            vg.window_should_close = 1;
-         }
+            _vg_terminate();
       }
       else if( event.type == SDL_TEXTINPUT )
-      {
          ui_proc_utf8( &vg_ui.ctx, event.text.text );
-      }
    }
-
    vg.mouse_state = SDL_GetMouseState( &vg.mouse_pos[0], &vg.mouse_pos[1] );
-   vg_process_inputs();
+   v2_zero( vg.mouse_wheel );
+   v2_zero( vg.mouse_delta );
+   _vg_window_swap();
 
+   vg_audio_preupdate();
+   vg_process_inputs();
    vg_steam_frame();
-}
-
-static void _vg_gameloop_update(void)
-{
-   vg_profile_begin( &vg_prof_update );
-
-   vg.engine_stage = k_engine_stage_update;
-   vg_pre_update();
-
-   /* Fixed update loop */
-   vg.engine_stage = k_engine_stage_update_fixed;
-
-   vg.fixed_iterations = 0;
-   vg_lines.enabled = vg_lines.render;
-   vg.time_fixed_accumulator += vg.time_delta;
-
-   while( vg.time_fixed_accumulator >= vg.time_fixed_delta )
+      
+   if( 1 )
    {
-      vg_fixed_update();
-      vg_lines.enabled = 0;
-      vg.time_fixed_accumulator -= vg.time_fixed_delta;
-
-      vg.fixed_iterations ++;
-      if( vg.fixed_iterations == 8 )
-         break;
-   }
-   vg_lines.enabled = vg_lines.render;
-   vg.time_fixed_extrapolate = vg.time_fixed_accumulator / vg.time_fixed_delta;
+      /* Update loop
+       * ----------------------------------------------------------------------------------------------------------- */
+      {
+         _vg_profiler_enter_block( vg.profiler, "update" );
+         vg.gameloop_stage = k_gameloop_update;
+         callback_fn( (vg_event_info[]){{ .type = k_vg_event_pre_update }} );
 
-   vg.engine_stage = k_engine_stage_update;
-   vg_post_update();
-   vg_profile_end( &vg_prof_update );
-}
+         /* Fixed update loop */
+         vg.gameloop_stage = k_gameloop_update_fixed;
+         vg.fixed_iterations = 0;
+         vg_lines.enabled = vg_lines.render;
+         vg.time_fixed_accumulator += vg.time_delta;
 
-static void vg_settings_gui( ui_context *ctx );
-static void _vg_gameloop_render(void)
-{
-   vg_profile_begin( &vg_prof_render );
+         while( vg.time_fixed_accumulator >= vg.time_fixed_delta )
+         {
+            callback_fn( (vg_event_info[]){{ .type = k_vg_event_fixed_update }} );
+            vg_lines.enabled = 0;
+            vg.time_fixed_accumulator -= vg.time_fixed_delta;
 
-   /* render */
-   vg.engine_stage = k_engine_stage_rendering;
-   vg_render();
+            vg.fixed_iterations ++;
+            if( vg.fixed_iterations == 8 )
+               break;
+         }
+         vg_lines.enabled = vg_lines.render;
+         vg.time_fixed_extrapolate = vg.time_fixed_accumulator / vg.time_fixed_delta;
 
-   vg_profile_end( &vg_prof_render );
+         vg.gameloop_stage = k_gameloop_update;
+         callback_fn( (vg_event_info[]){{ .type = k_vg_event_post_update }} );
+         _vg_profiler_exit_block( vg.profiler );
+      }
 
-   /* ui */
-   vg.engine_stage = k_engine_stage_ui;
-   {
-      if( _vg_tower_clearence( _vg_tower_mask(vg.sig_engine)|_vg_tower_mask(vg.sig_client) ) )
+      /* Render loop
+       * ------------------------------------------------------------------------------------------------------------ */
       {
+         /* render  -------------------------------------------------- */
+         _vg_profiler_enter_block( vg.profiler, "render" );
+         vg.gameloop_stage = k_gameloop_rendering;
+         callback_fn( (vg_event_info[]){{ .type = k_vg_event_render }} );
+         _vg_profiler_exit_block( vg.profiler );
+
+         /* ui --------------------------------------------------- */
+         vg.gameloop_stage = k_gameloop_ui;
+
          ui_prerender( &vg_ui.ctx );
-         vg_ui_set_screen( vg.window_x, vg.window_y );
+         vg_ui_set_screen( _vg_window.w, _vg_window.h );
          ui_update_mouse( &vg_ui.ctx, (ui_px[2]){ vg.mouse_pos[0], vg.mouse_pos[1] }, vg.mouse_state );
 
          if( vg_console.enabled )
-         { 
             ui_ignore_input_frames( &vg_ui.ctx, 10 );
-            vg_gui( &vg_ui.ctx );
+         callback_fn( (vg_event_info[]){{ .type = k_vg_event_gui, .gui = { .ctx = &vg_ui.ctx } }} );
+         if( vg_console.enabled )
+         {
             ui_ignore_input_frames( &vg_ui.ctx, 0 );
             ui_capture_mouse( &vg_ui.ctx, 1 );
             vg_console_draw( &vg_ui.ctx );
          }
-         else vg_gui( &vg_ui.ctx );
 
-         if( vg.settings_open )
-            vg_settings_gui( &vg_ui.ctx );
-         
+         _vg_settings_gui( &vg_ui.ctx );
          vg_framebuffer_ui( &vg_ui.ctx );
          _vg_magi_render( &vg_ui.ctx );
-
          ui_postrender( &vg_ui.ctx, vg.time_frame_delta );
          vg_ui_post_update();
       }
    }
-}
-
-static void vg_changevsync(void)
-{
-   if( vg.vsync && (vg.vsync_feature != k_vsync_feature_error) )
+   else
    {
-      /* turn on vsync if not enabled */
-
-      enum vsync_feature requested = k_vsync_feature_enabled;
-      if( vg.vsync < 0 ) requested = k_vsync_feature_enabled_adaptive;
-
-      if( vg.vsync_feature != requested )
-      {
-         vg_info( "Setting swap interval\n" );
-
-         int swap_interval = 1;
-         if( requested == k_vsync_feature_enabled_adaptive ) 
-            swap_interval = -1;
-
-         if( SDL_GL_SetSwapInterval( swap_interval ) == -1 )
-         {
-            if( requested == k_vsync_feature_enabled )
-            {
-               ui_start_modal( &vg_ui.ctx, 
-                     "Vsync not supported on this system.\n\n"
-                     "You may be overriding it in your"
-                     " graphics \ncontrol panel.\n",
-                     NULL, UI_MODAL_BAD, NULL );
-            }
-            else
-            {
-               ui_start_modal( &vg_ui.ctx, 
-                     "Adaptive Vsync is not supported by your system\n\n"
-                     "You may be overriding it in your"
-                     " graphics \ncontrol panel.\n",
-                     NULL, UI_MODAL_BAD, NULL );
-            }
-
-            vg.vsync_feature = k_vsync_feature_error;
-            vg.vsync = 0;
-         }
-         else
-         {
-            vg_success( "Vsync enabled (%d)\n", requested );
-            vg.vsync_feature = requested;
-         }
-      }
-   }
-   else {
-      if( vg.vsync_feature != k_vsync_feature_disabled ){
-         SDL_GL_SetSwapInterval( 0 );
-         vg.vsync_feature = k_vsync_feature_disabled;
-      }
+      vg_loader_render();
    }
-}
-
-static int vg_framefilter( f64 dt )
-{
-   if( vg.fps_limit < 24 ) 
-      vg.fps_limit = 24;
 
-   if( vg.fps_limit > 300 ) 
-      vg.fps_limit = 300;
-
-   double min_frametime = 1.0/(double)vg.fps_limit;
-   if( vg.time_frame_delta < min_frametime )
+   if( vg.loader_ring > 0.01f )
    {
-      /* TODO: we can use high res nanosleep on Linux here */
-      double sleep_ms = (min_frametime-vg.time_frame_delta) * 1000.0;
-      u32 ms = (u32)floor( sleep_ms );
+      vg_loader_render_ring( vg.loader_ring );
+      vg.loader_ring -= vg.time_frame_delta * 0.5f;
+   }
 
-      if( ms )
-         SDL_Delay(ms);
-      else
-         vg.time_spinning ++;
-
-      return 1;
-   }
-
-   return 0;
-}
-
-static void _vg_gameloop(void)
-{
-   vg.time_hp = SDL_GetPerformanceCounter();
-   vg.time_hp_last = vg.time_hp;
-
-   int post_start = 0;
-   while(1)
-   {
-      vg.time_hp = SDL_GetPerformanceCounter();
-      u64 dt_hp = vg.time_hp - vg.time_hp_last;
-      vg.time_hp_last = vg.time_hp;
-
-      f64 dt = (f64)dt_hp / (f64)SDL_GetPerformanceFrequency();
-      vg.time_frame_delta += dt;
-
-      while( vg_async_has_work( &vg.main_tasks ) )
-      {
-         if( vg_async_process_next_task( &vg.main_tasks ) == 0 )
-            return;
-      }
-
-      if( vg_framefilter( dt ) )
-         continue;
-
-      vg_changevsync();
-      vg_audio_preupdate();
-
-      enum engine_status status = SDL_AtomicGet( &vg.engine_status );
-      if( status == k_engine_status_running )
-         vg_profile_begin( &vg_prof_swap );
-
-      SDL_GL_SwapWindow( vg.window );
-
-      if( status == k_engine_status_running )
-         vg_profile_end( &vg_prof_swap );
-
-      vg.time_real += vg.time_frame_delta;
-      vg.time_delta = vg.time_frame_delta * vg.time_rate;
-      vg.time += vg.time_delta;
-
-      _vg_process_events();
-
-      if( vg.window_should_close )
-         return;
-         
-      if( status == k_engine_status_running )
-      {
-         _vg_gameloop_update();
-         _vg_gameloop_render();
-      }
-      else
-      {
-         vg_loader_render();
-      }
-
-      if( vg.loader_ring > 0.01f )
-      {
-         vg_loader_render_ring( vg.loader_ring );
-         vg.loader_ring -= vg.time_frame_delta * 0.5f;
-      }
-
-      vg.time_frame_delta = 0.0;
-      vg.time_spinning = 0;
-
-      vg_audio_lock();
-      vg_audio_sync_ui_master_controls();
-      vg_audio_unlock();
-   }
-}
-
-static void _vg_init_window( const char *window_name )
-{
-   vg_info( "SDL_INIT\n" );
-
-   if( SDL_Init( SDL_INIT_VIDEO ) != 0  )
-   {
-      vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
-      exit(0);
-   }
-
-#ifndef VG_NO_AUDIO
-   SDL_InitSubSystem( SDL_INIT_AUDIO );
-#endif
-   SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER );
-
-   char *exe_basepath = SDL_GetBasePath();
-   u32 len = vg_align8( strlen(exe_basepath)+1 );
-   char *dest = vg_stack_allocate( &vg.rtmem, len, 1, "Exe basepath" );
-   strcpy( dest, exe_basepath );
-   SDL_free( exe_basepath );
-   vg.base_path = dest;
-   vg_info( "Basepath: %s\n", vg.base_path );
-
-   SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR, SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
-   SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
-   SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
-   SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
-   SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
-   SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
-
-   /* 
-    * Get monitor information 
-    */
-   vg_info( "Getting display count\n" );
-   int display_count = 0, 
-       display_index = 0, 
-       mode_index = 0;
-
-   SDL_DisplayMode video_mode;
-   if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) )
-   {
-      vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() );
-      SDL_Quit();
-      exit(0);
-   }
-
-   vg.display_refresh_rate = video_mode.refresh_rate;
-   vg.window_x = video_mode.w;
-   vg.window_y = video_mode.h;
-
-   if( vg.screen_mode == 2 )
-   {
-      vg.window_x = 1280;
-      vg.window_y = 720;
-   }
-
-#ifndef _WIN32
-       SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" );
-       SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" );
-       SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" );
-#endif
-
-   u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_GRABBED |
-               SDL_WINDOW_RESIZABLE;
-
-   if( vg.screen_mode == 1 )
-      flags |= SDL_WINDOW_FULLSCREEN;
-   else if( vg.screen_mode == 0 )
-      flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
-
-   vg_info( "CreateWindow( %d %d %u )\n", vg.window_x, vg.window_y, flags );
-   if((vg.window = SDL_CreateWindow( window_name, 0, 0, vg.window_x, vg.window_y, flags )))
-   {
-      if( vg.screen_mode == 2 )
-         SDL_SetWindowPosition( vg.window, video_mode.w-vg.window_x, 0 );
-   }
-   else
-   {
-      vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
-      exit(0);
-   }
-
-   SDL_RaiseWindow( vg.window );
-   SDL_SetWindowMinimumSize( vg.window, 1280, 720 );
-   SDL_SetWindowMaximumSize( vg.window, 4096, 4096 );
-
-   vg_info( "CreateContext\n" );
+   vg_audio_lock();
+   vg_audio_sync_ui_master_controls();
+   vg_audio_unlock();
 
-   /* ????? */
-   if( SDL_IsTextInputActive() ) 
-      SDL_StopTextInput();
-
-   /* 
-    * OpenGL loading 
-    */
-   if( (vg.gl_context = SDL_GL_CreateContext(vg.window) ))
-   {
-      SDL_GL_GetDrawableSize( vg.window, &vg.window_x, &vg.window_y );
-      vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
-   }
-   else
-   {
-      vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
-      SDL_Quit();
-      exit(0);
-   }
-
-   if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) 
-   {
-      vg_error( "Glad Failed to initialize\n" );
-      SDL_GL_DeleteContext( vg.gl_context );
-      SDL_Quit();
-      exit(0);
-   }
-
-   const unsigned char* glver = glGetString( GL_VERSION );
-   vg_success( "Load setup complete, OpenGL version: %s\n", glver );
-   SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
-
-   SDL_DisplayMode dispmode;
-   if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) )
-   {
-      if( dispmode.refresh_rate )
-      {
-         vg.display_refresh_rate = dispmode.refresh_rate;
-      }
-   }
-
-   if( vg.display_refresh_rate < 25 || vg.display_refresh_rate > 300 )
-   {
-      vg.display_refresh_rate = 60;
-   }
-
-   vg_info( "Display refresh rate: %d\n", dispmode.refresh_rate );
-   if( !vg.fps_limit ) 
-      vg.fps_limit = vg.display_refresh_rate;
-}
-
-static void _vg_terminate(void)
-{
-   /* Shutdown */
-   vg_magi_save();
-   vg_console_write_persistent();
-
-   SDL_AtomicSet( &vg.engine_status, k_engine_status_none );
-   vg_loader_atexit();
-
-   SDL_GL_DeleteContext( vg.gl_context );
-   SDL_Quit();
-   vg_steam_shutdown();
-   exit(0);
-}
-
-static int _vg_loader_thread( void *pfn )
-{
-   SDL_TLSSet( vg.thread_purpose, &_thread_purpose_loader, NULL );
-   while( vg_async_process_next_task( &vg.loader_tasks ) ) {}
-   return 0;
-}
-
-static void vg_on_client_ready( vg_signal_id id, bool state )
-{
-   if( state )
-      vg_magi_restore();
-}
-
-#ifdef _WIN32
-#include <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
index 9955dbb8905a36c7fae1a576523d88241132d67b..54a519b03aa90dee5c08bbd6f8b71ce68c175258 100644 (file)
-#pragma once
-
-/* Copyright (C) 2021-2025 Mt.Zero Software - All Rights Reserved */
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_engine.c"
+#else
 
 /* configuration warnings */
-
-#ifndef VG_TIMESTEP_FIXED
+#if !defined( VG_TIMESTEP_FIXED )
  #warning VG_TIMESTEP_FIXED not defined; setting to 1/60
  #define VG_TIMESTEP_FIXED (1.0/60.0)
 #endif
 
-#ifndef VG_3D
- #ifndef VG_2D
+#if !defined( VG_3D )
+ #if !defined( VG_2D )
   #warning VG_3D or VG_2D not defined; defining VG_3D
   #define VG_3D
  #endif
 #endif
 
-#include "vg_opengl.h"
+enum vg_event
+{
+   k_vg_event_opts,
+   k_vg_event_register,
+   k_vg_event_init,
+   k_vg_event_pre_update,
+   k_vg_event_fixed_update,
+   k_vg_event_post_update,
+   k_vg_event_render,
+   k_vg_event_gui,
+   k_vg_event_framebuffer_resize,
+   k_vg_event_invalid_options
+};
 
-#define SDL_MAIN_HANDLED
-#include "dep/sdl/include/SDL.h"
+typedef struct vg_event_info vg_event_info;
+struct vg_event_info
+{
+   enum vg_event type;
+   union
+   {
+      struct
+      {
+         ui_context *ctx;
+      }
+      gui;
+
+      struct
+      {
+         i32 w, h;
+      }
+      framebuffer;
+   };
+};
 
-#include "vg_platform.h"
-#include "vg_mem.h"
-#include "vg_m.h"
-#include "vg_font.h"
-#include "vg_string.h"
-#include "vg_ui/imgui.h"
-#include "vg_async2.h"
-#include "vg_tower.h"
+typedef void (*vg_event_callback)( vg_event_info *info );
 
 /* API */
-void vg_init( int argc, const char *argv[], const char *window_name );
-void vg_run(void);
-
-/* Thread 1 */
-void vg_bake_shaders(void);
+VG_API void vg_run( int argc, const char *argv[], vg_event_callback fn );
 
 /* Main thread */
-extern void vg_framebuffer_resize(int w, int h);
-extern void vg_pre_update(void);
-extern void vg_fixed_update(void);
-extern void vg_post_update(void);
-
-extern void vg_render(void);
-extern void vg_gui( ui_context *ctx );
-
-void vg_settings_open(void);
-void vg_settings_close(void);
-
-enum quality_profile{
+enum quality_profile
+{
    k_quality_profile_high = 0,
    k_quality_profile_low = 1,
    k_quality_profile_min = 2
 };
 
-enum engine_status
-{
-   k_engine_status_none,
-   k_engine_status_load_internal,
-   k_engine_status_running,
-};
+#define VG_THREAD_OWNS_SDL    0x1
+#define VG_THREAD_OWNS_OPENGL 0x2
+#define VG_THREAD_OWNS_STEAM  0x4
+#define VG_THREAD_BACKGROUND  0x100
+#define VG_THREAD_MAIN        (VG_THREAD_OWNS_SDL|VG_THREAD_OWNS_OPENGL|VG_THREAD_OWNS_STEAM)
+#define VG_THREAD_ASYNC       (VG_THREAD_BACKGROUND)
+#define VG_THREAD_MAIN_ID     0
+#define VG_THREAD_ASYNC_ID    1
+#define VG_TEMP_STACK_MAX     8
+
+VG_API bool _vg_thread_has_flags( u32 flags );
+VG_API void _vg_thread_set_flags( u32 flags );
+VG_API const c8 *_vg_thread_prefix(void);
+
+VG_API void _vg_async_context_push_groups( u16 groups );
+VG_API void _vg_async_context_pop_groups(void);
+VG_API u16 _vg_async_context_get_groups(void);
+
+VG_API void *_vg_async_alloc( u32 thread_id_target, u32 bytes );
+VG_API void  _vg_async_send ( void *check_buffer, vg_async_fn fn );
+VG_API void _vg_add_exit_function( void( *fn )( void ) );
+
+VG_API u32 _vg_start_temp_frame(void);
+VG_API void _vg_end_temp_frame( u32 whence );
+VG_API vg_stack_allocator *_vg_temp_stack(void);
+
+VG_API void _vg_terminate(void);
 
 struct vg_engine 
 {
-   vg_stack_allocator rtmem, scratch;
-
    /* Engine sync */
-   SDL_Window     *window;
-   SDL_GLContext  gl_context;
-   SDL_TLSID      thread_purpose;
-
-   vg_async_queue main_tasks, loader_tasks;
-   SDL_atomic_t engine_status;
-
-   vg_signal_id sig_engine, sig_client;
-
-   /* Window information */
-   int window_x,
-       window_y,
-       samples,
-       window_should_close;
-   const char *base_path;
-   const char *window_name;
-
-   int display_refresh_rate,
-       fps_limit,
-       vsync,
-       screen_mode,
-       display_index;
-
-   int settings_open;
-
-   enum vsync_feature{
-      k_vsync_feature_disabled=0,
-      k_vsync_feature_enabled=1,
-      k_vsync_feature_enabled_adaptive=2,
-      k_vsync_feature_error=3
+   SDL_TLSID      thread_tls;
+
+   struct vg_thread_context
+   {
+      u32 flags;
+      vg_stack_allocator temporary_memory;
+
+      u32 temp_stack_depth,
+          temp_offsets[ VG_TEMP_STACK_MAX ];
+
+      u16 async_groups[ VG_TEMP_STACK_MAX ];
+      u32 async_group_depth;
+
+      vg_async_task *async_task;
+      vg_async_queue *async_task_queue;
+      const c8 *log_prefix;
    }
-   vsync_feature;
+   thread_contexts[3];
+
+   vg_async_queue thread_tasks[2], exit_tasks;
+   const c8 *base_path;
 
    i32 mouse_pos[2], mouse_state;
    v2f mouse_delta,
        mouse_wheel;
 
    /* Runtime */
-   double time,
-          time_real,
-          time_delta,
-          time_rate,
-
-          time_fixed_accumulator,
-          time_fixed_extrapolate,
-          time_frame_delta;
-
+   f64 time,
+       time_real,
+       time_delta,
+       time_rate,
+       time_fixed_accumulator,
+       time_fixed_extrapolate,
+       time_frame_delta;
    f32 time_fixed_delta;
    u64 time_hp, time_hp_last, time_spinning;
 
    int fixed_iterations;
 
-   enum engine_stage
+   enum vg_gameloop_stage
    {
-      k_engine_stage_none,
-      k_engine_stage_update,
-      k_engine_stage_update_fixed,
-      k_engine_stage_rendering,
-      k_engine_stage_ui
+      k_gameloop_update,
+      k_gameloop_update_fixed,
+      k_gameloop_rendering,
+      k_gameloop_ui
    }
-   engine_stage;
+   gameloop_stage;
 
    /* graphics */
-#ifdef VG_3D
+#if defined( VG_3D )
    m4x4f pv;
 #else
    m3x3f pv;
@@ -138,34 +150,15 @@ struct vg_engine
 
    i32 quality_profile;
 
-   float loader_ring;
+   f32 loader_ring;
    GLuint tex_missing;
-
    vg_rand rand;
-
    i32 load_step_delay;
-}
-extern vg;
-
-struct vg_ui
-{
-   GLuint vao, vbo, ebo;
-   m3x3f pv;
-   ui_context ctx;
-   GLuint tex_glyphs;
-   v2f inverse_font_sheet;
-
-   SDL_Cursor *cursor_map[ k_ui_cursor_max ];
 
-   /* at some point this should be implementation specific? */
-   v4f colour;
-   f32 frosting;
-   v2f bg_inverse_ratio;
-   GLuint tex_bg;
+   i32 fps_limit;
+   u32 profiler;
 }
-extern vg_ui;
-void vg_ui_set_screen( i32 width, i32 height );
-void vg_ui_set_mouse_pos( ui_px x, ui_px y );
+extern vg;
 
 struct vg_setting_enum
 {
@@ -191,18 +184,4 @@ void vg_settings_ui_header( ui_context *ctx, ui_rect inout_panel, const char *na
 bool vg_settings_apply_button( ui_context *ctx, ui_rect inout_panel, bool validated );
 enum engine_status _vg_engine_status(void);
 
-extern u32 _thread_purpose_main;
-extern u32 _thread_purpose_loader;
-u32 vg_thread_purpose(void);
-
-#define THREAD_0 VG_ASSERT( vg_thread_purpose() == _thread_purpose_main )
-#define THREAD_1 VG_ASSERT( vg_thread_purpose() == _thread_purpose_loader )
-
-void vg_checkgl( const char *src_info );
-#define VG_STRINGIT( X ) #X
-#define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
-
-/* the few includes we always want. */
-#include "vg_log.h"
-#include "vg_shader.h"
-#include "vg_console.h"
+#endif
index 9a17561ec7b5b9ed4476f27c3cbda5efab4242b6..4406418173f17ea81d5d8c9a0c123f0bea0bf8fd 100644 (file)
--- a/vg_font.h
+++ b/vg_font.h
@@ -1,5 +1,4 @@
-#pragma once
-
+#if !defined( VG_IMPLEMENTATION )
 typedef struct vg_font_char vg_font_char;
 typedef struct vg_font_face vg_font_face;
 typedef struct vg_font_sheet vg_font_sheet;
@@ -21,4 +20,4 @@ struct vg_font_sheet
    i16 w, h;
    u32 bitmap[];
 };
-
+#endif
index 616d6a096fcbc64d6ad05644cfa9318970230478..ceab2c54574fbf087fb0677a0a853a781598f86d 100644 (file)
@@ -1,6 +1,3 @@
-#include "vg_framebuffer.h"
-#include "vg_platform.h"
-
 struct
 {
    vg_framebuffer *list[16];
@@ -8,12 +5,29 @@ struct
 }
 static _vg_framebuffer;
 
-void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y )
+VG_API vg_framebuffer *_vg_framebuffer_alloc( vg_stack_allocator *stack, u32 attachment_count, bool track )
+{
+   u32 size = sizeof(vg_framebuffer) + sizeof(vg_framebuffer_attachment)*attachment_count;
+   vg_framebuffer *fb = vg_stack_allocate( stack, size, 8, "Framebuffer metadata" );
+   vg_zero_mem( fb, size );
+
+   fb->attachment_count = attachment_count;
+   if( track )
+   {
+      if( _vg_framebuffer.count != VG_ARRAY_LEN(_vg_framebuffer.list) )
+         _vg_framebuffer.list[ _vg_framebuffer.count ++ ] = fb;
+      else
+         vg_fatal_error( "Framebuffer list is full, and tried to allocate another.\n");
+   }
+   return fb;
+}
+
+VG_API void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y )
 {
    if( fb->resolution_div )
    {
-      *x = vg.window_x / fb->resolution_div;
-      *y = vg.window_y / fb->resolution_div;
+      *x = _vg_window.w / fb->resolution_div;
+      *y = _vg_window.h / fb->resolution_div;
    }
    else
    {
@@ -22,7 +36,7 @@ void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y )
    }
 }
 
-void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse )
+VG_API void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse )
 {
    if( fb )
    {
@@ -36,11 +50,11 @@ void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse )
    }
    else
    {
-      v2_div( (v2f){1.0f,1.0f}, (v2f){ vg.window_x, vg.window_y }, inverse );
+      v2_div( (v2f){1.0f,1.0f}, (v2f){ _vg_window.w, _vg_window.h }, inverse );
    }
 }
 
-void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling )
+VG_API void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling )
 {
    int x, y;
    vg_framebuffer_get_res( fb, &x, &y );
@@ -66,7 +80,7 @@ void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling )
    glViewport( 0, 0, x, y );
 }
 
-void vg_framebuffer_bind_texture( vg_framebuffer *fb, int attachment, int slot )
+VG_TIER_0 void vg_framebuffer_bind_texture( vg_framebuffer *fb, u32 attachment, u32 slot )
 {
    vg_framebuffer_attachment *at = &fb->attachments[attachment];
 
@@ -76,8 +90,7 @@ void vg_framebuffer_bind_texture( vg_framebuffer *fb, int attachment, int slot )
       vg_fatal_error( "illegal operation: bind non-texture framebuffer attachment to texture slot" );
    }
 
-   glActiveTexture( GL_TEXTURE0 + slot );
-   glBindTexture( GL_TEXTURE_2D, fb->attachments[attachment].id );
+   vg_tex_bind( GL_TEXTURE_2D, &fb->attachments[attachment].tex, slot );
 }
 
 /* 
@@ -203,46 +216,31 @@ static const char *render_fb_format_str( GLenum format )
 /*
  * Bind and allocate texture for framebuffer attachment
  */
-static void vg_framebuffer_allocate_texture( vg_framebuffer *fb, 
-                                             vg_framebuffer_attachment *a )
+static void vg_framebuffer_allocate_texture( vg_framebuffer *fb, vg_framebuffer_attachment *a )
 {
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
+   if( a->tex.name == 0 )
+      return;
+
    int rx, ry;
    vg_framebuffer_get_res( fb, &rx, &ry );
 
    if( a->purpose == k_framebuffer_attachment_type_renderbuffer )
    {
-      glBindRenderbuffer( GL_RENDERBUFFER, a->id );
+      glBindRenderbuffer( GL_RENDERBUFFER, a->tex.name );
       glRenderbufferStorage( GL_RENDERBUFFER, a->internalformat, rx, ry );
    }
    else if( a->purpose == k_framebuffer_attachment_type_texture ||
             a->purpose == k_framebuffer_attachment_type_texture_depth )
    {
-      glBindTexture( GL_TEXTURE_2D, a->id );
-      glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry,
-                                   0, a->format, a->type, NULL );
+      glBindTexture( GL_TEXTURE_2D, a->tex.name );
+      glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry, 0, a->format, a->type, NULL );
    }
 }
 
-vg_framebuffer *vg_framebuffer_allocate( vg_stack_allocator *stack, u32 attachment_count, bool track )
+VG_TIER_0 void vg_framebuffer_init( vg_framebuffer *fb )
 {
-   vg_framebuffer *fb = VG_STACK_ALLOCATE_STRUCT( stack, vg_framebuffer );
-
-   if( track )
-   {
-      if( _vg_framebuffer.count != VG_ARRAY_LEN(_vg_framebuffer.list) )
-         _vg_framebuffer.list[ _vg_framebuffer.count ++ ] = fb;
-      else
-         vg_fatal_error( "Framebuffer list is full, and tried to allocate another.\n");
-   }
-
-   fb->attachments = vg_stack_allocate( stack, sizeof(vg_framebuffer_attachment) * attachment_count, 8, NULL );
-   fb->attachment_count = attachment_count;
-   return fb;
-}
-
-static void async_framebuffer_create( void *_fb )
-{
-   vg_framebuffer *fb = _fb;
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
    glGenFramebuffers( 1, &fb->id );
    glBindFramebuffer( GL_FRAMEBUFFER, fb->id );
 
@@ -268,24 +266,24 @@ static void async_framebuffer_create( void *_fb )
 
       if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer )
       {
-         glGenRenderbuffers( 1, &attachment->id );
+         glGenRenderbuffers( 1, &attachment->tex.name );
+         attachment->tex.flags = VG_TEX_COMPLETE|VG_TEX_FRAMEBUFFER_ATTACHMENT|VG_TEX_PRIVATE|VG_TEX_NOMIP;
+
          vg_framebuffer_allocate_texture( fb, attachment );
-         glFramebufferRenderbuffer( GL_FRAMEBUFFER, 
-                                    GL_DEPTH_STENCIL_ATTACHMENT,
-                                    GL_RENDERBUFFER, attachment->id );
+         glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, attachment->tex.name );
       }
       else if( attachment->purpose == k_framebuffer_attachment_type_texture || 
-         attachment->purpose == k_framebuffer_attachment_type_texture_depth )
+               attachment->purpose == k_framebuffer_attachment_type_texture_depth )
       {
-         glGenTextures( 1, &attachment->id );
+         glGenTextures( 1, &attachment->tex.name );
+         attachment->tex.flags = VG_TEX_COMPLETE|VG_TEX_FRAMEBUFFER_ATTACHMENT|VG_TEX_LINEAR|VG_TEX_CLAMP|VG_TEX_NOMIP;
+
          vg_framebuffer_allocate_texture( fb, attachment );
          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-
-         glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment,
-                                 GL_TEXTURE_2D,  attachment->id, 0 );
+         glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment, GL_TEXTURE_2D, attachment->tex.name, 0 );
 
          if( attachment->purpose == k_framebuffer_attachment_type_texture )
             colour_attachments[ colour_count ++ ] = attachment->attachment;
@@ -298,7 +296,6 @@ static void async_framebuffer_create( void *_fb )
     * Check result 
     */
    GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER );
-
    if( result == GL_FRAMEBUFFER_COMPLETE )
    {
       vg_success( "  status: complete\n" );
@@ -319,13 +316,7 @@ static void async_framebuffer_create( void *_fb )
    }
 }
 
-void vg_framebuffer_create( vg_framebuffer *fb )
-{
-   THREAD_1;
-   vg_async_call( &vg.main_tasks, async_framebuffer_create, fb );
-}
-
-void vg_framebuffer_free( vg_framebuffer *fb )
+VG_TIER_0 void vg_framebuffer_free( vg_framebuffer *fb )
 {
    glDeleteFramebuffers( 1, &fb->id );
 
@@ -338,22 +329,23 @@ void vg_framebuffer_free( vg_framebuffer *fb )
 
       if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer )
       {
-         glDeleteRenderbuffers( 1, &attachment->id );
+         glDeleteRenderbuffers( 1, &attachment->tex.name );
+         vg_zero_mem( &attachment->tex, sizeof(vg_tex) );
       }
       else if( attachment->purpose == k_framebuffer_attachment_type_texture || 
                attachment->purpose == k_framebuffer_attachment_type_texture_depth )
       {
-         glDeleteTextures( 1, &attachment->id );
+         vg_tex_delete( &attachment->tex );
       }
    }
 }
 
-void vg_framebuffer_ui( ui_context *ctx )
+VG_API void vg_framebuffer_ui( ui_context *ctx )
 {
-   ui_px w = vg.window_x/3,
-         h = vg.window_y/3;
+   ui_px w = _vg_window.w/3,
+         h = _vg_window.h/3;
 
-   ui_rect frame = {0,0,vg.window_x/3,vg.window_y/3};
+   ui_rect frame = {0,0,_vg_window.w/3,_vg_window.h/3};
 
    for( int i=0; i<_vg_framebuffer.count; i++ )
    {
@@ -390,7 +382,7 @@ void vg_framebuffer_ui( ui_context *ctx )
             }
 
             ui_rect_center( frame, img );
-            ui_image( ctx, img, &fb->attachments[j].id, 0 );
+            ui_image( ctx, img, &fb->attachments[j].tex, 0 );
          }
 
          ui_rect panel;
@@ -403,7 +395,7 @@ void vg_framebuffer_ui( ui_context *ctx )
 
          frame[0] += w;
 
-         if( (frame[0] + w) > vg.window_x )
+         if( (frame[0] + w) > _vg_window.w )
          {
             frame[0] = 0;
             frame[1] += h;
@@ -412,13 +404,10 @@ void vg_framebuffer_ui( ui_context *ctx )
    }
 }
 
-static void vg_framebuffer_show( vg_framebuffer *fb,
-                                 vg_framebuffer_attachment *at,
-                                 int operation )
+static void vg_framebuffer_show( vg_framebuffer *fb, vg_framebuffer_attachment *at, int operation )
 {
    at->debug_view = operation;
-   vg_info( "%s %s:%s\n", (operation? "shown": "hidden" ), 
-               fb->display_name, at->display_name );
+   vg_info( "%s %s:%s\n", (operation? "shown": "hidden" ), fb->display_name, at->display_name );
 }
 
 /* 
@@ -531,22 +520,15 @@ static void vg_framebuffer_poll( int argc, char const *argv[] )
    }
 }
 
-void vg_framebuffer_register(void)
+VG_API void _vg_framebuffer_register(void)
 {
-   //vg_console_reg_var( "blur_strength", &k_blur_strength, k_var_dtype_f32, 0 );
-   //vg_console_reg_var( "render_scale", &k_render_scale,
-   //                    k_var_dtype_f32, VG_VAR_PERSISTENT );
-   //vg_console_reg_var( "fov", &k_fov, k_var_dtype_f32, VG_VAR_PERSISTENT );
-   //vg_console_reg_var( "cam_height", &k_cam_height, 
-   //                     k_var_dtype_f32, VG_VAR_PERSISTENT );
-   //vg_console_reg_var( "blur_effect", &k_blur_effect, 
-   //                     k_var_dtype_i32, VG_VAR_PERSISTENT );
-
    vg_console_reg_cmd( "fb", vg_framebuffer_control, vg_framebuffer_poll );
 }
 
-void vg_framebuffer_update_sizes(void)
+VG_API void vg_framebuffer_update_sizes(void)
 {
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
+
    for( int i=0; i<_vg_framebuffer.count; i++ )
    {
       vg_framebuffer *fb = _vg_framebuffer.list[i];
index fef287d325b681dca5cd32a690a39fd332cba2e9..df4ad6dcd2f293072066ed6407a875da52ca1be6 100644 (file)
@@ -1,30 +1,30 @@
-/*
- * Copyright (C) 2021-2024 Mt.ZERO Software, Harry Godden - All Rights Reserved
- */
-#pragma once
-#include "vg_engine.h"
-
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_framebuffer.c"
+#else
 typedef struct vg_framebuffer vg_framebuffer;
 typedef struct vg_framebuffer_attachment vg_framebuffer_attachment;
 
+#define VG_FRAMEBUFFER_GLOBAL 1
+#define VG_FRAMEBUFFER_SPECIALIZED 0
+
 struct vg_framebuffer
 {
-   const char *display_name;
-   int         resolution_div, /* If 0:     Use fixed_w, fixed_h.
-                                  If non-0: Automatically size itself to 
-                                            the window resolution divided by 
-                                            this value */
-               fixed_w,
-               fixed_h,
-
-               render_w, /* The currently rendering resolution */
-               render_h;
+   const c8 *display_name;
+   int       resolution_div, /* If 0:     Use fixed_w, fixed_h.
+                                If non-0: Automatically size itself to 
+                                          the window resolution divided by 
+                                          this value */
+             fixed_w,
+             fixed_h,
+
+             render_w, /* The currently rendering resolution */
+             render_h;
    GLuint id;
+   u32 attachment_count;
 
    struct vg_framebuffer_attachment
    {
-      const char *display_name;
-      
+      const c8 *display_name;
       enum vg_framebuffer_attachment_type
       {
          k_framebuffer_attachment_type_none,
@@ -45,25 +45,24 @@ struct vg_framebuffer
              format,
              type,
              attachment;
-
-      GLuint id;
+      vg_tex tex;
 
       /* Runtime */
       int debug_view;
    }
-   *attachments;
-   u32 attachment_count;
+   attachments[];
 };
 
 /* 
  * Initialize framebuffer system
  */
-void vg_framebuffer_register(void);
+VG_API void _vg_framebuffer_register(void);
+VG_API vg_framebuffer *_vg_framebuffer_alloc( vg_stack_allocator *stack, u32 attachment_count, bool track );
 
 /* 
  * Get the current (automatically scaled or fixed) resolution of framebuffer
  */
-void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y );
+VG_API void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y );
 
 /*
  * Get the inverse ratio to project pixel coordinates (0->1920) to UV coordinates
@@ -71,33 +70,27 @@ void vg_framebuffer_get_res( vg_framebuffer *fb, int *x, int *y );
  * NOTE: won't necesarily use the full 0->1 range, but may index a subsection
  *       of the framebuffer if using variable scale rendering.
  */
-void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse );
+VG_API void vg_framebuffer_inverse_ratio( vg_framebuffer *fb, v2f inverse );
 
 /*
  * Bind framebuffer for drawing to
  */
-void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling );
+VG_API void vg_framebuffer_bind( vg_framebuffer *fb, f32 scaling );
 
 /*
  * Bind framebuffer attachment's texture
  */
-void vg_framebuffer_bind_texture( vg_framebuffer *fb, int attachment, int slot );
-
-/* 
- * Allocation of a framebuffer memory. Optionally, track this framebuffer in 
- * debugging systems.
- */
-vg_framebuffer *vg_framebuffer_allocate( vg_stack_allocator *stack, u32 attachment_count, bool track );
+VG_TIER_0 void vg_framebuffer_bind_texture( vg_framebuffer *fb, u32 attachment, u32 slot );
 
 /*
  * Allocate graphics memory and initialize 
  */
-void vg_framebuffer_create( vg_framebuffer *fb ); /* LOADER THREAD */
-void vg_framebuffer_free( vg_framebuffer *fb );
+VG_TIER_0 void vg_framebuffer_init( vg_framebuffer *fb );
+VG_TIER_0 void vg_framebuffer_free( vg_framebuffer *fb );
 
 /*
  * Draw framebuffer debugging stuff
  */
-void vg_framebuffer_ui( ui_context *ctx );
-
-void vg_framebuffer_update_sizes(void);
+VG_API void vg_framebuffer_ui( ui_context *ctx );
+VG_API void vg_framebuffer_update_sizes(void);
+#endif
index 8933e98f8c613e7416ef4bce8dd4172c7a253797..8917e2d6cdd083ba638435fcaef862bcb9700476 100644 (file)
@@ -1,10 +1,3 @@
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#include "vg_input.h"
-#include "vg_loader.h"
-#include "vg_engine.h"
-#include "vg_string.h"
-
 f32 controller_deadzone = 0.05f;
 
 struct vg_input vg_input = {
@@ -256,8 +249,29 @@ void vg_process_inputs(void)
       vg_input.hidden_mouse_travel = 0.0f;
 }
 
-static void async_vg_input_init( void *_ )
+VG_API void _vg_input_register(void)
+{
+   VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
+}
+
+static void vg_input_free(void)
+{
+   for( int i=0; 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" );
    
@@ -272,31 +286,8 @@ static void async_vg_input_init( void *_ )
       if( is_controller )
          vg_open_gamecontroller( i );
    }
-}
-
-void vg_input_register(void)
-{
-   VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
-}
 
-void vg_input_init(void)
-{
-   THREAD_1;
-   vg_async_call( &vg.main_tasks, async_vg_input_init, NULL );
-}
-
-void vg_input_free(void)
-{
-   for( int i=0; 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)
@@ -499,7 +490,7 @@ void vg_keyboard_key_string( vg_str *str, u32 key, int special_glyphs )
       vg_strcat( str, special_glyphs? "\x96 ": "down" );
    else {
       vg_strcat( str, "keyboard key #" );
-      vg_strcati32( str, key );
+      vg_strcati64( str, key, 10 );
    }
 }
 
@@ -516,7 +507,7 @@ void vg_mouse_button_string( vg_str *str, u32 button, int special_glyphs )
       vg_strcat( str, special_glyphs? "\x9c": "middle mouse" );
    else{
       vg_strcat( str, "mouse button #" );
-      vg_strcati32( str, button );
+      vg_strcati64( str, button, 10 );
    }
 }
 
@@ -540,7 +531,7 @@ void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis,
       vg_strcat( str, special_glyphs?"\x8d":"right stick vertical" );
    else{
       vg_strcat( str, "axis " );
-      vg_strcati32( str, axis );
+      vg_strcati64( str, axis, 10 );
    }
 }
 
index bbb3f26da92312ff31b7ab28839d00e71e939a63..c6320c401daad3f2032b1c89c45a72a0544d8c50 100644 (file)
@@ -1,10 +1,10 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_input.c"
+#else
 
 /* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
 #define VG_MAX_CONTROLLERS 4
 
-#include "vg_platform.h"
-
 extern f32 controller_deadzone;
 
 typedef u32 vg_input_op;
@@ -72,22 +72,22 @@ struct vg_input
 }
 extern vg_input;
 
+VG_API void _vg_input_register(void);
+VG_API void _vg_input_init(void);
+
 u8 vg_getkey( SDL_Keycode kc );
 void vg_process_inputs(void);
-void vg_input_register(void);
-void vg_input_init(void);
-void vg_input_free(void);
 struct vg_controller *vg_active_controller(void);
 u8 vg_controller_button( SDL_GameControllerButton button );
 f32 vg_controller_axis( SDL_GameControllerAxis axis );
-void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops, 
-                            void *out_result );
+void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops, void *out_result );
 const char *controller_button_str( SDL_GameControllerButton button );
 void vg_keyboard_key_string( vg_str *str, u32 key, int special_glyphs );
 void vg_mouse_button_string( vg_str *str, u32 button, int special_glyphs );
-void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis, 
-                         int special_glyphs );
+void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis, int special_glyphs );
 void vg_joy_string( vg_str *str, vg_input_op op, int special_glyphs );
 void vg_input_string( vg_str *str, vg_input_op *ops, int glyphs );
 void vg_input_device_event( SDL_Event *ev );
 void vg_input_controller_event( SDL_Event *ev );
+
+#endif
diff --git a/vg_io.c b/vg_io.c
index cebcd7eeb3ef3836d4fe51dd6fd1467b684229ae..e778989150e7a872ea072cd55ff90cb1a64e81d8 100644 (file)
--- a/vg_io.c
+++ b/vg_io.c
@@ -1,12 +1,3 @@
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#include "vg_io.h"
-#include "vg_platform.h"
-#include "vg_log.h"
-#include "vg_mem.h"
-#include <string.h>
-#include <errno.h>
-
 const char *dir_open_result_str[] = 
 {
    [k_dir_open_none] = "None",
@@ -174,41 +165,155 @@ void vg_dir_close( vg_dir *dir )
    dir->index = 0;
 }
 
-void vg_file_error_info( FILE *fp )
+VG_TIER_0 void vg_buffer_stream_open( vg_stream *stream, const void *buffer, u32 buffer_length, u32 mode )
 {
-   if( feof( fp )) 
+   VG_ASSERT( buffer_length );
+   VG_ASSERT( buffer );
+
+   stream->buffer = buffer;
+   stream->byte_limit = buffer_length;
+   stream->byte_count = 0;
+   stream->flags = mode;
+}
+
+VG_TIER_0 bool vg_file_stream_open( vg_stream *stream, const c8 *path, u32 mode )
+{
+#if defined( VG_ENGINE )
+   if( !_vg_thread_has_flags( VG_THREAD_BACKGROUND ) )
+      vg_warn( "Performance: I/O file stream opened in main thread. This will cause frame stalls!\n" );
+#endif
+   stream->fp = fopen( path, (mode & VG_STREAM_WRITE)? "wb": "rb" );
+   stream->byte_limit = 0;
+   stream->byte_count = 0;
+   stream->flags = mode | VG_STREAM_FILE;
+
+   if( stream->fp )
+      return 1;
+   else
    {
-      vg_error( "mdl_open: header too short\n" );
+      vg_error( "Failed to open disk stream '%s'\n", path );
+      return 0;
+   }
+}
+
+VG_TIER_0 u32 vg_stream_usable_length( vg_stream *stream, u32 length )
+{
+   if( stream->byte_limit && ((stream->byte_count + length) > stream->byte_limit) )
+      length = stream->byte_limit - stream->byte_count;
+   return length;
+}
+
+VG_TIER_0 u32 vg_stream_read( vg_stream *stream, void *buffer, u32 length )
+{
+   VG_ASSERT( stream->flags & VG_STREAM_READ );
+   u32 read_length = vg_stream_usable_length( stream, length );
+
+   if( stream->flags & VG_STREAM_FILE )
+   {
+      u64 l = (u32)fread( buffer, 1, read_length, stream->fp );
+      if( l != read_length )
+      {
+         if( !feof( stream->fp ) )
+         {
+            if( ferror( stream->fp ) )
+            {
+               fclose( stream->fp );
+               vg_fatal_error( "Read error (%u: %s)\n", (u32)errno, strerror(errno) );
+            }
+            else
+            {
+               fclose( stream->fp );
+               vg_fatal_error( "Unknown read error (fread)\n" );
+            }
+         }
+      }
+      read_length = l;
    }
    else
    {
-      if( ferror( fp )) vg_info( "fopen: %s\n", strerror(errno) );
-      else              vg_info( "fopen: unkown failure\n" );
+      for( u32 i=0; 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;
 
@@ -234,11 +339,9 @@ void *vg_file_read( vg_stack_allocator *stack, const char *path, u32 *size, bool
 
       if( text )
       {
-         buffer = vg_stack_extend_last( stack, 1 );
+         vg_stack_extend_last( stack, +1 );
          buffer[ current ++ ] = '\0';
       }
-
-      buffer = vg_stack_resize_last( stack, current );
                fclose( f );
 
       *size = (u32)current;
diff --git a/vg_io.h b/vg_io.h
index 6a84aaf984a7c1e5c8410e8c7d99beb44593afd2..032e4d5723c177f2a976af8d87a51e48bce2ccd9 100644 (file)
--- a/vg_io.h
+++ b/vg_io.h
@@ -1,31 +1,51 @@
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#pragma once
-#include "vg_platform.h"
-#include "vg_log.h"
-#include "vg_mem.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_io.c"
+#else
 
 typedef struct vg_dir vg_dir;
-#ifndef _WIN32
- #include <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
@@ -59,4 +79,5 @@ void *vg_file_read( vg_stack_allocator *stack, const char *path, u32 *size, bool
 
 bool vg_asset_write( const char *path, void *data, i64 size );
 const char *vg_path_filename( const char *path );
-void vg_file_error_info( FILE *fp );
+
+#endif
diff --git a/vg_kv.c b/vg_kv.c
index aaf68a950381c94d8c5bed8eb7f9f3d19b9bd304..382a2a66d187a3324a2d0bdc9fbd9fe0fb943379 100644 (file)
--- a/vg_kv.c
+++ b/vg_kv.c
@@ -1,12 +1,13 @@
-#include "vg_kv.h"
-
 static vg_kv *vg_kvs_newkv( vg_kvs *kvs )
 {
    void *kv_page;
    if( (kvs->kv_page_count == VG_KV_PAGE_COUNT) || (kvs->kv_page_offset == 0) )
    {
       u32 page_size = sizeof(vg_kv)*VG_KV_PAGE_COUNT;
-      kv_page = vg_stack_allocate( kvs->stack, page_size, 64, "KV Page" );
+
+      // FIXME FIXME: 64 is causing issues with metadata enabled buffers!!!!!!!!!!!!!!!!!!!!!!
+      // kv_page = vg_stack_allocate( kvs->stack, page_size, 64, "KV Page" );
+       kv_page = vg_stack_allocate( kvs->stack, page_size, 8, "KV Page" );
       vg_zero_mem( kv_page, page_size );
       kvs->kv_page_offset = vg_stack_offset( kvs->stack, kv_page );
       kvs->kv_page_count = 0;
@@ -29,7 +30,7 @@ void vg_kvs_init( vg_kvs *kvs, vg_stack_allocator *stack )
    kvs->root_offset = vg_stack_offset( kvs->stack, root_kv );
 }
 
-static u32 vg_kv_string_append( vg_kvs *kvs, c8 *string )
+static u32 vg_kv_string_append( vg_kvs *kvs, const c8 *string )
 {
    if( string == NULL )
       return 0;
@@ -38,8 +39,11 @@ static u32 vg_kv_string_append( vg_kvs *kvs, c8 *string )
    return vg_stack_offset( kvs->stack, buf );
 }
 
-u32 vg_kv_append( vg_kvs *kvs, u32 parent_offset, c8 *key, c8 *value )
+u32 vg_kv_append( vg_kvs *kvs, u32 parent_offset, const c8 *key, const c8 *value )
 {
+   if( parent_offset == 0 )
+      parent_offset = kvs->root_offset;
+
    vg_kv *kv = vg_kvs_newkv( kvs );
    u32 key_offset = vg_kv_string_append( kvs, key ),
        value_offset = vg_kv_string_append( kvs, value );
@@ -101,15 +105,12 @@ void vg_kv_link( vg_kv_parser *parser, u32 offset, u32 depth )
    parser->frame_stack[ depth ].latest_child_offset = offset;
 }
 
-void vg_kv_parse_buffer( vg_kv_parser *parser, c8 *buffer, u32 buffer_length )
+void vg_kv_parse_stream( vg_kv_parser *parser, vg_stream *in_stream )
 {
-   if( buffer_length == 0 )
-      buffer_length = 0xffffffff;
-
-   for( u32 i=0; 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;
 
@@ -130,7 +131,7 @@ void vg_kv_parse_buffer( vg_kv_parser *parser, c8 *buffer, u32 buffer_length )
          if( parser->token0_length )
          {
             parser->token0_length --;
-            parser->token0_buffer = vg_stack_extend_last( parser->kvs->stack, 1 );
+            vg_stack_extend_last( parser->kvs->stack, +1 );
             parser->token0_buffer[ parser->token0_length ] = '\0';
 
             if( parser->token1_length )
@@ -194,7 +195,7 @@ void vg_kv_parse_buffer( vg_kv_parser *parser, c8 *buffer, u32 buffer_length )
       {
          if( parser->token0_length )
          {
-            parser->token0_buffer = vg_stack_extend_last( parser->kvs->stack, 1 );
+            vg_stack_extend_last( parser->kvs->stack, +1 );
             parser->token0_buffer[ parser->token0_length-1 ] = c;
             parser->token0_length ++;
             parser->token0_hash = ((parser->token0_hash << 5) + parser->token0_hash) + (u32)c;
@@ -230,21 +231,30 @@ u32 vg_kv_type( vg_kvs *kvs, u32 kv_offset )
    return (kv->key_info >> 30) & 0x3;
 }
 
-c8 *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length )
+const c8 *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length )
 {
+   if( kv_offset == 0 )
+      return NULL;
+
    vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset );
-   *out_length = (kv->key_info >> 20) & 0x3FF;
-   return (*out_length)? vg_stack_pointer( kvs->stack, kv->key_offset ): NULL;
+   u32 length = (kv->key_info >> 20) & 0x3FF;
+   if( out_length )
+      *out_length = length;
+   return length? vg_stack_pointer( kvs->stack, kv->key_offset ): NULL;
 }
 
-c8 *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length )
-{ 
+const c8 *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length )
+{
+   if( kv_offset == 0 )
+      return NULL;
+
    if( vg_kv_type( kvs, kv_offset ) == 0x0 )
       return NULL;
    else
    {
       vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset );
-      *out_length = kv->value.length;
+      if( out_length )
+         *out_length = kv->value.length;
       return vg_stack_pointer( kvs->stack, kv->key_offset + kv->value.offset_from_key );
    }
 }
@@ -272,19 +282,22 @@ u32 vg_kv_child( vg_kvs *kvs, u32 root_offset, u32 index )
    else return 0;
 }
 
-u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, c8 *key )
+u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, const c8 *key )
 {
+   if( root_offset == 0 )
+      root_offset = kvs->root_offset;
+
    u32 hash = vg_strdjb2( key );
    u32 child_offset = vg_kv_child( kvs, root_offset, 0 );
    while( child_offset )
    {
       vg_kv *kv = vg_stack_pointer( kvs->stack, child_offset );
       u32 key_length;
-      c8 *child_key = vg_kv_key( kvs, child_offset, &key_length );
+      const c8 *child_key = vg_kv_key( kvs, child_offset, &key_length );
       if( ((kv->key_info ^ hash) & 0xFFFFF) == 0 )
       {
          u32 key_length;
-         c8 *child_key = vg_kv_key( kvs, child_offset, &key_length );
+         const c8 *child_key = vg_kv_key( kvs, child_offset, &key_length );
          if( child_key )
          {
             for( u32 i=0; i<key_length; i ++ )
@@ -306,7 +319,7 @@ static void vg_kv_write_indent( vg_kv_write *w )
       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;
@@ -341,7 +354,7 @@ static void vg_kv_write_string( vg_kv_write *w, c8 *string, u32 length )
    if( delim ) fputc( delim, w->fp );
 }
 
-void vg_kv_write_block( vg_kv_write *w, c8 *name, u32 name_length )
+void vg_kv_write_block( vg_kv_write *w, const c8 *name, u32 name_length )
 {
    vg_kv_write_indent( w );
    if( name )
@@ -364,7 +377,7 @@ void vg_kv_end_block( vg_kv_write *w )
    fputc( '\n', w->fp );
 }
 
-void vg_kv_write_kv( vg_kv_write *w, c8 *key, u32 key_len, c8 *value, u32 value_len )
+void vg_kv_write_kv( vg_kv_write *w, const c8 *key, u32 key_len, const c8 *value, u32 value_len )
 {
    vg_kv_write_indent( w );
    vg_kv_write_string( w, key, key_len );
@@ -387,10 +400,13 @@ void vg_kv_parser_print_info( vg_kv_parser *parser )
 
 void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset )
 {
+   if( root_offset == 0 )
+      root_offset = kvs->root_offset;
+
    VG_ASSERT( vg_kv_type( kvs, root_offset ) == 0x0 );
 
    u32 root_len;
-   c8 *root_str = vg_kv_key( kvs, root_offset, &root_len );
+   const c8 *root_str = vg_kv_key( kvs, root_offset, &root_len );
    vg_kv_write_block( w, root_str, root_len );
 
    u32 child_offset = vg_kv_child( kvs, root_offset, 0 );
@@ -401,10 +417,10 @@ void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset )
       else
       {
          u32 key_len;
-         c8 *key_str = vg_kv_key( kvs, child_offset, &key_len );
+         const c8 *key_str = vg_kv_key( kvs, child_offset, &key_len );
 
          u32 value_len;
-         c8 *value_str = vg_kv_value( kvs, child_offset, &value_len );
+         const c8 *value_str = vg_kv_value( kvs, child_offset, &value_len );
 
          if( key_str && value_str )
             vg_kv_write_kv( w, key_str, key_len, value_str, value_len );
@@ -415,3 +431,77 @@ void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset )
 
    vg_kv_end_block( w );
 }
+
+bool vg_kv_read_vu32( vg_kvs *kvs, u32 root_offset, const c8 *key, u32 *default_value, u32 *out_value, u32 len )
+{
+   bool good = 1;
+   vg_strp s = { .buffer = vg_kv_value( kvs, vg_kv_find( kvs, root_offset, key ), NULL ) };
+   for( u32 i=0; 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 );
+}
diff --git a/vg_kv.h b/vg_kv.h
index 8976caa9072bb6d27496aed1521781d605bc4e2a..c76b9c03baf79fb45c3a833117b8f464bfe387c1 100644 (file)
--- a/vg_kv.h
+++ b/vg_kv.h
@@ -1,6 +1,6 @@
-#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
 
@@ -16,11 +16,9 @@ void vg_kvs_init( vg_kvs *kvs, vg_stack_allocator *stack );
 
 /* 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.
@@ -30,13 +28,16 @@ void vg_kv_link( vg_kv_parser *parser, u32 offset, u32 depth );
 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
@@ -44,12 +45,14 @@ u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, c8 *key );
  *  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 );
@@ -106,3 +109,5 @@ struct vg_kv_write
    FILE *fp;
    u32 depth;
 };
+
+#endif
index c74a3d5ca1be35ec256cf5db528aa56041cd78c9..e54eb906b19ec9e6fe8041e576524b8df3b5d87b 100644 (file)
@@ -1,56 +1,17 @@
-/* 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 );
@@ -80,24 +41,10 @@ static void async_vg_lines_init( void *_ )
    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 );
index ab22c3dc0a6a436b29500e11d7ca2e46c67416e0..53bd51acf58b085567b19ba86fc243f2e16515e4 100644 (file)
@@ -1,8 +1,6 @@
-/* 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;
 
@@ -22,6 +20,7 @@ struct vg_lines
    u32 enabled, 
        render;
        
+   vg_stack_allocator vertex_stack;
    struct vg_lines_vert
    {
       v3f co;
@@ -34,6 +33,9 @@ struct vg_lines
 }
 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 );
@@ -46,5 +48,5 @@ void vg_line_arrow( line_co co, line_co dir, 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
index d13a86574c88aa8456454740c3e140007d8f69cc..ddde5bc486df461bc6759538bc8cf14c9ddbdad2 100644 (file)
@@ -1,73 +1,10 @@
-#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 );
@@ -75,56 +12,19 @@ void vg_loader_init(void)
    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);
@@ -133,19 +33,21 @@ void vg_loader_render_ring( f32 opacity )
 
    opacity *= opacity;
 
-   glUseProgram( _shader_loader.id );
-       glUniform1f( glGetUniformLocation( _shader_loader.id, "uTime" ), vg.time_real );
-   f32 ratio = (f32)vg.window_x / (f32)vg.window_y;
-   glUniform1f( glGetUniformLocation( _shader_loader.id, "uRatio"), ratio );
-   glUniform1f( glGetUniformLocation( _shader_loader.id, "uOpacity"), opacity );
+   shader_vgloader_use();
+   shader_vgloader_uInverseRatio( (v2f){1.0f,1.0f} );
+   shader_vgloader_uTime( vg.time_real );
+
+   f32 ratio = (f32)_vg_window.w / (f32)_vg_window.h;
+   shader_vgloader_uRatio( ratio );
+   shader_vgloader_uOpacity( opacity );
    glBindVertexArray( vg_loader.vao );
    glDrawArrays( GL_TRIANGLES, 0, 6 );
 
    ui_prerender( &vg_ui.ctx );
-   vg_ui_set_screen( vg.window_x, vg.window_y );
+   vg_ui_set_screen( _vg_window.w, _vg_window.h );
 
 
-   ui_rect test = { 0, vg.window_y - 28*2, vg.window_x, 28 };
+   ui_rect test = { 0, _vg_window.h - 28*2, _vg_window.w, 28 };
    if( vg_loader.information_for_user )
    {
       vg_ui.ctx.font = &vgf_default_large;
@@ -159,35 +61,11 @@ void vg_loader_render_ring( f32 opacity )
 
 void vg_loader_render(void)
 {
-   glViewport( 0,0, vg.window_x, vg.window_y );
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
+   glViewport( 0,0, _vg_window.w, _vg_window.h );
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
    glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
    vg.loader_ring = 1.0f;
 }
 
-/*
- * Schedule something to be ran now, freed later. Checks in with engine status
- */
-void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void), const char *alias )
-{
-   THREAD_1;
-
-   u64 t0 = SDL_GetPerformanceCounter();
-   if( fn_load )
-      fn_load();
-
-   u64 function_run_time = SDL_GetPerformanceCounter() - t0;
-   f64 run_time_seconds = (f64)function_run_time / (f64)SDL_GetPerformanceFrequency();
-   vg_info( "%s: %fs\n", alias, run_time_seconds );
-
-   if( fn_free )
-   {
-      struct loader_free_step step;
-      step.fn_free = fn_free;
-
-      VG_ASSERT( vg_loader.step_count < VG_ARRAY_LEN(vg_loader.step_buffer) );
-      vg_loader.step_buffer[ vg_loader.step_count ++ ] = step;
-   }
-}
-
index 9dfb600e40ee4ab087240dfdf7375233475003ab..eb17e1c6869f9e50aefcdbb059ce3483b03e0277 100644 (file)
@@ -1,17 +1,6 @@
-
-/*
- * Copyright 2021-2025 (C) Mount0 Software, Harry Godden - All Rights Reserved
- * -----------------------------------------------------------------------------
- *
- * Splash / load screen
- *
- * -----------------------------------------------------------------------------
- */
-
-#pragma once
-
-#include "vg_platform.h"
-#include "vg_engine.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_loader.c"
+#else
 
 #define VG_ENGINE_READY 0x1
 #define VG_CLIENT_READY 0x2
@@ -36,15 +25,10 @@ struct vg_loader
 }
 extern vg_loader;
 
-//void vg_loader_start( void(*pfn)(void *data), void *data );
-void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void), const char *alias );
-void vg_loader_set_user_information( const char *information );
+VG_API void _vg_loader_init(void);
+
+VG_API void _vg_loader_set_user_information( const c8 *information );
 void vg_loader_render(void);
 void vg_loader_render_ring( f32 opacity );
-void vg_loader_free(void);
-void vg_loader_atexit(void);
-void vg_loader_init(void);
 
-//int vg_loader_availible(void);
-#define vg_loader_step( FN, FN_FREE )\
-   _vg_loader_step( FN, FN_FREE, #FN )
+#endif
index 1eb4941f505b78eedd8e0756d107ff9364ca212a..e8777d25bd9c04d50f5ee31b62129073827b126a 100644 (file)
--- a/vg_log.c
+++ b/vg_log.c
@@ -1,17 +1,3 @@
-#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 )
@@ -26,7 +12,7 @@ static void _vg_log_append_line( const char *str )
       vg_log.log_line_current = 0;
 }
 
-void vg_log_init(void)
+VG_API void _vg_log_pre_init(void)
 {
    vg_log.initialized = 1;
 
@@ -39,19 +25,7 @@ void vg_log_init(void)
    if( !vg_strgood( &log_path ) )
       exit(-2);
 
-#ifdef VG_MULTITHREAD
- #ifdef VG_ENGINE
-   vg_log.mutex = SDL_CreateMutex();
-   if( vg_log.mutex == NULL )
-   {
-      printf( "SDL2 Error: %s\n", SDL_GetError() );
-      exit(-1);
-   }
- #else
-   if( pthread_mutex_init( &vg_log.lock, NULL ) )
-      exit(-1);
- #endif
-#endif
+   VG_ASSERT( VG_MUTEX_INIT( vg_log.lock ) );
 }
 
 void _vg_logx_va( FILE *file, const char *location, const char *prefix,
@@ -65,19 +39,12 @@ void _vg_logx_va( FILE *file, const char *location, const char *prefix,
       return;
    }
 
-#ifdef VG_MULTITHREAD
- #ifdef VG_ENGINE
-   if( SDL_LockMutex( vg_log.mutex ) )
-      vg_fatal_error( "" );
- #else
-   pthread_mutex_lock( &vg_log.lock );
- #endif
-#endif
+   VG_MUTEX_LOCK( vg_log.lock );
 
        char buffer[4096], line[96];
    vsnprintf( buffer, VG_ARRAY_LEN(buffer), fmt, args );
 
-#ifdef VG_ENGINE
+#if defined( VG_ENGINE )
    if( vg_log.plain_output_file )
    {
       fputs( prefix, vg_log.plain_output_file );
@@ -86,7 +53,7 @@ void _vg_logx_va( FILE *file, const char *location, const char *prefix,
    }
 #endif
 
-   int line_length = snprintf( line, 90, "%s%7s" KNRM "|%s ", colour, prefix, colour );
+   int line_length = snprintf( line, 90, "%s%3s" KNRM "|%s ", colour, prefix, colour );
 
    for( u32 i=0; i<VG_ARRAY_LEN(buffer); i ++ )
    {
@@ -114,35 +81,26 @@ void _vg_logx_va( FILE *file, const char *location, const char *prefix,
       {
          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, 
@@ -153,7 +111,7 @@ 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,
@@ -162,8 +120,6 @@ void vg_logx( FILE *file,
    va_end( args );
 }
 
-/* FIXME: THIS NEEDS ITS OWN FILE, VG_PLATFORM.C */
-
 void vg_fatal_error( const char *fmt, ... )
 {
    va_list args;
@@ -173,27 +129,22 @@ void vg_fatal_error( const char *fmt, ... )
    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;
@@ -267,6 +218,7 @@ void vg_fatal_exit( const char *comment )
    backtrace_symbols_fd( functions, count, fd );
 #endif
 
+#if defined( VG_RELEASE_MODE )
    vg_strcat( &line, "\n\nVG Console log\n"
                      "-----------------------------------\n" );
 
@@ -302,5 +254,7 @@ void vg_fatal_exit( const char *comment )
    }
 
    close( fd );
+#endif 
+
    exit(-1);
 }
index 9781115e395ed6a6fa50909da6cba30656a21a28..6c788b047337aeb3c0fbc0f88b86b574327fd2b4 100644 (file)
--- a/vg_log.h
+++ b/vg_log.h
@@ -1,7 +1,6 @@
-#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
@@ -9,17 +8,26 @@
         "                                    "
 
 #define vg_success( ... ) \
- vg_logx(stdout,VG_LOG_WHERE,"success",KGRN,__VA_ARGS__)
+ vg_logx(stdout,VG_LOG_WHERE,"ok",KGRN,__VA_ARGS__)
 #define vg_info( ... ) \
- vg_logx(stdout,VG_LOG_WHERE,"info",KNRM,__VA_ARGS__)
+ vg_logx(stdout,VG_LOG_WHERE,"inf",KNRM,__VA_ARGS__)
 #define vg_warn( ... ) \
- vg_logx(stdout,VG_LOG_WHERE,"warn",KYEL,__VA_ARGS__ )
+ vg_logx(stdout,VG_LOG_WHERE,"wrn",KYEL,__VA_ARGS__ )
 #define vg_error( ... ) \
- vg_logx(stdout,VG_LOG_WHERE,"error",KRED,__VA_ARGS__)
+ vg_logx(stdout,VG_LOG_WHERE,"err",KRED,__VA_ARGS__)
 #define vg_low( ... ) \
- vg_logx(stdout,VG_LOG_WHERE,"log",KWHT,__VA_ARGS__)
+ vg_logx(stdout,VG_LOG_WHERE,"log",KBLK,__VA_ARGS__)
 #define vg_logsteam( ... ) \
- vg_logx(stdout,VG_LOG_WHERE,"steam",KBLU,__VA_ARGS__)
+ vg_logx(stdout,VG_LOG_WHERE,"stm",KBLU,__VA_ARGS__)
+
+/* TODO: Virtualize these colour codes to be our own. These vt codes are uncomfy to code against + look bad if they 
+ *       accidently leak outside of the terminal.
+ *       
+ *       Instead we can do like:
+ *        "Jim is [c red]a strange fellow"
+ *
+ *       Or maybe there is a common standard?
+ */
 
 #define KNRM  "\x1B[0m"
 #define KBLK  "\x1B[30m"
 #define KCYN  "\x1B[36m"
 #define KWHT  "\x1B[37m"
 
-#define PRINTF_v2f( V2 ) "%.4f %.4f\n",           V2[0], V2[1]
-#define PRINTF_v3f( V3 ) "%.4f %.4f %.4f\n",      V3[0], V3[1], V3[2]
-#define PRINTF_v4f( V4 ) "%.4f %.4f %.4f %.4f\n", V4[0], V4[1], V4[2], V4[3]
-
-/* fuck off you stupid fucks */
-#ifdef _WIN32
- #define PRINTF_U64 "%llu"
- #define PRINTF_X64 "%llx"
-#else
- #define PRINTF_U64 "%lu"
- #define PRINTF_X64 "%lx"
-#endif
-
-#ifdef VG_ENGINE
- #include "dep/sdl/include/SDL.h"
-#endif
-
-#ifdef VG_ENGINE
- #define VG_MULTITHREAD
-#endif
-
 struct vg_log
 {
    char crash_path[256];
@@ -59,19 +46,15 @@ struct vg_log
    u32  log_line_count, log_line_current;
 
    FILE *plain_output_file;
-
    bool initialized;
-#ifdef VG_MULTITHREAD
- #ifdef VG_ENGINE
-   SDL_mutex *mutex;
- #else
-   pthread_mutex_t lock;
- #endif
+
+#if defined( VG_MULTITHREAD )
+   vg_mutex lock;
 #endif
 }
 extern vg_log;
 
-void vg_log_init(void);
+VG_API void _vg_log_pre_init(void);
 void vg_log_free(void);
 
 void vg_logx( FILE *file, 
@@ -83,3 +66,5 @@ void _vg_logx_va( FILE *file,
                   const char *location, const char *prefix,
                   const char *colour,
                   const char *fmt, va_list args );
+
+#endif
diff --git a/vg_m.h b/vg_m.h
index d582bf0fd0b805e0c657b20aa6f2b783da6ac1a2..5a92c4c666a32744fd39b8251060acaaa86982f2 100644 (file)
--- a/vg_m.h
+++ b/vg_m.h
@@ -1,4 +1,7 @@
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved 
+#if !defined( VG_MATH_HEADER )
+# define VG_MATH_HEADER
+
+/* Copyright (C) 2021-2025 Harry Godden (hgn) - All Rights Reserved 
  *
  *  0. Misc
  *  1. Scalar operations
  *    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
 
@@ -2682,3 +2679,5 @@ static void vg_rgb_hsv( v3f rgb, v3f hsv ){
 
    hsv[0] = vg_fractf( hsv[0] * (60.0f/360.0f) );
 }
+
+#endif
index 15ad09097d677a69fa25534ad56472d89d3574f7..71c1f2e1fc4235a34d4bd84ecf447439b333f04b 100644 (file)
--- a/vg_magi.c
+++ b/vg_magi.c
@@ -1,6 +1,3 @@
-#include "vg_magi.h"
-#include "vg_console.h"
-
 struct vg_magi _vg_magi;
 
 struct vg_magi_panel *_vg_magi_open( ui_px w, ui_px h, u32 flags )
@@ -263,7 +260,7 @@ void vg_magi_save(void)
 
       if( magi->flags & VG_MAGI_PERSISTENT )
       {
-         ui_rect vp = {0,0,vg.window_x,vg.window_y};
+         ui_rect vp = {0,0,_vg_window.w,_vg_window.h};
          ui_px c[2], p[2];
          vg_magi_getcorner( vp, magi->corner, c );
          p[0] = magi->rect[0] - c[0];
@@ -311,7 +308,7 @@ static int cmd_vg_magi_dim( int argc, const char *argv[] )
 
    if( argc >= 6 )
    {
-      ui_rect vp = {0,0,vg.window_x,vg.window_y};
+      ui_rect vp = {0,0,_vg_window.w,_vg_window.h};
       ui_px c[2];
       magi->corner = atoi( argv[5] );
       vg_magi_getcorner( vp, magi->corner, c );
@@ -322,7 +319,7 @@ static int cmd_vg_magi_dim( int argc, const char *argv[] )
    return 1;
 }
 
-void vg_magi_init(void)
+VG_API void _vg_magi_register(void)
 {
    vg_console_reg_cmd( "magi_pos", cmd_vg_magi_dim, NULL );
 }
index 029091479c745cc0ad1a10e76902ce3d5205e88e..d986e73ee86fc9ca6d2e9da54229a78e95fe05c6 100644 (file)
--- a/vg_magi.h
+++ b/vg_magi.h
@@ -1,4 +1,7 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_magi.c"
+#else
+
 #define VG_MAGI_MAX_PANELS 8
 
 #define VG_MAGI_RESIZEABLE 0x1
@@ -44,9 +47,12 @@ struct vg_magi
 }
 extern _vg_magi;
 
+VG_API void _vg_magi_register(void);
+
 struct vg_magi_panel *_vg_magi_open( ui_px w, ui_px h, u32 flags );
 void _vg_magi_render( ui_context *ctx );
-void vg_magi_init(void);
 void vg_magi_save(void);
 void vg_magi_restore(void);
 void _vg_magi_area_change( i32 d[2] );
+
+#endif
index 9d3a730741e1dc7bee1460999974b5e5239a3d32..f5acab69d93b50e37ed3130680d6f0ad67d61a73 100644 (file)
--- a/vg_mem.c
+++ b/vg_mem.c
@@ -1,13 +1,3 @@
-#pragma once
-
-#include "vg_platform.h"
-#include "vg_log.h"
-#include "vg_mem.h"
-
-#include <stdlib.h>
-#include <malloc.h>
-#include <stdio.h>
-
 void *vg_malloc( u64 size )
 {
    void *buf = malloc( size );
@@ -57,94 +47,37 @@ u32 vg_align4( u32 s )
 
 void vg_stack_init( vg_stack_allocator *stack, void *buffer, u32 capacity, const char *debug_name )
 {
-   VG_ASSERT( sizeof( vg_allocation_meta ) == 24 );
    VG_ASSERT( sizeof( vg_stack_allocator ) == 24 );
-
    vg_zero_mem( stack, sizeof(vg_stack_allocator) );
    stack->data = buffer? buffer: vg_malloc( capacity );
    stack->capacity = capacity;
    stack->flags = buffer? 0: VG_STACK_ALLOCATOR_BUFFER_FROM_MALLOC;
 }
 
-vg_stack_allocator *vg_stack_make_substack( vg_stack_allocator *parent, u32 capacity, const char *debug_name )
-{
-   void *block = vg_stack_allocate( parent, sizeof(vg_stack_allocator) + capacity, 8, debug_name );
-   vg_stack_set_flags_last( parent, VG_ALLOCATION_FLAG_IS_STACK );
-
-   vg_stack_allocator *stack = block;
-   vg_stack_init( stack, block + sizeof(vg_stack_allocator), capacity, debug_name );
-   return stack;
-}
-
-static void vg_stack_growmaybe( vg_stack_allocator *stack, u32 new_usage )
-{
-   if( new_usage > stack->capacity )
-   {
-      if( stack->flags & VG_STACK_ALLOCATOR_DOUBLE_IF_FULL )
-      {
-         u32 cap = stack->capacity * 2;
-         if( new_usage > cap )
-            cap = new_usage;
-
-         stack->data = vg_realloc( stack->data, cap );
-         stack->capacity = cap;
-      }
-      else
-      {
-         vg_fatal_error( "Stack allocator overflow (capacity: %u, used: %u, new_usage: %u)\n",
-                         stack->capacity, stack->used, new_usage );
-      }
-   }
-}
-
 void *vg_stack_allocate( vg_stack_allocator *stack, u32 size, u32 alignment, const char *debug_name )
 {
+   if( !stack )
+      return vg_malloc( size );
    VG_ASSERT( (alignment >= 1) && (alignment <= 64) );
-   VG_ASSERT( ~stack->used & 0x10000000 );
-   VG_ASSERT( ~size        & 0x10000000 );
-
-   u32 block_size = size;
-   if( stack->flags & VG_STACK_ALLOCATOR_METADATA )
-   {
-      if( alignment > 8 )
-      {
-         vg_fatal_error( "Allocation wants %u bytes alignment, but allocator has METADATA flag set."
-                         "  Alignment with this flag is always 8!\n", alignment );
-      }
-      block_size += sizeof( vg_allocation_meta );
-      alignment = 8;
-   }
-
-   u32 previous_base_offset = stack->used - stack->last_allocation_totalsize;
-   stack->last_allocation_totalsize = block_size;
-
-   u32 new_usage = stack->used + block_size + alignment;
-   vg_stack_growmaybe( stack, new_usage );
+   VG_ASSERT( ~stack->offset & 0x10000000 );
+   VG_ASSERT( ~size          & 0x10000000 );
 
-   while( ((u64)stack->data + stack->used) & (alignment-1) )
-      stack->used ++;
-
-   void *block = (void *)stack->data + stack->used;
-   stack->used += block_size;
-
-   if( stack->flags & VG_STACK_ALLOCATOR_METADATA )
+   u32 new_usage = stack->offset + size + alignment;
+   if( new_usage > stack->capacity )
    {
-      vg_allocation_meta *meta = block;
-      memset( block, 0, sizeof(vg_allocation_meta) );
-      meta->name = debug_name;
-      meta->size = size;
-      meta->previous_offset = previous_base_offset;
-      meta->flags = 0;
-
-      return (void *)meta->data;
+      vg_fatal_error( "Stack allocator overflow (capacity: %u, used: %u, new_usage: %u)\n",
+                      stack->capacity, stack->offset, new_usage );
    }
-   else return block;
+   while( ((u64)stack->data + stack->offset) & (alignment-1) )
+      stack->offset ++;
+   void *block = stack->data + stack->offset;
+   stack->offset += size;
+   return block;
 }
 
 void vg_stack_clear( vg_stack_allocator *stack )
 {
-   stack->used = 0;
-   stack->last_allocation_totalsize = 0;
+   stack->offset = 0;
 }
 
 void vg_stack_free( vg_stack_allocator *stack )
@@ -154,19 +87,6 @@ void vg_stack_free( vg_stack_allocator *stack )
    vg_zero_mem( stack, sizeof(vg_stack_allocator) );
 }
 
-void vg_stack_set_flags( vg_stack_allocator *stack, u16 append_flags )
-{
-   if( append_flags & VG_STACK_ALLOCATOR_DOUBLE_IF_FULL )
-      if( !(stack->flags & VG_STACK_ALLOCATOR_BUFFER_FROM_MALLOC) )
-         vg_fatal_error( "Cannot append DOUBLE_IF_FULL to stack allocator, since buffer wasn't from malloc.\n" );
-
-   if( append_flags & VG_STACK_ALLOCATOR_METADATA )
-      if( stack->used )
-         vg_fatal_error( "Cannot append METADATA to stack allocator after allocating something.\n" );
-
-   stack->flags |= append_flags;
-}
-
 u32 vg_stack_offset( vg_stack_allocator *stack, void *pointer )
 {
    return pointer - stack->data;
@@ -177,56 +97,9 @@ void *vg_stack_pointer( vg_stack_allocator *stack, u32 offset )
    return stack->data + offset;
 }
 
-void vg_stack_set_flags_last( vg_stack_allocator *stack, u16 append_flags )
-{
-   if( !(stack->flags & VG_STACK_ALLOCATOR_METADATA) )
-      vg_fatal_error( "Stack allocator does not have the METADATA flag set.\n" );
-
-   vg_allocation_meta *meta = (void *)stack->data + (stack->used - stack->last_allocation_totalsize);
-   meta->flags |= append_flags;
-}
-
-void *vg_stack_resize_last( vg_stack_allocator *stack, u32 new_size )
+void vg_stack_extend_last( vg_stack_allocator *stack, i32 extra_bytes )
 {
-   u32 base_offset = stack->used - stack->last_allocation_totalsize,
-       block_size  = new_size;
-
-   if( stack->flags & VG_STACK_ALLOCATOR_METADATA )
-   {
-      vg_allocation_meta *meta = (void *)stack->data + base_offset;
-      meta->size = new_size;
-      block_size += sizeof( vg_allocation_meta );
-   }
-
-   u32 new_usage = base_offset + block_size;
-   vg_stack_growmaybe( stack, new_usage );
-   stack->used = new_usage;
-   stack->last_allocation_totalsize = block_size;
-   return stack->data + (new_usage - new_size);
-}
-
-void *vg_stack_extend_last( vg_stack_allocator *stack, u32 extra_bytes )
-{
-   u32 current_size = stack->last_allocation_totalsize;
-   if( stack->flags & VG_STACK_ALLOCATOR_METADATA )
-      current_size -= sizeof(vg_allocation_meta);
-   return vg_stack_resize_last( stack, current_size + extra_bytes );
-}
-
-void *vg_stack_pop_last( vg_stack_allocator *stack )
-{
-   if( !(stack->flags & VG_STACK_ALLOCATOR_METADATA) )
-      vg_fatal_error( "Stack allocator does not have the METADATA flag set.\n" );
-
-   if( !stack->last_allocation_totalsize )
-      return NULL;
-
-   u32 base_offset = stack->used - stack->last_allocation_totalsize;
-   vg_allocation_meta *meta = (void *)stack->data + base_offset;
-
-   stack->last_allocation_totalsize = base_offset - meta->previous_offset;
-   stack->used = base_offset;
-   return (void *)meta->data;
+   stack->offset += extra_bytes;
 }
 
 static void vg_mem_print_size( u32 bytes, char buf[32] )
@@ -275,3 +148,41 @@ void vg_mem_dumphex( FILE *fp, void *buffer, u32 offset, u32 bytes )
 
    fprintf( fp, "----------------------------------------------------------------------------\n" );
 }
+
+#if !defined( VG_ENGINE )
+
+#define VG_TEMP_STACK_MAX 8
+vg_stack_allocator _temp_allocator;
+u32 _temp_offsets[ VG_TEMP_STACK_MAX ];
+u32 _temp_stack_depth = 0;
+
+VG_API u32 _vg_start_temp_frame(void)
+{
+   if( !_temp_allocator.data )
+      vg_stack_init( &_temp_allocator, NULL, VG_MB(20), "Temp allocator" );
+   VG_ASSERT( _temp_stack_depth < VG_TEMP_STACK_MAX );
+   u32 offset = _temp_allocator.offset;
+   _temp_offsets[ _temp_stack_depth ++ ] = offset;
+   return offset;
+}
+
+VG_API void _vg_end_temp_frame( u32 whence )
+{
+   VG_ASSERT( _temp_stack_depth );
+   _temp_stack_depth --;
+   VG_ASSERT( _temp_offsets[ _temp_stack_depth ] == whence );
+   _temp_allocator.offset = whence;
+}
+
+VG_API void *_vg_temp_alloc( u32 bytes, u32 alignment )
+{
+   VG_ASSERT( _temp_stack_depth );
+   return vg_stack_allocate( &_temp_allocator, bytes, alignment, NULL );
+}
+
+VG_API vg_stack_allocator *_vg_temp_stack(void)
+{
+   return &_temp_allocator;
+}
+
+#endif
index 7a230c3688d1ebd560cc5967fff638598d229541..fd91e66caee014671206733ef8783260a52477fc 100644 (file)
--- a/vg_mem.h
+++ b/vg_mem.h
@@ -1,11 +1,11 @@
-#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 );
@@ -13,27 +13,6 @@ void vg_free( void *buf );
 void vg_zero_mem( void *buffer, u32 length );
 
 typedef struct vg_stack_allocator vg_stack_allocator;
-typedef struct vg_allocation_meta vg_allocation_meta;
-
-#define VG_ALLOCATION_FLAG_IS_STACK      0x1
-
-/* 24 bytes + size */
-struct vg_allocation_meta
-{
-   const char *name;
-
-   u32 size;
-   u32 previous_offset;
-   u16 flags;
-   u16 unused0;
-   u32 unused1;
-
-   u64 data[];
-};
-
-/* configuration flags */
-#define VG_STACK_ALLOCATOR_METADATA       0x1
-#define VG_STACK_ALLOCATOR_DOUBLE_IF_FULL 0x2
 
 /* system flags */
 #define VG_STACK_ALLOCATOR_BUFFER_FROM_MALLOC 0x4
@@ -42,11 +21,9 @@ struct vg_allocation_meta
 struct vg_stack_allocator
 {
    u32 capacity; /* bytes */
-   u32 used;
-   u32 last_allocation_totalsize;
+   u32 offset;
    u16 flags;
-   u16 unused0;
-
+   u16 unused0, unused1;
    void *data;
 };
 
@@ -63,23 +40,21 @@ u32 vg_align4( u32 s );
  */
 void vg_stack_init( vg_stack_allocator *stack, void *buffer, u32 capacity, const char *debug_name );
 void *vg_stack_allocate( vg_stack_allocator *stack, u32 size, u32 alignment, const char *debug_name );
+void *vg_stack_allocate_locked();
+
 void vg_stack_clear( vg_stack_allocator *stack );
 void vg_stack_free( vg_stack_allocator *stack );
 void vg_stack_set_flags( vg_stack_allocator *stack, u16 append_flags );
-vg_stack_allocator *vg_stack_make_substack( vg_stack_allocator *parent, u32 capacity, const char *debug_name );
-
-#define VG_STACK_ALLOCATE_STRUCT( STACK, STRUCT ) vg_stack_allocate( STACK, sizeof(STRUCT), alignof(STRUCT), NULL )
+void vg_stack_extend_last( vg_stack_allocator *stack, i32 extra_bytes );
 
-__attribute__((warn_unused_result))
-void *vg_stack_resize_last( vg_stack_allocator *stack, u32 new_size );
-
-__attribute__((warn_unused_result))
-void *vg_stack_extend_last( vg_stack_allocator *stack, u32 extra_bytes );
 u32 vg_stack_offset( vg_stack_allocator *stack, void *pointer );
 void *vg_stack_pointer( vg_stack_allocator *stack, u32 offset );
 
-/*
- * The following are availible if allocator has the flag METADATA set
- */
-void vg_stack_set_flags_last( vg_stack_allocator *stack, u16 append_flags );
-void *vg_stack_pop_last( vg_stack_allocator *stack );
+#if !defined( VG_ENGINE )
+VG_API u32 _vg_start_temp_frame(void);
+VG_API void _vg_end_temp_frame( u32 whence );
+VG_API void *_vg_temp_alloc( u32 bytes, u32 alignment );
+VG_API vg_stack_allocator *_vg_temp_stack(void);
+#endif
+
+#endif
index 44ce62cd69171d9400ffe6005ecf2192b0b06faf..68e9d32f00dc7104033d79a8c3100e5d74a2227d 100644 (file)
@@ -1,7 +1,3 @@
-#include "vg_platform.h"
-#include "vg_mem.h"
-#include "vg_mem_pool.h"
-
 void vg_pool_switch( vg_pool *pool, vg_pool_chain *source, vg_pool_chain *dest, u16 which )
 {
    VG_ASSERT( which );
@@ -65,7 +61,7 @@ u16 vg_pool_reference( vg_pool *pool, u16 pool_id, bool increment )
 void vg_pool_init( vg_pool *pool, vg_pool_chain *start_chain, u16 count, vg_stack_allocator *stack )
 {
    u32 size = sizeof(vg_pool_node) * count;
-   pool->nodes = stack? vg_stack_allocate( stack, size, 8, "Pool Nodes" ): vg_malloc( size );
+   pool->nodes = vg_stack_allocate( stack, size, 8, "Pool Nodes" );
    pool->count = count;
 
    for( u16 i=0; i<count; i ++ )
index d033bbb0e4f7d25a43300cf8b4bce4972f72eea1..7a518d40c562642da232c395c9e3aaa5b854ad01 100644 (file)
@@ -1,6 +1,6 @@
-#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
@@ -35,3 +35,5 @@ u16  vg_pool_reference( vg_pool *pool, u16 pool_id, bool increment );
 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
index d7f15ad25c0e73a6551ab1064bef23cc9d9f02d0..f92da026bfb381120f0e06dd052101105b9311e5 100644 (file)
@@ -1,9 +1,3 @@
-#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 )
@@ -28,10 +22,20 @@ void *vg_queue_tail_data( vg_queue *q )
    else                      return NULL;
 }
 
+u32 vg_queue_offset( vg_queue *q, void *pointer )
+{
+   return pointer - q->buffer;
+}
+
+void *vg_queue_pointer( vg_queue *q, u32 offset )
+{
+   return q->buffer + offset;
+}
+
 /*
- * Allocate memory on the queue. Returns NULL if allocation failed for any reason & out_offset undefined
+ * Allocate memory on the queue. Returns NULL if allocation failed for any reason 
  */
-void *vg_queue_alloc( vg_queue *q, u32 size, u32 *out_offset )
+void *vg_queue_alloc( vg_queue *q, u32 size )
 {
    u32 total = vg_align8(size) + sizeof(vg_queue_item);
    if( total > q->size )
@@ -80,15 +84,12 @@ void *vg_queue_alloc( vg_queue *q, u32 size, u32 *out_offset )
    }
 
    vg_queue_item *item = (vg_queue_item *)(q->buffer + new_item_offset);
-   memset( item, 0, sizeof(vg_queue_item) );
+   vg_zero_mem( item, sizeof(vg_queue_item) );
    item->alloc_size = total;
    item->prev_size = prev_size;
 
    q->head_offset = new_item_offset;
    q->allocation_count ++;
-
-   if( out_offset )
-      *out_offset = new_item_offset;
    return item->data;
 }
 
index 3af3de642d67a3a07dea610bd2d821d428387055..a16701ff4b80bac313f0146185ea662e39fe6a5b 100644 (file)
@@ -1,4 +1,6 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_mem_queue.c"
+#else
 
 #define VG_MEM_QUEUE_INVALID 0xffffffff
 
@@ -19,10 +21,11 @@ struct vg_queue
    u32 allocation_count;
 };
 
-void *vg_queue_alloc( vg_queue *q, u32 size, u32 *out_offset );
+void *vg_queue_alloc( vg_queue *q, u32 size );
 
 void *vg_queue_data( vg_queue *q, u32 offset );
 void *vg_queue_tail_data( vg_queue *q );
+u32 vg_queue_offset( vg_queue *q, void *pointer );
 
 /* warning: this is not the size but the allocation size (may be padded) */
 u32 vg_queue_item_size( vg_queue *q, u32 item_id );
@@ -33,3 +36,5 @@ void vg_queue_memcpy( vg_queue *q, void *dst, u32 start, u32 size );
 
 u32 vg_queue_usage( vg_queue *q );
 void vg_queue_clear( vg_queue *q );
+
+#endif
index 5159ac8667845ab54905421a47b021ff58035e9a..e77edd4d7e30d4757f0b2cfa9b5360e41a4ee15f 100644 (file)
@@ -1,5 +1,3 @@
-#include "vg_ui/imgui.h"
-
 struct mem_view_data
 {
    void *base_buffer;
@@ -304,6 +302,9 @@ static void cb_vg_mem_view( ui_context *ctx, ui_rect rect, struct vg_magi_panel
 
       ui_flush( ctx, k_ui_shader_colour, NULL );
       ui_fill_rect( ctx, freq_box, 0xffffffff, (ui_px[4]){ 0,256,256,0 } );
+
+      // FIXME FIXME
+#if 0
       struct ui_batch_shader_data_image_gradient inf = { 
          .resource = &mv->vis_tex,
          .log = 1,
@@ -321,6 +322,7 @@ static void cb_vg_mem_view( ui_context *ctx, ui_rect rect, struct vg_magi_panel
             mv->route[ mv->route_depth ] = context.highlight_id;
          }
       }
+#endif
    }
 }
 
@@ -333,12 +335,11 @@ static void cb_mem_view_close( struct vg_magi_panel *me )
 
 static struct
 {
-   const char *name;
+   const c8 *name;
    vg_stack_allocator *stack;
 }
 _vg_mem_named_buffers[] = 
 {
-   { "rtmem", &vg.rtmem },
 };
 
 int _CB_vg_mem_infosort( const void *a, const void *b )
@@ -356,6 +357,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] )
       return 0;
    }
 
+#if 0
    for( u32 i=0; i<VG_ARRAY_LEN( _vg_mem_named_buffers ); i ++ )
    {
       if( !strcmp( _vg_mem_named_buffers[i].name, argv[0] ) )
@@ -392,7 +394,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] )
          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;
@@ -407,9 +409,9 @@ static int cmd_vg_mem_view( int argc, const char *argv[] )
                frame->info->stack_children_count = 0;
 
                /* create array of all immediate child allocations */
-               u32 offset = frame->stack->used - frame->stack->last_allocation_totalsize; 
+               u32 offset = frame->stack->offset - frame->stack->last_allocation_totalsize; 
 
-               if( (frame->stack->used == 0) || !(frame->stack->flags & VG_STACK_ALLOCATOR_METADATA) )
+               if( (frame->stack->offset == 0) || !(frame->stack->flags & VG_STACK_ALLOCATOR_METADATA) )
                   goto l1;
 
             l2:{
@@ -434,7 +436,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] )
                      info->is_stack = 1;
                      vg_stack_allocator *substack = mv->base_buffer + info->buffer_offset;
 
-                     info->stack_used_bytes = substack->used;
+                     info->stack_used_bytes = substack->offset;
                      info->stack_children_start = 0; /* deferred to !frame->init */
                      info->stack_children_count = 0;
                   }
@@ -504,6 +506,7 @@ static int cmd_vg_mem_view( int argc, const char *argv[] )
          return 1;
       }
    }
+#endif
 
    vg_error( "No named buffer '%s'\n", argv[0] );
    return 0;
@@ -518,7 +521,7 @@ static void cmd_vg_mem_view_poll( int argc, const char *argv[] )
          console_suggest_score_text( _vg_mem_named_buffers[i].name, term, 0 );
 }
 
-void vg_mem_view_register(void)
+VG_API void _vg_mem_view_register(void)
 {
    vg_console_reg_cmd( "vg_mem_view", cmd_vg_mem_view, cmd_vg_mem_view_poll );
 }
index 29e793f6725ee7ffc748ad8df9303384db31a2d3..02c593f93a089471368cb7fa16de6ddd674401ee 100644 (file)
@@ -1,4 +1,6 @@
-#pragma once
-#include "vg_ui/imgui.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_mem_view.c"
+#else
 extern int vg_mem_view;
-void vg_mem_view_register(void);
+VG_API void _vg_mem_view_register(void);
+#endif
index 35a937c51b271f2c83e98c8d6bbd7f75a98a34e1..0457bd2c7f5eb12b80cce1187669ac29989a1e48 100644 (file)
--- a/vg_msg.c
+++ b/vg_msg.c
@@ -1,10 +1,4 @@
-#include "vg_msg.h"
-#include "vg_platform.h"
-#include "vg_string.h"
-#include <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 )
 {
@@ -34,7 +28,7 @@ void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len )
    }
 }
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 /* write null terminated string to stream */
 void vg_msg_wstr( vg_msg *msg, const char *str )
 {
@@ -66,7 +60,7 @@ const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 )
    return str;
 }
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 /* begin a new frame in message stream */
 void vg_msg_frame( vg_msg *msg, const char *name )
 {
@@ -129,7 +123,7 @@ u8 vg_msg_count_bits( u32 count )
    return ((count-1)<<2);
 }
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 /* write a sized type */
 void vg_msg_wkvnum( vg_msg *msg, const char *key, u8 type, u8 count, void *data )
 {
@@ -162,14 +156,13 @@ int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd )
    vg_msg_rbuf( msg, &cmd->code, 1 );
    if( msg->error != k_vg_msg_error_OK ) return 0;
 
-#ifdef VG_MSG_V1_SUPPORT
   /* |sized|  |count-1| |shift|
    * 0     1  0  0  0 1 0     0  0x44 (1 byte, float[2]. So, never used anyway)
    * converts to
    * 1     0  0  0  0 0 1     0  0x82
    */
    if( cmd->code == 0x44 ) cmd->code = 0x82;
-#endif
+
    cmd->key_djb2 = 0;
    if( msg->error != k_vg_msg_error_OK ) return 0;
 
@@ -218,7 +211,7 @@ int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd )
       return 1;
 }
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 /* move through the frame(and subframes) until we fall out of it */
 int vg_msg_skip_frame( vg_msg *msg )
 {
@@ -327,7 +320,7 @@ f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size )
       return (f64)vg_msg_cast_to_i64( src, src_base, src_size );
 }
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 /*
  * Convert any full integral type code to another
  * Passing in non-integral codes is undefined
@@ -403,7 +396,7 @@ int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd )
    return 0;
 }
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 /*
  * Read a integral KV out to dst, and perform conversion if needed
  * dst is always defined, if its not found its set to 0
@@ -438,7 +431,7 @@ const char *vg_msg_getkvstr( vg_msg *msg, const char *key )
 }
 #endif
 
-#if !defined( VG_MSG_TO_KVS )
+#if defined( VG_MSG_LEGACY )
 int vg_msg_getkvvecf( vg_msg *msg, const char *key, u8 type, 
                       void *v, void *default_value )
 {
@@ -526,8 +519,7 @@ void vg_msg_print( vg_msg *msg, u32 len )
 #endif
 
 #if defined( VG_MSG_TO_KVS )
-#include "vg_kv.h"
-bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len, vg_stack_allocator *stack )
+bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len )
 {
    vg_msg b;
    vg_msg_init( &b, buffer, len );
@@ -574,21 +566,21 @@ bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 le
                if( base == k_vg_msg_unsigned )
                {
                   u64 val = vg_msg_cast_to_u64( p, base, size );
-                  vg_strcatu64_internal( &value_str, val, 10, 0 );
+                  vg_strcatu64( &value_str, val, 10 );
                }
                else if( base == k_vg_msg_signed )
                {
                   i64 val = vg_msg_cast_to_i64( p, base, size );
-                  vg_strcati64( &value_str, val, 10, 0 );
+                  vg_strcati64( &value_str, val, 10 );
                }
                else 
                {
                   f64 val = vg_msg_cast_to_f64( p, base, size );
-                  vg_strcatf64( &value_str, val, 10, 0 );
+                  vg_strcatf64( &value_str, val, 10, 5 );
                }
 
                if( i+1!=count )
-                  vg_strcatch( &format_str, ' ' );
+                  vg_strcatch( &value_str, ' ' );
             }
 
             if( !vg_strgood( &value_str ) )
index 5f485861a8617040ccc0940dfffe70fb47946cfb..897153e8912fe1da447aa91ca7780818a61d0473 100644 (file)
--- a/vg_msg.h
+++ b/vg_msg.h
@@ -1,83 +1,6 @@
-#pragma once
-/*
- * Example data:
- *   kvstr "someinfo"
- *   kvint 200
- *   frame "person"{
- *      name "jeff"
- *      country "england"
- *   }
- *   frame "building"{
- *      capacity 1000
- *   }
- *   frame "person"{
- *      country "wales"
- *      name "micheal"
- *   }
- *
- * Creating the data in code:
- * -----------------------------------------------------------------------------
- *   u8 data_buf[512];
- *   vg_msg data;
- *   vg_msg_init( &data, data_buf, 512 );
- *
- *   vg_msg_wkvstr( &data, "kvstr", "someinfo" );
- *   vg_msg_wkvu32( &data, "kvint", 200 );
- *
- *   vg_msg_frame( &data, "person" );
- *      vg_msg_wkvstr( &data, "name", "jeff" );
- *      vg_msg_wkvstr( &data, "country", "england" );
- *   vg_msg_end_frame( &data );
- *
- *   vg_msg_frame( &data, "building" );
- *      vg_msg_wkvu32( &data, "capacity", 1000 );
- *   vg_msg_end_frame( &data );
- *
- *   vg_msg_frame( &data, "person" );
- *      vg_msg_wkvstr( &data, "country", "wales" );
- *      vg_msg_wkvstr( &data, "name", "micheal" );
- *   vg_msg_end_frame( &data );
- *
- * Saving the data out
- * -----------------------------------------------------------------------------
- *
- *   if( data.error == k_vg_msg_error_OK ){
- *      // write data_buf, for length data.cur
- *   }
- *
- * Load the data
- * -----------------------------------------------------------------------------
- *
- *   u8 data_buf[512];
- *   u32 data_len;
- *
- *   // read data_buf and data_len
- *
- *   vg_msg data;
- *   vg_msg_init( &data, data_buf, data_len );
- *
- *
- * Reading data
- * -----------------------------------------------------------------------------
- *
- * if( vg_msg_seekframe( &msg, "rows" ) ){
- *    while( vg_msg_seekframe( &msg, NULL ) ){
- *       vg_warn( "%s\n", vg_msg_readkvstr( &msg, "yedo" ) );
- *       vg_msg_skip_frame( &msg );
- *    }
- * }
- *
- * Reading back the stream linearly
- * -----------------------------------------------------------------------------
- * 
- *   vg_msg_cmd cmd;
- *   while( vg_msg_next( &data, &cmd ) ){
- *           if( cmd.code == k_vg_msg_frame ) printf( "{" );
- *      else if( cmd.code == k_vg_msg_endframe ) printf( "}" );
- *      esle if( cmd.code == k_vg_msg_kvstring )
- *         printf( "string: %s\n", cmd.value._buf );
- *   }
- */
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_msg.c"
+#else
 
 enum vg_msg_code{
    /* low types */
@@ -159,27 +82,30 @@ struct vg_msg_cmd
    u32 len; /* set if binary type */
 };
 
-void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len );
+u32 vg_msg_cmd_array_count( u8 code );
+u32 vg_msg_cmd_type_size( u8 code );
+u32 vg_msg_cmd_bytecount( u8 code );
+u8 vg_msg_count_bits( u32 count );
+
 void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len );
-void vg_msg_wstr( vg_msg *msg, const char *str );
 const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 );
+u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size );
+i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size );
+f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size );
+
+#if defined( VG_MSG_LEGACY )
+void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len );
+void vg_msg_wstr( vg_msg *msg, const char *str );
 void vg_msg_frame( vg_msg *msg, const char *name );
 void vg_msg_end_frame( vg_msg *msg );
 void vg_msg_wkvstr( vg_msg *msg, const char *key, const char *value );
 void vg_msg_wkvbin( vg_msg *msg, const char *key, u8 *bin, u32 len );
 void vg_msg_wkvnum( vg_msg *msg, const char *key, u8 type, u8 count, void *data );
-u32 vg_msg_cmd_array_count( u8 code );
-u32 vg_msg_cmd_type_size( u8 code );
-u32 vg_msg_cmd_bytecount( u8 code );
-u8 vg_msg_count_bits( u32 count );
 
 void vg_msg_init( vg_msg *msg, u8 *buffer, u32 len );
 int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd );
 int vg_msg_skip_frame( vg_msg *msg );
 int vg_msg_seekframe( vg_msg *msg, const char *name );
-u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size );
-i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size );
-f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size );
 void vg_msg_cast( const void *src, u8 src_code, void *dst, u8 dst_code );
 
 int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd );
@@ -187,14 +113,16 @@ int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd );
 /*
  * Read a integral KV out to dst, and perform conversion if needed
  */
-int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst,
-                      void *default_value );
+int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst, void *default_value );
 
 /* helper for reading string kvs. returns NULL if not found */
 const char *vg_msg_getkvstr( vg_msg *msg, const char *key );
-int vg_msg_getkvvecf( vg_msg *msg, const char *key, u8 type, 
-                      void *v, void *default_value );
+int vg_msg_getkvvecf( vg_msg *msg, const char *key, u8 type, void *v, void *default_value );
 void vg_msg_print( vg_msg *msg, u32 len );
+#endif
+
+#if defined( VG_MSG_TO_KVS )
+bool vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len );
+#endif
 
-#include "vg_kv.h"
-void vg_kvs_append_from_legacy_msg2( vg_kvs *kvs, u32 root, void *buffer, u32 len, vg_stack_allocator *stack );
+#endif
index 7e9974c35923e9ccd7e61a16f9f12fbe7baa56bb..b30507c01bcb7aa4c6c837d38afb3ff6139b7f03 100644 (file)
@@ -1,34 +1,45 @@
-#ifdef VG_ENGINE
-#define vg_semaphore SDL_sem *
-#define vg_mutex SDL_mutex *
-#define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (SEMAPHORE = SDL_CreateSemaphore( VALUE ))
-#define VG_SEMAPHORE_FREE( SEMAPHORE ) SDL_DestroySemaphore( SEMAPHORE ); SEMAPHORE = NULL;
-#define VG_MUTEX_INIT( MUTEX ) (MUTEX = SDL_CreateMutex())
-#define VG_MUTEX_FREE( MUTEX ) SDL_DestroyMutex( MUTEX ); MUTEX = NULL;
-#define VG_MUTEX_LOCK( MUTEX ) SDL_LockMutex( MUTEX )
-#define VG_MUTEX_UNLOCK( MUTEX ) SDL_UnlockMutex( MUTEX )
-#define VG_SEMAPHORE_WAIT( SEMAPHORE ) SDL_SemWait( SEMAPHORE )
-#define VG_SEMAPHORE_POST( SEMAPHORE ) SDL_SemPost( SEMAPHORE )
-#define VG_SEMAPHORE_VALUE( SEMAPHORE ) SDL_SemValue( SEMAPHORE )
-#else
-#include <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
-
index ce224e32c5287c1c952fd4febe8ee56f6847d45c..a9a966ae1ddfa510f142f9998fe398dc908e95ba 100644 (file)
--- a/vg_opt.c
+++ b/vg_opt.c
@@ -1,12 +1,3 @@
-/*
- * Copyright (C) 2021-2025 Mt.ZERO Software, Harry Godden - All Rights Reserved
- */
-
-#include "vg_opt.h"
-#include "vg_platform.h"
-#include "vg_log.h"
-#include <stdlib.h>
-
 /* 
  * Supported:
  *   short flags       |  -abc
index 12e6187a26a2aa4d74cb2c55bb4119b223fdf24c..bd8f49805b81d541a34480bb7794a96b1be9fc35 100644 (file)
--- a/vg_opt.h
+++ b/vg_opt.h
@@ -1,9 +1,6 @@
-/*
- * Copyright (C) 2021-2025 Mt.ZERO Software - All Rights Reserved
- */
-
-#pragma once
-#include "vg_platform.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_opt.c"
+#else
 
 void _vg_opt_init( int argc, const char *argv[] );
 bool _vg_opt_check(void);
@@ -22,3 +19,5 @@ const char *vg_long_opt_arg( char *name, const char *desc );
 
 /* Example: get regular_thing */
 const char *vg_arg( u32 index );
+
+#endif
index 6b8b087f143afb9d34ba092d0d33c7d77240bd64..6647a915bd80dca19036d9640c0a6ef9fbfc810b 100644 (file)
@@ -1,6 +1,3 @@
-#include "vg_m.h"
-#include "vg_perlin.h"
-
 static int perlin_hash[] = {
 0x46,0xD5,0xB8,0xD3,0xF2,0xE5,0xCC,0x07,0xD0,0xB3,0x7A,0xA2,0xC3,0xDA,0xDC,0x7F,
 0xE0,0xB7,0x42,0xA0,0xBF,0x41,0x92,0x32,0x6F,0x0D,0x45,0xC7,0x54,0xDB,0x30,0xC2,
index 1d28c769360606065d163cb15836c8ea0b29de34..56d240c11dfe7a119b05a92191a0b9b67b43c514 100644 (file)
@@ -1,6 +1,10 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_perlin.c"
+#else
 
 f32 vg_perlin_noise2d( f32 x, f32 y, int seed );
 f32 vg_perlin_noise1d( f32 v, int seed );
 f32 vg_perlin_fract_1d( f32 v, f32 freq, int octaves, int seed );
 f32 vg_perlin_fract_2d( f32 x, f32 y, f32 freq, int octaves, int seed );
+
+#endif
index 5c6e3e61d4b736f942ef277e1d191559e47ef142..7f3ddb16bac7ce0da7be17bd5dd0eee4694a1c5d 100644 (file)
@@ -1,7 +1,68 @@
-#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;
@@ -16,18 +77,17 @@ typedef float    f32;
 typedef double   f64;
 typedef uint8_t  bool;
 
-typedef unsigned int uint;
-typedef int            v2i[2];
-typedef int            v3i[3];
-typedef int            v4i[4];
-typedef float          v2f[2];
-typedef float          v3f[3];
-typedef float          v4f[4];
-typedef v2f                    m2x2f[2];
-typedef v3f                    m3x3f[3];
-typedef v3f                    m4x3f[4];
-typedef v4f       m4x4f[4];
-typedef v3f                    boxf[2];
+typedef i32 v2i[2];
+typedef i32 v3i[3];
+typedef i32 v4i[4];
+typedef f32    v2f[2];
+typedef f32    v3f[3];
+typedef f32    v4f[4];
+typedef v2f    m2x2f[2];
+typedef v3f    m3x3f[3];
+typedef v3f    m4x3f[4];
+typedef v4f m4x4f[4];
+typedef v3f    boxf[2];
 
 /* anything compiled against VG shall implement vg_fatal_exit() somewhere. */
 void vg_fatal_exit( const char *comment );
@@ -41,3 +101,5 @@ void vg_fatal_error( const char *fmt, ... );
 #define VG_MIN( A, B ) ((A)<(B)?(A):(B))
 #define VG_MAX( A, B ) ((A)>(B)?(A):(B))
 #define VG_ARRAY_LEN( A ) (sizeof(A)/sizeof(A[0]))
+
+#endif
index aa7b16f3a87823fc1954a2b55f762cbdf9dda762..f4bd4fcf45658fb1d957977f2c2555614fe08bbf 100644 (file)
-#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;
@@ -154,16 +204,20 @@ static int cmd_vg_profile( int argc, const char *argv[] )
    return 0;
 }
 
-static void cmd_vg_profile_poll( int argc, const char *argv[] )
+static void cmd_vg_profile_poll( int argc, const c8 *argv[] )
 {
-   const char *term = argv[ argc-1 ];
-
+   const c8 *term = argv[ argc-1 ];
    if( argc == 1 )
-      for( u32 i=0; i<_vg_profiler.named_count; i ++ )
-         console_suggest_score_text( _vg_profiler.named_sets[i]->name,term,0 );
+   {
+      for( u32 i=0; i<_vg_profiler.count; i ++ )
+      {
+         struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) );
+         console_suggest_score_text( profiler->name, term, 0 );
+      }
+   }
 }
 
-void vg_profiler_register(void)
+VG_API void _vg_profiler_register(void)
 {
    vg_console_reg_cmd( "vg_profile", cmd_vg_profile, cmd_vg_profile_poll );
 }
index dd6d694eb2ef2348a8b3ac09ab34d45a2be3512a..8c57e3fd7180062ff7e069cca305a3caae5bd4d1 100644 (file)
@@ -1,45 +1,14 @@
-#pragma once
-#include "vg_platform.h"
-#include "vg_ui/imgui.h"
-#define VG_PROFILE_SAMPLE_COUNT 128
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_profiler.c"
+#else
 
-struct vg_profile
-{
-   const char *name;
+VG_API void _vg_profiler_register(void);
+VG_API void _vg_profiler_init(void);
 
-   u64    samples[ VG_PROFILE_SAMPLE_COUNT ];
-   u32    buffer_count, buffer_current;
+VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms );
+VG_API void _vg_profiler_tick( u32 profiler_id );
+VG_API void _vg_profiler_enter_block( u32 profiler_id, const c8 *block_name );
+VG_API void _vg_profiler_exit_block( u32 profiler_id );
+VG_API void _vg_profiler_draw( ui_context *ctx, u32 profiler_id, f32 budget_ms, ui_rect panel, i32 dir, bool normalize );
 
-   enum profile_mode
-   {
-      k_profile_mode_frame,
-      k_profile_mode_accum
-   }
-   mode;
-
-   u64 start;
-};
-
-struct _vg_profiler
-{
-   struct vg_profile_set 
-   {
-      const char *name;
-      u32 count;
-      f64 (*get_budget)(void);
-      struct vg_profile *list[];
-   }
-   *named_sets[ 8 ];
-   u32 named_count;
-}
-extern _vg_profiler;
-
-void vg_profile_begin( struct vg_profile *profile );
-void vg_profile_increment( struct vg_profile *profile );
-void vg_profile_end( struct vg_profile *profile );
-void vg_profile_drawn( ui_context *ctx, struct vg_profile **profiles, u32 count,
-                       f64 budget, ui_rect panel,
-                       int dir, i32 normalize );
-void vg_profiler_register(void);
-
-void _vg_profile_reg_set( struct vg_profile_set *set );
+#endif
index ba631a8334709598ed049d8bbf069487b8ba6b62..4a22a898e00abe3cc840dbc9b5e07fb8df692134 100644 (file)
@@ -1,39 +1,3 @@
-#include "vg_render.h"
-
-#ifdef VG_3D
-#include "shaders/blitblur.h"
-#endif
-
-static struct vg_shader _shader_blit =
-{
-   .name = "[vg] blit texture",
-   .vs = {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       ""
-   "uniform vec2 uInverseRatio;"
-       "out vec2 aUv;"
-       ""
-       "void main(){"
-      "gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);"
-      "aUv = a_co * uInverseRatio;"
-       "}",
-   },
-   .fs = {
-      .orig_file = NULL,
-      .static_src = 
-   "uniform sampler2D uTexMain;"
-       "out vec4 FragColor;"
-       ""
-       "in vec2 aUv;"
-       ""
-       "void main(){"
-      "FragColor = texture( uTexMain, aUv );"
-       "}"
-   }
-};
-
 struct vg_postprocess _vg_postprocess = 
 {
 #ifdef VG_3D
@@ -47,10 +11,16 @@ struct vg_render _vg_render =
    .scale = 1.0f
 };
 
-static void vg_async_postprocess_init( void *userdata )
+VG_API void _vg_render_register(void)
 {
-   THREAD_0;
+   vg_console_reg_var( "render_scale", &_vg_render.scale, k_var_dtype_f32, VG_VAR_PERSISTENT );
+   vg_console_reg_var( "blur_strength", &_vg_postprocess.blur_strength, k_var_dtype_f32, 0 );
+   vg_console_reg_var( "blur_effect", &_vg_postprocess.blur_effect, k_var_dtype_i32, VG_VAR_PERSISTENT );
+}
 
+VG_API void _vg_render_init(void)
+{
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
    f32 quad[] = 
    { 
       0.00f,0.00f, 1.00f,1.00f, 0.00f,1.00f,
@@ -65,28 +35,13 @@ static void vg_async_postprocess_init( void *userdata )
    glBindVertexArray( _vg_postprocess.quad_vao );
    glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(f32)*2, (void*)0 );
    glEnableVertexAttribArray( 0 );
-   vg_compile_shader( &_shader_blit );
-}
-
-void vg_render_register(void)
-{
-   vg_console_reg_var( "render_scale", &_vg_render.scale, k_var_dtype_f32, VG_VAR_PERSISTENT );
-   vg_console_reg_var( "blur_strength", &_vg_postprocess.blur_strength, k_var_dtype_f32, 0 );
-   vg_console_reg_var( "blur_effect", &_vg_postprocess.blur_effect, k_var_dtype_i32, VG_VAR_PERSISTENT );
-}
-
-void vg_render_init(void)
-{
-   THREAD_1;
-
-   vg_async_call( &vg.main_tasks, vg_async_postprocess_init, NULL );
 
-#ifdef VG_3D
+#if defined( VG_3D )
    
    /* 
     * Main framebuffer
     */
-   _vg_render.fb_main = vg_framebuffer_allocate( &vg.rtmem, 3, 1 );
+   _vg_render.fb_main = _vg_framebuffer_alloc( NULL, 3, VG_FRAMEBUFFER_GLOBAL );
    _vg_render.fb_main->display_name = "main";
    _vg_render.fb_main->resolution_div = 1;
    _vg_render.fb_main->attachments[0] = (vg_framebuffer_attachment)
@@ -116,12 +71,12 @@ void vg_render_init(void)
       .type           = GL_UNSIGNED_INT_24_8,
       .attachment     = GL_DEPTH_STENCIL_ATTACHMENT
    };
-   vg_framebuffer_create( _vg_render.fb_main );
+   vg_framebuffer_init( _vg_render.fb_main );
    
    /* 
     * Water reflection
     */
-   _vg_render.fb_water_reflection = vg_framebuffer_allocate( &vg.rtmem, 2, 1 );
+   _vg_render.fb_water_reflection = _vg_framebuffer_alloc( NULL, 2, VG_FRAMEBUFFER_GLOBAL );
    _vg_render.fb_water_reflection->display_name = "water_reflection";
    _vg_render.fb_water_reflection->resolution_div = 2;
    _vg_render.fb_water_reflection->attachments[0] = (vg_framebuffer_attachment) 
@@ -138,13 +93,13 @@ void vg_render_init(void)
       .internalformat = GL_DEPTH24_STENCIL8,
       .attachment     = GL_DEPTH_STENCIL_ATTACHMENT
    };
-   vg_framebuffer_create( _vg_render.fb_water_reflection );
+   vg_framebuffer_init( _vg_render.fb_water_reflection );
 
    /*
     * Thid rendered view from the perspective of the camera, but just 
     * captures stuff thats under the water
     */
-   _vg_render.fb_water_beneath = vg_framebuffer_allocate( &vg.rtmem, 2, 1 );
+   _vg_render.fb_water_beneath = _vg_framebuffer_alloc( NULL, 2, VG_FRAMEBUFFER_GLOBAL );
    _vg_render.fb_water_beneath->display_name = "water_beneath";
    _vg_render.fb_water_beneath->resolution_div = 2;
    _vg_render.fb_water_beneath->attachments[0] = (vg_framebuffer_attachment) 
@@ -161,7 +116,7 @@ void vg_render_init(void)
       .internalformat = GL_DEPTH24_STENCIL8,
       .attachment     = GL_DEPTH_STENCIL_ATTACHMENT
    };
-   vg_framebuffer_create( _vg_render.fb_water_beneath );
+   vg_framebuffer_init( _vg_render.fb_water_beneath );
 #endif
 }
 
@@ -178,7 +133,7 @@ void vg_render_fullscreen_quad(void)
 void vg_postprocess_to_screen( vg_framebuffer *fb )
 {
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
-   glViewport( 0,0, vg.window_x, vg.window_y );
+   glViewport( 0,0, _vg_window.w, _vg_window.h );
 
    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
@@ -210,9 +165,9 @@ void vg_postprocess_to_screen( vg_framebuffer *fb )
    else
 #endif
    {
-      glUseProgram( _shader_blit.id );
-      glUniform1i( glGetUniformLocation( _shader_blit.id, "uTexMain" ), 0 );
-      glUniform2fv( glGetUniformLocation( _shader_blit.id, "uInverseRatio" ), 1, vg_ui.bg_inverse_ratio );
+      shader_blit_use();
+      shader_blit_uTexMain( 0 );
+      shader_blit_uInverseRatio( vg_ui.bg_inverse_ratio );
       vg_framebuffer_bind_texture( fb, 0, 0 );
    }
 
index 5da2ae456a417b31d37e4a016eb765a03b632e1f..7a4f74e30501da194b96113114bcee87dacedac8 100644 (file)
@@ -1,6 +1,6 @@
-#pragma once
-#include "vg_engine.h"
-#include "vg_framebuffer.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_render.c"
+#else
 
 struct vg_postprocess
 {
@@ -19,15 +19,17 @@ struct vg_render
 {
    f32 scale;
 
-#ifdef VG_3D
+#if defined( VG_3D )
    vg_framebuffer *fb_main,
                   *fb_water_reflection,
                   *fb_water_beneath;
 #endif
 }
 extern _vg_render;
+VG_API void _vg_render_register(void);
+VG_API void _vg_render_init(void);
 
-void vg_render_register(void);
-void vg_render_init(void);
 void vg_render_fullscreen_quad(void);
 void vg_postprocess_to_screen( vg_framebuffer *fb );
+
+#endif
index c833a9dc26f03a42ded87a8bedbd377c576a8362..4afccfb4aff1c31e09d41061873acd7725925b3c 100644 (file)
@@ -1,10 +1,3 @@
-#include "vg_console.h"
-#include "vg_m.h"
-#include "vg_rigidbody.h"
-#include "vg_platform.h"
-#include "vg_engine.h"
-#include <math.h>
-
 static float
    k_limit_bias       = 0.02f,
    k_joint_correction = 0.01f,
@@ -13,7 +6,7 @@ static float
 
 f32 k_gravity = 9.6f;
 
-void rb_register_cvar(void)
+VG_API void _vg_rigidbody_register(void)
 {
    VG_VAR_F32( k_limit_bias, flags=VG_VAR_CHEAT );
    VG_VAR_F32( k_joint_bias, flags=VG_VAR_CHEAT );
index 68ed57da4924555b5be197ba486662d72e1566dd..59c1524c4ab2508e354d50de7dee2d8fec2f421d 100644 (file)
@@ -1,5 +1,6 @@
-#pragma once
-#include "vg/vg_platform.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_rigidbody.c"
+#else
 
 /*
  * Copyright (C) 2021-2024 Mt.ZERO Software - All Rights Reserved
@@ -46,7 +47,7 @@ struct rigidbody
    m4x3f to_world, to_local;
 };
 
-void rb_register_cvar(void);
+VG_API void _vg_rigidbody_register(void);
 
 /* 
  * Initialize rigidbody inverse mass and inertia tensor with some common shapes
@@ -83,3 +84,5 @@ void rb_linear_impulse( rigidbody *rb, v3f delta, v3f impulse );
  */
 void rb_effect_simple_bouyency( rigidbody *ra, v4f plane, f32 amt, f32 drag );
 void rb_effect_spring_target_vector( rigidbody *rba, v3f ra, v3f rt, f32 spring, f32 dampening, f32 timestep );
+
+#endif
index 2989a46e026c75fc2e8b024511117a13bd6ec3e7..156416e420833d922212e8cfe8bdc8ebac33c594 100644 (file)
@@ -1,9 +1,3 @@
-#include "vg_rigidbody.h"
-#include "vg_rigidbody_collision.h"
-#include "vg_m.h"
-#include "vg_lines.h"
-#include "vg_platform.h"
-
 int rb_contact_count = 0;
 struct rb_ct rb_contact_buffer[VG_MAX_CONTACTS];
 
index 9a1957dfd5125830511b944808cc23bef9490d17..024a35a74b9428684f9cceb086099e3bdbfa8ca4 100644 (file)
@@ -1,6 +1,6 @@
-#pragma once
-#include "vg_m.h"
-#include "vg_rigidbody.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_rigidbody_collision.c"
+#else
 
 /* TODO: Get rid of this! */
 #define VG_MAX_CONTACTS 256
@@ -70,3 +70,5 @@ void rb_depenetrate( rb_ct *manifold, int len, v3f dt );
 void rb_presolve_contacts( rb_ct *buffer, f32 dt, int len );
 void rb_contact_restitution( rb_ct *ct, float cr );
 void rb_solve_contacts( rb_ct *buf, int len );
+
+#endif
index 3d6b932d398bff2ef3331404faa1a64be4fe7fce..ea2648787ff2d91b99db55b66d91e3b3d460bde4 100644 (file)
@@ -1,9 +1,3 @@
-#pragma once
-#include "vg_rigidbody.h"
-#include "vg_rigidbody_constraints.h"
-#include "vg_m.h"
-#include "vg_lines.h"
-
 /*
  * -----------------------------------------------------------------------------
  *                               Constraints
@@ -431,8 +425,7 @@ void rb_correct_position_constraints( rb_constr_pos *buf, int len, f32 amt )
    }
 }
 
-void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, 
-                                        int len, float amt )
+void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, int len, float amt )
 {
    for( int i=0; i<len; i++ ){
       rb_constr_swingtwist *st = &buf[i];
index 92ec26d9e30e1388b9e664ae6b69c6e2e275dfb2..fd1505356a83f862c40ba24b27aa64eba219b030 100644 (file)
@@ -1,4 +1,6 @@
-#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;
@@ -43,5 +45,6 @@ void rb_solve_constr_angle( rigidbody *rba, rigidbody *rbb, v3f ra, v3f rb );
  * [ 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
index f03f1c499faac4212a26edafee52fb64e25efc22..b161d5a3dc08e663e6e9d574f8e3c8862532e727 100644 (file)
@@ -1,91 +1,3 @@
-#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;
@@ -95,51 +7,17 @@ struct rb_view_vert {
 
 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,
@@ -157,26 +35,16 @@ void vg_rb_view_init(void)
    tris_count  += H*2 + (V-2)*(H*2);
    vg_rb_view.sphere_count = H*2 + (V-2)*(H*2);
 
-   u32 hdr_size  = vg_align8( sizeof(struct vg_rb_mesh_init) ),
-       vert_size = vg_align8( verts_count * sizeof(rb_view_vert) ),
-       tris_size = vg_align8( tris_count * 3 * sizeof(u16) );
-
-   vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, hdr_size + vert_size + tris_size, 1 );
-   struct vg_rb_mesh_init *inf = (void *)task->data;
-   rb_view_vert *verts = ((void *)inf) + hdr_size;
-   u16 *tris = ((void *)inf) + hdr_size + vert_size;
-
-   inf->verts = verts;
-   inf->tris = tris;
-   inf->verts_size = vert_size;
-   inf->tris_size = tris_size;
+   rb_view_vert verts[ verts_count ];
+   u16 tris[ tris_count*3 ];
 
    u32 tri_index = 0,
        vert_index = 0;
 
    /* box 
     * ----------------------------------------------------------- */
-   for( u32 i=0; i<6; i ++ ){
+   for( u32 i=0; i<6; i ++ )
+   {
       v3f n = {i%3==0,i%3==1,i%3==2};
       if( i >= 3 ) v3_negate( n, n );
       v3f v0, v1;
@@ -185,7 +53,8 @@ void vg_rb_view_init(void)
       rb_view_vert *vs = &verts[vert_index];
       vert_index += 4;
 
-      for( u32 j=0; j<4; j ++ ){
+      for( u32 j=0; j<4; j ++ )
+      {
          v3_copy( n, vs[j].n );
          v3_muladds( n, v0, j&0x1?1.0f:-1.0f, vs[j].co );
          v3_muladds( vs[j].co, v1, j&0x2?1.0f:-1.0f, vs[j].co );
@@ -208,24 +77,28 @@ void vg_rb_view_init(void)
    v4_copy( (v4f){0,-1,0,0}, verts[vert_index].co );
    v3_copy( (v3f){0,-1,0}, verts[vert_index ++].n );
 
-   for( u32 x=0; x<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);
@@ -240,14 +113,32 @@ void vg_rb_view_init(void)
    v4_copy( (v4f){0, 1,0,1}, verts[vert_index].co );
    v3_copy( (v3f){0, 1,0}, verts[vert_index ++].n );
 
-   for( u32 x=0; x<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)
@@ -255,9 +146,8 @@ 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 );
 }
@@ -274,14 +164,10 @@ void vg_rb_view_box( m4x3f mdl, boxf bbx, v4f colour )
    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)) );
 }
 
@@ -290,14 +176,11 @@ void vg_rb_view_sphere( m4x3f mdl, f32 r, v4f colour )
    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)) );
 }
 
@@ -313,13 +196,9 @@ void vg_rb_view_capsule( m4x3f mdl, f32 r, f32 h, v4f colour )
    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)) );
 }
index 111ff7c2a5e87ee99a8937eb12f3e5a6df6a9463..888940dedab7629eb93bf30b26bd45b49593ef90 100644 (file)
@@ -1,8 +1,12 @@
-#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
diff --git a/vg_settings.c b/vg_settings.c
new file mode 100644 (file)
index 0000000..e86b592
--- /dev/null
@@ -0,0 +1,457 @@
+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;
+}
diff --git a/vg_settings.h b/vg_settings.h
new file mode 100644 (file)
index 0000000..fd9d842
--- /dev/null
@@ -0,0 +1,9 @@
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_settings.c"
+#else
+VG_API void _vg_settings_register(void);
+VG_API void _vg_settings_gui( ui_context *ctx );
+VG_API void _vg_settings_open(void);
+VG_API void _vg_settings_close(void);
+VG_API bool _vg_settings_is_open(void);
+#endif
index 7888b9ddd864bb4b2925c8f09d8dece81a0b8a38..e530014eb896d2e899392b307e31a7a883f8edec 100644 (file)
@@ -1,37 +1,15 @@
-/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
-
-#pragma once
-#include "vg_platform.h"
-#include "vg_shader.h"
-#include "vg_engine.h"
-
-#define STB_INCLUDE_IMPLEMENTATION
-#define STB_INCLUDE_LINE_GLSL
-#define STB_MALLOC  vg_alloc
-#define STB_FREE    vg_free
-#define STB_REALLOC vg_realloc
-#include "submodules/stb/stb_include.h"
-
 const char *vg_shader_gl_ver = "#version 330 core\n";
 
-struct vg_shaders
-{
-   vg_shader *shaders[48];
-   u32 count;
-}
-static vg_shaders;
-
 /* 
  * Compile OpenGL subshader from GLSL source. Type is subshader type.
  * If critical is set to 1, the program will fatal exit on compile failure.
  */
-static GLuint vg_compile_opengl_subshader( GLint type, const char *src, bool critical )
+static GLuint vg_compile_opengl_subshader( GLint type, const c8 *src, bool critical, const c8 *debug_path )
 {
        GLuint shader = glCreateShader( type );
 
    if( shader == 0 )
    {
-      vg_opengl_log_errors();
       vg_fatal_error( "glCreateShader returned 0.\n" );
       return 0;
    }
@@ -58,7 +36,7 @@ static GLuint vg_compile_opengl_subshader( GLint type, const char *src, bool cri
       else if( type == GL_FRAGMENT_SHADER ) type_str = "GL_FRAGMENT_SHADER";
 
       if( critical ) 
-         vg_fatal_error( "%s subshader compile error:\n\n%s\n", type_str, info );
+         vg_fatal_error( "shader source path: %s\n %s subshader compile error:\n\n%s\n", debug_path, type_str, info );
       return 0;
    }
 }
@@ -93,11 +71,10 @@ void vg_compile_shader( struct vg_shader *shader )
 {
    VG_ASSERT( shader->compiled == 0 );
 
-   const char *vs = shader->vs.static_src,
-              *fs = shader->fs.static_src;
-
-   GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 1 ),
-          frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 1 ),
+   const c8 *vs = _vg_shaders_glsl + shader->vs.glsl,
+            *fs = _vg_shaders_glsl + shader->fs.glsl;
+   GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 1, _vg_shaders_infos + shader->vs.src ),
+          frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 1, _vg_shaders_infos + shader->fs.src ),
               program = glCreateProgram();
                
        glAttachShader( program, vert );
@@ -108,7 +85,7 @@ void vg_compile_shader( struct vg_shader *shader )
        glDeleteShader( vert );
        glDeleteShader( frag );
 
-   shader->id = program;
+   _vg_shader_names[ shader->names_start ] = program;
    shader->compiled = 1;
 }
 
@@ -121,32 +98,36 @@ void vg_recompile_shader( struct vg_shader *shader )
    VG_ASSERT( shader->compiled == 1 );
 
    char error[260];
-   char path[260];
+   char path_buf[260];
+   vg_str path;
 
-   if( shader->vs.orig_file == NULL ) return;
-   if( shader->fs.orig_file == NULL ) return;
+   vg_strnull( &path, path_buf, sizeof(path_buf) );
+   vg_strcat( &path, "../../" );
+   vg_strcat( &path, _vg_shaders_infos + shader->vs.src );
+   VG_ASSERT( vg_strgood( &path ) );
+   
+   c8 *vs = stb_include_file( path_buf, "", "../../shaders", error );
 
-   strcpy( path, "../../" );
-   strcat( path, shader->vs.orig_file );
-   char *vs = stb_include_file( path, "", "../../shaders", error );
+   vg_strnull( &path, path_buf, sizeof(path_buf) );
+   vg_strcat( &path, "../../" );
+   vg_strcat( &path, _vg_shaders_infos + shader->fs.src );
+   VG_ASSERT( vg_strgood( &path ) );
 
-   strcpy( path, "../../" );
-   strcat( path, shader->fs.orig_file );
-   char *fs = stb_include_file( path, "", "../../shaders", error );
+   c8 *fs = stb_include_file( path_buf, "", "../../shaders", error );
 
    if( !vs || !fs )
    {
       vg_warn( "Could not recompile shader due to missing source files:\n" );
 
-      if( !vs ) vg_info( "  Vertex: %s\n", shader->vs.orig_file );
-      if( !fs ) vg_info( "  Fragment: %s\n", shader->fs.orig_file );
+      if( !vs ) vg_info( "  Vertex: %s\n", _vg_shaders_infos + shader->vs.src );
+      if( !fs ) vg_info( "  Fragment: %s\n", _vg_shaders_infos + shader->fs.src );
       free( vs );
       free( fs );
       return;
    }
 
-   GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 0 ),
-          frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 0 );
+   GLuint vert = vg_compile_opengl_subshader( GL_VERTEX_SHADER, vs, 0, _vg_shaders_infos + shader->vs.src ),
+          frag = vg_compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 0, _vg_shaders_infos + shader->fs.src );
 
    free( vs );
    free( fs );
@@ -161,8 +142,8 @@ void vg_recompile_shader( struct vg_shader *shader )
    if( vg_link_opengl_program( program, 0 ) )
    {
       /* replace existing */
-      glDeleteProgram( shader->id );
-      shader->id = program;
+      glDeleteProgram( _vg_shader_names[ shader->names_start ] );
+      _vg_shader_names[ shader->names_start ] = program;
    }
    else
    {
@@ -178,49 +159,54 @@ void vg_free_shader( struct vg_shader *shader )
 {
    if( shader->compiled )
    {
-      glDeleteProgram( shader->id );
-      shader->id = 0;
       shader->compiled = 0;
+      glDeleteProgram( _vg_shader_names[ shader->names_start ] );
+      _vg_shader_names[ shader->names_start ] = 0;
+      for( u32 i=0; 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 );
 }
index afcdc819526ac904e32e88f878e399ae59e45fec..4f7bdf736b717ad175f98f44e3594401839b116d 100644 (file)
@@ -1,23 +1,27 @@
-#pragma once
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_shader.c"
+#else
 
 typedef struct vg_shader vg_shader;
 struct vg_shader
 {
-   GLuint id;
-   const char *name;
-
+   u32 alias_offset;
    struct vg_subshader
    {
-      const char *orig_file,
-                 *static_src;
+      u32 glsl, src;
    }
    vs, fs;
-   int compiled;
+
+   u32 names_start, uniform_aliases_offset, uniform_count;
+   bool compiled;
 };
 
-void vg_shaders_compile(void *_);
+VG_API void _vg_shaders_register(void);
+VG_API void _vg_shaders_init(void);
+
 int vg_shaders_live_recompile(int argc, const char *argv[]);
-void vg_shader_register( struct vg_shader *shader );
 void vg_compile_shader( struct vg_shader *shader );
 void vg_recompile_shader( struct vg_shader *shader );
 void vg_free_shader( struct vg_shader *shader );
+
+#endif
index cc10baf6642e984c8eba54949143007d747e788f..1c14bbd2e2e16ca4fe305e4184b21ea44e390e7f 100644 (file)
@@ -1,11 +1,3 @@
-#include "vg_steam2.h"
-#include "vg_log.h"
-#include "vg_string.h"
-#include "vg_mem.h"
-#include "vg_io.h"
-#include <stdio.h>
-#include <string.h>
-
 struct vg_steam_api _steam_api;
 
 static void cb_steam_warning( i32 severity, const c8 *pchMessage )
@@ -51,10 +43,10 @@ static u8 vg_char_base16( c8 c )
 #endif
 
 #if defined( VG_SERVER )
-bool vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, const c8 *pchVersionString,
-                    const c8 *appid_str )
+VG_API bool _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, 
+                            const c8 *pchVersionString, const c8 *appid_str )
 #else
-bool vg_steam_init(void)
+VG_API bool _vg_steam_init(void)
 #endif
 {
    if( _steam_api.disabled )
@@ -65,6 +57,7 @@ bool vg_steam_init(void)
    /* Steamworks init step
     * ---------------------------------------------------------------------------- */
 #if defined( VG_ENGINE )
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) );
 
        const char *pszInternalCheckInterfaceVersions = 
                STEAMUTILS_INTERFACE_VERSION "\0"
@@ -113,7 +106,7 @@ bool vg_steam_init(void)
    vg_stack_allocator stack;
    vg_stack_init( &stack, NULL, VG_KB(256), NULL );
    u32 size;
-   char *src = vg_file_read( &stack, "application_key", &size, 0 );
+   c8 *src = vg_file_read( &stack, "application_key", &size, 0 );
    if( src )
    {
       if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen )
@@ -324,7 +317,7 @@ void vg_steam_frame(void)
    }
 }
 
-void vg_steam_shutdown(void)
+VG_API void _vg_steam_shutdown(void)
 {
 #if defined( VG_SERVER )
    if( _steam_api.is_connected )
index 857f24e2998c2b90926af127fbaba144ac3f59a4..010647ddc22a777e9a0a6675b8495964d14e06d0 100644 (file)
@@ -1,13 +1,13 @@
-#pragma once
-#include "vg_platform.h"
-
-#if defined( VG_ENGINE ) || defined( VG_SERVER )
-# if defined( VG_ENGINE ) && defined( VG_SERVER )
-#  error Can't be both!
-# endif
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_steam2.c"
 #else
-# error Must define VG_ENGINE or VG_SERVER
-#endif
+# if defined( VG_ENGINE ) || defined( VG_SERVER )
+#  if defined( VG_ENGINE ) && defined( VG_SERVER )
+#   error Can't be both!
+#  endif
+# else
+#  error Must define VG_ENGINE or VG_SERVER
+# endif
 
 #define STEAMTIMELINE_INTERFACE_VERSION "STEAMTIMELINE_INTERFACE_V004"
 #define STEAMUTILS_INTERFACE_VERSION "SteamUtils010"
@@ -1195,13 +1195,15 @@ struct vg_steam_api
 extern _steam_api;
 
 #if defined( VG_SERVER )
-bool vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, const c8 *pchVersionString,
-                    const c8 *appid_str );
+VG_API bool _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, 
+                            const c8 *pchVersionString, const c8 *appid_str );
 #else
-bool vg_steam_init(void);
+VG_API bool _vg_steam_init(void);
 #endif
 void vg_steam_frame(void);
-void vg_steam_shutdown(void);
+VG_API void _vg_steam_shutdown(void);
 
 vg_steam_api_call *vg_alloc_async_steam_api_call(void);
 void vg_steam_set_achievement( const c8 *name, bool yes );
+
+#endif
index 60a82d05322696afb031d18ed3d0f20e3eda7db6..449193e745836a5a05bb0053c7fee2b39d2000d5 100644 (file)
@@ -1,11 +1,3 @@
-#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 )
@@ -24,7 +16,7 @@ i32 vg_str_storage( vg_str *str )
  * Reset string. If len is 0 (dynamically allocated), buffer must be either 
  * NULL or be acquired from malloc or realloc
  */
-void vg_strnull( vg_str *str, char *buffer, i32 len )
+void vg_strnull( vg_str *str, c8 *buffer, i32 len )
 {
    VG_ASSERT( len >= 0 );
 
@@ -43,7 +35,7 @@ void vg_strfree( vg_str *str )
       if( str->buffer )
       {
          vg_str_dynamic *arr = (vg_str_dynamic *)str->buffer;
-         free( arr-1 );
+         vg_free( arr-1 );
 
          str->buffer = NULL;
          str->i = 0;
@@ -61,22 +53,22 @@ static i32 vg_str_dynamic_grow( vg_str *str )
    {
       vg_str_dynamic *hdr = ((vg_str_dynamic *)str->buffer) - 1;
       i32 total = (hdr->len + sizeof(vg_str_dynamic)) * 2;
-      hdr = realloc( hdr, total );
+      hdr = vg_realloc( hdr, total );
       hdr->len = total - sizeof(vg_str_dynamic);
-      str->buffer = (char *)(hdr+1);
+      str->buffer = (c8 *)(hdr+1);
       return hdr->len;
    }
    else 
    {
-      vg_str_dynamic *hdr = malloc(16);
+      vg_str_dynamic *hdr = vg_malloc(16);
       hdr->len = 16-sizeof(vg_str_dynamic);
-      str->buffer = (char *)(hdr+1);
+      str->buffer = (c8 *)(hdr+1);
       str->buffer[0] = '\0';
       return hdr->len;
    }
 }
 
-static bool _vg_strcatch( vg_str *str, char c )
+static bool _vg_strcatch( vg_str *str, c8 c )
 {
    if( str->i == -1 ) return 0;
 
@@ -97,7 +89,7 @@ static bool _vg_strcatch( vg_str *str, char c )
    return 1;
 }
 
-void vg_strcat( vg_str *str, const char *append )
+void vg_strcat_limit( vg_str *str, const c8 *append, u32 max )
 {
    if( !append || (str->i == -1) ) 
       return;
@@ -105,10 +97,13 @@ void vg_strcat( vg_str *str, const char *append )
    i32 i = 0;
 
 append:;
-   char c = append[ i ++ ]; 
+   c8 c = append[ i ++ ]; 
    if( !_vg_strcatch( str, c ) ) 
       return;
 
+   if( max && i >= max )
+      return;
+
    if( c == '\0' )
    {
       str->i --;
@@ -117,7 +112,12 @@ append:;
    else goto append;
 }
 
-void vg_strcatch( vg_str *str, char c )
+void vg_strcat( vg_str *str, const c8 *append ) 
+{
+   vg_strcat_limit( str, append, 0 );
+}
+
+void vg_strcatch( vg_str *str, c8 c )
 {
    _vg_strcatch( str, c );
    _vg_strcatch( str, '\x00' );
@@ -138,11 +138,11 @@ bool vg_str_flushfd( vg_str *str, int fd )
 }
 
 /*
- * Returns pointer to last instance of character
+ * Returns pointer to last instance of c8acter
  */
-char *vg_strch( vg_str *str, char c )
+c8 *vg_strch( vg_str *str, c8 c )
 {
-   char *ptr = NULL;
+   c8 *ptr = NULL;
    for( i32 i=0; i<str->i; i++ ){
       if( str->buffer[i] == c )
          ptr = str->buffer+i;
@@ -151,7 +151,30 @@ char *vg_strch( vg_str *str, char c )
    return ptr;
 }
 
-u32 vg_strncpy( const char *src, char *dst, u32 len, enum strncpy_behaviour behaviour )
+u32 vg_strcpy( const c8 *src, c8 *dst )
+{
+   for( u32 i=0; i<32*1024; i ++ )
+   {
+      dst[i] = src[i];
+      if( !src[i] )
+         return i;
+   }
+
+   vg_fatal_error( "Hit string copy limit! (32 KB max length)\n" );
+   return 0;
+}
+
+u32 vg_strlen( const c8 *src )
+{
+   for( u32 i=0; i<32*1024; i ++ )
+      if( !src[i] )
+         return i;
+
+   vg_fatal_error( "Hit string len limit! (32 KB max length)\n" );
+   return 0;
+}
+
+u32 vg_strncpy( const c8 *src, c8 *dst, u32 len, enum strncpy_behaviour behaviour )
 {
    if( src == NULL )
    {
@@ -180,14 +203,14 @@ u32 vg_strncpy( const char *src, char *dst, u32 len, enum strncpy_behaviour beha
    return 0;
 }
 
-static void _vg_strcatf_va( vg_str *str, const char *fmt, va_list args )
+static void _vg_strcatf_va( vg_str *str, const c8 *fmt, va_list args )
 {
-       char buffer[4096];
+       c8 buffer[4096];
    vsnprintf( buffer, VG_ARRAY_LEN(buffer), fmt, args );
    vg_strcat( str, buffer );
 }
 
-void vg_strcatf( vg_str *str, const char *fmt, ... )
+void vg_strcatf( vg_str *str, const c8 *fmt, ... )
 {
    va_list args;
    va_start( args, fmt );
@@ -204,7 +227,7 @@ u32 vg_strdjb2( const c8 *str )
    return hash;
 }
 
-bool vg_strdjb2_eq( const char *s1, u32 h1, const char *s2, u32 h2 )
+bool vg_strdjb2_eq( const c8 *s1, u32 h1, const c8 *s2, u32 h2 )
 {
    if( h1 == h2 )
    {
@@ -214,7 +237,7 @@ bool vg_strdjb2_eq( const char *s1, u32 h1, const char *s2, u32 h2 )
    else return 0;
 }
 
-bool vg_str_eq( const char *s1, const char *s2 )
+bool vg_str_eq( const c8 *s1, const c8 *s2 )
 {
    if( vg_strdjb2( s1 ) == vg_strdjb2( s2 ) )
       return !strcmp( s1, s2 );
@@ -222,18 +245,18 @@ bool vg_str_eq( const char *s1, const char *s2 )
    return 0;
 }
 
-static u32 utf8_byte0_byte_count( u8 char0 )
+static u32 utf8_byte0_byte_count( u8 c80 )
 {
    for( u32 k=2; k<4; k++ )
    {
-      if( !(char0 & (0x80 >> k)) )
+      if( !(c80 & (0x80 >> k)) )
          return k;
    }
 
    return 0;
 }
 
-u32 str_utf8_collapse( const char *str, char *buf, u32 length )
+u32 str_utf8_collapse( const c8 *str, c8 *buf, u32 length )
 {
    u8 *ustr = (u8 *)str;
    u32 utf32_code = 0x00000000;
@@ -250,9 +273,9 @@ u32 str_utf8_collapse( const char *str, char *buf, u32 length )
             utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
             if( !utf32_byte_ct )
             {
-               const char *match;
-               size_t chars = anyascii( utf32_code, &match );
-               for( u32 k=0; 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];
             }
          }
@@ -287,7 +310,7 @@ const c8 *vg_strp_info_str[] =
    [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 ) 
@@ -312,7 +335,7 @@ vg_strp_info vg_strp_u64( vg_strp *p, u64 *value )
    c8 c;
    vg_strp_info info = k_vg_strp_whitespace;
    while( info == k_vg_strp_whitespace )
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
 
    u64 result = 0;
    bool got = 0;
@@ -325,13 +348,13 @@ vg_strp_info vg_strp_u64( vg_strp *p, u64 *value )
       }
       else 
          goto err;
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    }
    info = k_vg_strp_ok;
    goto ok;
    
    err: while( info == k_vg_strp_ok )
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    info = k_vg_strp_error;
 
    ok: *value = result;
@@ -343,7 +366,7 @@ vg_strp_info vg_strp_i64( vg_strp *p, i64 *value )
    c8 c;
    vg_strp_info info = k_vg_strp_whitespace;
    while( info == k_vg_strp_whitespace )
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
 
    i64 result = 0,
        sign   = 1;
@@ -352,7 +375,7 @@ vg_strp_info vg_strp_i64( vg_strp *p, i64 *value )
    {
       if( c == '-' )
          sign = -1;
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    }
 
    bool got = 0;
@@ -366,13 +389,13 @@ vg_strp_info vg_strp_i64( vg_strp *p, i64 *value )
       else 
          goto err;
 
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    }
    info = k_vg_strp_ok;
    goto ok;
    
    err: while( info == k_vg_strp_ok )
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    info = k_vg_strp_error;
 
    ok: *value = result*sign;
@@ -384,7 +407,7 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value )
    c8 c;
    vg_strp_info info = k_vg_strp_whitespace;
    while( info == k_vg_strp_whitespace )
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
 
    i64 result = 0,
        resultm= 0;
@@ -395,7 +418,7 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value )
    {
       if( c == '-' )
          sign = -1.0;
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    }
 
    bool got = 0, got_decimal = 0;
@@ -418,14 +441,14 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value )
       }
       else 
          goto err;
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    }
 
    info = k_vg_strp_ok;
    goto ok;
    
    err: while( info == k_vg_strp_ok )
-      info = vg_strp_char( p, &c );
+      info = vg_strp_c8( p, &c );
    info = k_vg_strp_error;
 
    ok:;
@@ -444,18 +467,18 @@ vg_strp_info vg_strp_f64( vg_strp *p, f64 *value )
    return (got||got_decimal)? info: k_vg_strp_eof;
 }
 
-static u32 vg_strcatu64_internal( c8 reverse_buffer[64], u64 value, u64 base, u32 max_characters )
+static u32 vg_strcatu64_internal( c8 reverse_buffer[64], u64 value, u64 base, u32 max_c8acters )
 {
    VG_ASSERT( base >= 2 );
 
-   if( max_characters == 0 )
-      max_characters = 64;
+   if( max_c8acters == 0 )
+      max_c8acters = 64;
 
    const c8 *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if( value )
    {
       u32 i = 0;
-      while( value && (i<max_characters) )
+      while( value && (i<max_c8acters) )
       {
          reverse_buffer[ i ++ ] = digits[ (u32)(value % base) ];
          value /= base;
@@ -469,7 +492,7 @@ static u32 vg_strcatu64_internal( c8 reverse_buffer[64], u64 value, u64 base, u3
    }
 }
 
-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 );
 
@@ -485,7 +508,7 @@ void vg_strcati64r( vg_str *str, i64 value, u64 base, u32 width, c8 blank_charac
       padding = width - digits;
 
    for( u32 i=0; i<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 ] );
index 1590b985ed6ea0eb58a667bbc8411a2577dc1e94..8ad1bdcdd5d4ae0ac930fd07e6556e1b3876457d 100644 (file)
@@ -1,5 +1,6 @@
-#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. */
 
@@ -8,7 +9,7 @@ typedef struct vg_str_dynamic vg_str_dynamic;
 
 struct vg_str
 {
-   char *buffer;
+   c8 *buffer;
    i32 i,   /* -1: error condition. otherwise, current cursor position */
        len; /* -1: dynamically allocated. otherwise, buffer length */
 };
@@ -27,22 +28,22 @@ i32 vg_str_storage( vg_str *str );
  * 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 );
@@ -53,9 +54,9 @@ 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
 {
@@ -64,17 +65,19 @@ 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
@@ -91,9 +94,15 @@ enum vg_strp_info
    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
index 412571fa244cc416e1507ff24bc9415ee32a85c9..8d911739e3e8655b28eaf19ec93dba216e52da6e 100644 (file)
--- a/vg_tex.c
+++ b/vg_tex.c
-#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) );
 }
index b25780808efd936daa3df4d7d8da12926044118e..bc1c3023ac4ff498ecc848276853d3907f258f41 100644 (file)
--- a/vg_tex.h
+++ b/vg_tex.h
@@ -1,72 +1,66 @@
-/* Copyright (C) 2021-2025 Harry Godden (hgn) - All Rights Reserved 
- *
- * A portion of this file is copied and altered from the QOI projects' source,
- * Originally written by Dominic Szablewski. It is slightly modified.
- * For the original unaltered QOI header, you can find it here:
- *    https://github.com/phoboslab/qoi/blob/master/qoi.h
- *
- * Copyright (C) 2021, Dominic Szablewski 
- * SPDX-License-Identifier: MIT
- *
- * MIT License
-  Copyright (c) 2022 Dominic Szablewski - https://phoboslab.org
-  
-  Permission is hereby granted, free of charge, to any person obtaining a copy
-  of this software and associated documentation files (the "Software"), to deal
-  in the Software without restriction, including without limitation the rights
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the Software is
-  furnished to do so, subject to the following conditions:
-  
-  The above copyright notice and this permission notice shall be included in all
-  copies or substantial portions of the Software.
-  
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-  SOFTWARE.
-*/
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_tex.c"
+#else
 
-#pragma once
-#include "vg_log.h"
-#include "vg_image.h"
-#include "vg_engine.h"
+/* TODO: Include Okaypeg alongside QOI. */
 
-#define QOI_SRGB   0
-#define QOI_LINEAR 1
+typedef union qoi_rgba_t qoi_rgba_t;
+typedef struct qoi_desc qoi_desc;
+typedef struct vg_qoi_ctx vg_qoi_ctx;
+typedef struct vg_tex vg_tex;
+struct vg_tex
+{
+   u32 name;
+   u32 flags;
+};
 
-typedef struct {
-       unsigned int width;
-       unsigned int height;
-       unsigned char channels;
-       unsigned char colorspace;
-} qoi_desc;
+VG_API void _vg_tex_init(void);
 
-struct vg_sprite
+#pragma pack(push,1)
+union qoi_rgba_t
 {
-       v4f uv_xywh;
+   u8 rgba[4];
+   u32 v;
 };
 
-#define VG_TEX2D_LINEAR  0x1
-#define VG_TEX2D_NEAREST 0x2
-#define VG_TEX2D_REPEAT  0x4
-#define VG_TEX2D_CLAMP   0x8
-#define VG_TEX2D_NOMIP   0x10
-#define VG_TEX2D_CUBEMAP 0x20
-#define VG_TEX2D_ERROR   0x40
+struct qoi_desc
+{
+   u32 magic;
+   u32 width;
+   u32 height;
+   u8  channels;
+   u8  colorspace;
+};
+#pragma pack(pop)
 
-/* options to create texutres; call only from loader thread. 
- * *dest will be replaced synchronously by the main thread when ready. */
+# define QOI_SRGB   0
+# define QOI_LINEAR 1
 
-void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest );
+/* Reading qois */
+VG_TIER_0 u32 vg_qoi_stream_init( qoi_desc *desc, vg_stream *file );
+VG_TIER_0 void vg_qoi_stream_decode( qoi_desc *desc, vg_stream *file, u8 *dest_buffer, bool v_flip );
 
-void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, 
-                              u32 flags, GLuint *dest );
+/* Writing qois */
+VG_TIER_0 u32 vg_query_qoi_max_compressed_size( const qoi_desc *desc );
+VG_TIER_0 u32 vg_qoi_stream_encode( const qoi_desc *desc, const u8 *pixels, vg_stream *stream, bool v_flip );
 
-void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest );
+# if defined( VG_ENGINE )
+#  define VG_TEX_LINEAR   0x1
+#  define VG_TEX_NEAREST  0x2
+#  define VG_TEX_REPEAT   0x4
+#  define VG_TEX_CLAMP    0x8
+#  define VG_TEX_NOMIP    0x10
+#  define VG_TEX_CUBEMAP  0x20
+#  define VG_TEX_ERROR    0x40
+#  define VG_TEX_COMPLETE 0x80
+#  define VG_TEX_FLIP_V   0x100
+#  define VG_TEX_FRAMEBUFFER_ATTACHMENT 0x200
+#  define VG_TEX_PRIVATE  0x400 /* used on renderbuffers... */
 
-u32 vg_query_qoi_storage_size( const qoi_desc *desc );
-bool vg_encode_qoi2( const u8 *pixels, const qoi_desc *desc, u8 *out_bytes, int *out_len );
+VG_API u32  vg_tex_name( GLuint target, vg_tex *tex );
+VG_API void vg_tex_bind( GLuint target, vg_tex *tex, u32 slot );
+VG_API void vg_tex_delete( vg_tex *tex );
+VG_API void _vg_tex_load( vg_tex *out_tex, const c8 *path, u32 flags );
+VG_API bool _vg_tex_load_stream( vg_tex *out_tex, vg_stream *in_stream, u32 flags );
+# endif
+#endif
diff --git a/vg_thirdparty.c b/vg_thirdparty.c
new file mode 100644 (file)
index 0000000..ac24e53
--- /dev/null
@@ -0,0 +1,2 @@
+#define VG_THIRDPARTY
+#include "vg/vg.h"
index 84f6ed0b6289ba3ecc0e8fbd2f420c3b174adada..9b877c28a9d745743dfc9475b0f7b10b6ba3875c 100644 (file)
@@ -1,6 +1,3 @@
-#pragma once
-#include "vg_tower.h"
-
 struct _vg_tower _vg_tower;
 
 vg_signal_id _vg_tower_create_signal( const char *name )
index b3b9cae478a0c93951bfc51b8c6e8fb71b46e7b1..988c56ec5e0eca6aea474c17ebd1264a8d2ae7db 100644 (file)
@@ -1,5 +1,6 @@
-#pragma once
-#include "vg_async2.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_tower.c"
+#else
 
 typedef u8 vg_signal_id;
 
@@ -28,3 +29,5 @@ void _vg_tower_register_trigger( u64 mask, void(*fn)( vg_signal_id id, bool stat
 void _vg_tower_set_flag( vg_signal_id id, bool state );
 bool _vg_tower_clearence( u64 mask );
 u64 _vg_tower_mask( vg_signal_id id );
+
+#endif
diff --git a/vg_ui/console.c b/vg_ui/console.c
new file mode 100644 (file)
index 0000000..3639d05
--- /dev/null
@@ -0,0 +1,408 @@
+u32 str_lev_distance( const char *s1, const char *s2 )
+{
+   u32 m = strlen( s1 ),
+       n = strlen( s2 );
+
+   if( m==0 ) return n;
+   if( n==0 ) return m;
+
+   u32 costs[ 256 ];
+
+   for( u32 k=0; k<=n; k++ ) 
+      costs[k] = k;
+
+   u32 i = 0;
+   for( u32 i=0; i<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 );
+}
diff --git a/vg_ui/console.h b/vg_ui/console.h
new file mode 100644 (file)
index 0000000..b3f1df2
--- /dev/null
@@ -0,0 +1,10 @@
+#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
index 172b22d3b21681c9ccaa2aceb24881a0ad714834..e952928394f86d533b58bf79eac1461d71f4aec9 100644 (file)
@@ -1,12 +1,3 @@
-#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;
index 9b0ec2cade6e14f15c91e678ac875ebc4cc8b4f6..c5e4f1dcca7323c9b57c2e36b3dbfb839e265de5 100644 (file)
@@ -1,5 +1,6 @@
-#pragma once
-#include "vg/vg_ui/imgui.h"
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_ui/filebrowser.c"
+#else
 
 struct vg_filebrowser_entry
 {
@@ -52,3 +53,5 @@ void vg_filebrowser_populate( struct vg_filebrowser *browser );
 void vg_filebrowser_free_entries( struct vg_filebrowser *browser );
 
 void vg_filebrowser_set_path_to_home( struct vg_filebrowser *browser );
+
+#endif
index ecfb7d93dcae5eeaad3e6cc9a8d6fc0f0d2b992b..160f76bdc1ea006b8115359057e460e8bb3fe855 100644 (file)
@@ -1,11 +1,5 @@
-#pragma once
-
-#include "vg_ui/imgui.h"
-#include "vg_platform.h"
-#include "vg_m.h"
-#include "vg_font.h"
-#include "vg_log.h"
-#include <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 )
 {
@@ -659,17 +653,14 @@ void ui_spacer( ui_context *ctx, ui_rect inout_panel )
    ui_fill( ctx, inner, ui_colour( ctx, k_ui_bg+6 ) );
 }
 
-void ui_image( ui_context *ctx, ui_rect rect, void *resource, bool flip )
+void ui_image( ui_context *ctx, ui_rect rect, vg_tex *tex, bool flip )
 {
    ui_flush( ctx, k_ui_shader_colour, NULL );
-
    if( flip )
       ui_fill_rect( ctx, rect, 0xffffffff, (ui_px[4]){ 0,0,256,256 } );
    else
       ui_fill_rect( ctx, rect, 0xffffffff, (ui_px[4]){ 0,256,256,0 } );
-
-   struct ui_batch_shader_data_image inf = { .resource = resource };
-   ui_flush( ctx, k_ui_shader_image, &inf );
+   ui_flush( ctx, k_ui_shader_image, tex );
 }
 
 void ui_defocus_all( ui_context *ctx )
index 8ce4782a51c73393721d07af850b51814d20c830..ed840e007f55010c0a0780436e620ebb5e16bc77 100644 (file)
@@ -1,10 +1,6 @@
-#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];
@@ -237,7 +233,7 @@ struct ui_batch_shader_data_image
 
 struct ui_batch_shader_data_image_gradient
 {
-   void *resource;
+   vg_tex *tex;
    bool log;
    f32 scale;
 };
@@ -315,7 +311,7 @@ void ui_panel( ui_context *ctx, ui_rect in_rect, ui_rect out_panel );
 void ui_label( ui_context *ctx, ui_rect rect, const char *text, ui_px size, ui_px gap, ui_rect r );
 void ui_info( ui_context *ctx, ui_rect inout_panel, const char *text );
 void ui_spacer( ui_context *ctx, ui_rect inout_panel );
-void ui_image( ui_context *ctx, ui_rect rect, void *resource, bool flip );
+void ui_image( ui_context *ctx, ui_rect rect, vg_tex *tex, bool flip );
 
 enum ui_button_state ui_button_base( ui_context *ctx, ui_rect rect );
 
@@ -386,3 +382,5 @@ void ui_dev_colourview( ui_context *ctx );
 
 extern vg_font_sheet vg_default_font_sheet;
 extern vg_font_face vgf_default_small, vgf_default_large, vgf_default_title;
+
+#endif
index 69d1187fa5c30db6acc0d6b90191d09c34069a7f..3d2ba35b287454dc71252e2f0931fa43ec7748fa 100644 (file)
@@ -1,8 +1,3 @@
-#include "vg_opengl.h"
-#include "vg_shader.h"
-#include "vg_ui/imgui.h"
-#include "vg_engine.h"
-
 struct vg_ui vg_ui = 
 {
    .ctx = 
@@ -53,227 +48,14 @@ struct vg_ui vg_ui =
    .bg_inverse_ratio = {1,1},
 };
 
-static struct vg_shader _shader_ui ={
-   .name = "[vg] ui - transparent",
-   .vs = {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       "layout (location=1) in vec2 a_uv;"
-       "layout (location=2) in vec4 a_colour;"
-       "uniform mat3 uPv;"
-   "uniform vec2 uBGInverseRatio;"
-   "uniform vec2 uInverseFontSheet;"
-       ""
-       "out vec4 aTexCoords;"
-       "out vec4 aColour;"
-       ""
-       "void main(){"
-      "vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
-               "gl_Position = proj_pos;"
-               "aTexCoords = vec4( a_uv * uInverseFontSheet, "
-                        " (proj_pos.xy*0.5+0.5) * uBGInverseRatio );"
-               "aColour = a_colour;"
-       "}",
-   },
-   .fs = {
-      .orig_file = NULL,
-      .static_src = 
-   "uniform sampler2D uTexGlyphs;"
-       "uniform sampler2D uTexBG;"
-   "uniform vec4 uColour;"
-   "uniform float uSpread;"
-       "out vec4 FragColor;"
-       ""
-       "in vec4 aTexCoords;"
-       "in vec4 aColour;"
-       ""
-   "vec2 rand_hash22( vec2 p ){"
-      "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);"
-      "p3 += dot(p3, p3.yzx+19.19);"
-      "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));"
-   "}"
-       ""
-       "void main(){"
-               "vec4 diffuse = aColour;"
-               
-      "vec4 avg = vec4(0.0);"
-
-               "if( aColour.a == 0.0 ){"
-         "avg = aColour;"
-                       "avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;"
-               "}"
-      "else{"
-         "if( uSpread > 0.0001 ){"
-            "for( int i=0; i<4; i ++ ){"
-               "vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));"
-               "avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );"
-            "}"
-            "avg *= 0.25;"
-            "avg.a = 1.0;"
-            "avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );"
-         "}"
-         "else{"
-            "avg = aColour;"
-         "}"
-      "}"
-
-               "FragColor = avg * uColour;"
-       "}"
-   }
-};
-
-static struct vg_shader _shader_ui_image = {
-   .name = "[vg] ui_image",
-   .vs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       "layout (location=1) in vec2 a_uv;"
-       "layout (location=2) in vec4 a_colour;"
-       "uniform mat3 uPv;"
-
-       "out vec2 aTexCoords;"
-       "out vec4 aColour;"
-       "out vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
-               "aTexCoords = a_uv * 0.00390625;"
-               "aColour = a_colour;"
-               
-               "aWsp = a_co;"
-       "}",
-   },
-   .fs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "uniform sampler2D uTexImage;"
-   "uniform vec4 uColour;"
-       "out vec4 FragColor;"
-
-       "in vec2 aTexCoords;"
-       "in vec4 aColour;"
-       "in vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "vec4 colour = texture( uTexImage, aTexCoords );"
-               "FragColor = colour * aColour * uColour;"
-       "}"
-   }
-};
-
-static struct vg_shader _shader_ui_image_grad = {
-   .name = "[vg] ui_image (gradient)",
-   .vs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       "layout (location=1) in vec2 a_uv;"
-       "layout (location=2) in vec4 a_colour;"
-       "uniform mat3 uPv;"
-
-       "out vec2 aTexCoords;"
-       "out vec4 aColour;"
-       "out vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
-               "aTexCoords = a_uv * 0.00390625;"
-               "aColour = a_colour;"
-               
-               "aWsp = a_co;"
-       "}",
-   },
-   .fs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "uniform sampler2D uTexImage;"
-   "uniform vec4 uColour;"
-   "uniform int uLog;"
-   "uniform float uScale;"
-
-       "out vec4 FragColor;"
-
-       "in vec2 aTexCoords;"
-       "in vec4 aColour;"
-       "in vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "vec4 colour = texture( uTexImage, aTexCoords );"
-      "float v = colour.r;"
-      "if( uLog == 1 ){"
-         "v = log(v);"
-      "}"
-      "v *= uScale;"
-               "FragColor = vec4(vec3(v)*uColour.rgb,1.0);"
-       "}"
-   }
-};
-
-static struct vg_shader _shader_ui_hsv = {
-   .name = "[vg] ui_hsv",
-   .vs = {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       "layout (location=1) in vec2 a_uv;"
-       "layout (location=2) in vec4 a_colour;"
-       "uniform mat3 uPv;"
-
-       "out vec2 aTexCoords;"
-       "out vec4 aColour;"
-       "out vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
-               "aTexCoords = a_uv * 0.00390625;"
-               "aColour = a_colour;"
-               
-               "aWsp = a_co;"
-       "}",
-   },
-   .fs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-   "uniform float uHue;"
-       "out vec4 FragColor;"
-
-       "in vec2 aTexCoords;"
-       "in vec4 aColour;"
-       "in vec2 aWsp;"
-
-       "void main()"
-       "{"
-      "vec3 c = vec3( uHue, aTexCoords );"
-      "vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);"
-      "vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);"
-      "vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);"
-               "FragColor = vec4( colour, 1.0 );"
-       "}"
-   }
-};
-
-void vg_ui_set_screen( i32 width, i32 height )
+VG_API void vg_ui_set_screen( i32 width, i32 height )
 {
    ui_set_area( &vg_ui.ctx, width, height );
    m3x3_identity( vg_ui.pv );
    m3x3_translate( vg_ui.pv, (v3f){ -1.0f, 1.0f, 0.0f } );
-   m3x3_scale( vg_ui.pv, (v3f){ 1.0f/((f32)width*0.5f), 
-                                   -1.0f/((f32)height*0.5f), 1.0f } );
+   m3x3_scale( vg_ui.pv, (v3f){ 1.0f/((f32)width*0.5f), -1.0f/((f32)height*0.5f), 1.0f } );
 }
-void ui_impl_render_batch( ui_context *ctx, ui_batch *batch, 
-                           enum ui_shader shader, void *shader_data )
+void ui_impl_render_batch( ui_context *ctx, ui_batch *batch, enum ui_shader shader, void *shader_data )
 {
        glBindVertexArray( vg_ui.vao );
        glBindBuffer( GL_ARRAY_BUFFER, vg_ui.vbo );
@@ -296,86 +78,63 @@ void ui_impl_render_batch( ui_context *ctx, ui_batch *batch,
 
    if( shader == k_ui_shader_colour )
    {
-      glUseProgram( _shader_ui.id );
-      glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1, GL_FALSE, (float *)vg_ui.pv );
-      glUniform4fv( glGetUniformLocation( _shader_ui.id, "uColour" ), 1, vg_ui.colour );
-       
+      shader_vgui_use();
+      shader_vgui_uPv( vg_ui.pv );
+      shader_vgui_uColour( vg_ui.colour );
+      
       glActiveTexture( GL_TEXTURE0 );
       glBindTexture( GL_TEXTURE_2D, vg_ui.tex_glyphs );
-      glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 );
-
-      glActiveTexture( GL_TEXTURE1 );
-      glBindTexture( GL_TEXTURE_2D, vg_ui.tex_bg );
-      glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexBG" ), 1 );
-      glUniform1f( glGetUniformLocation( _shader_ui.id, "uSpread" ), vg_ui.frosting );
-      glUniform2fv( glGetUniformLocation( _shader_ui.id, "uBGInverseRatio" ),1, vg_ui.bg_inverse_ratio );
-      glUniform2fv( glGetUniformLocation( _shader_ui.id, "uInverseFontSheet" ), 1, vg_ui.inverse_font_sheet );
+      shader_vgui_uTexGlyphs( 0 );
+
+      vg_tex_bind( GL_TEXTURE_2D, vg_ui.tex_bg, 1 );
+      shader_vgui_uTexBG( 1 );
+      shader_vgui_uSpread( vg_ui.frosting );
+      shader_vgui_uBGInverseRatio( vg_ui.bg_inverse_ratio );
+      shader_vgui_uInverseFontSheet( vg_ui.inverse_font_sheet );
    }
    else if( shader == k_ui_shader_image )
    {
-      glUseProgram( _shader_ui_image.id );
-      glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1, 
-                          GL_FALSE, (float *)vg_ui.pv );
-      glUniform1i( glGetUniformLocation(_shader_ui_image.id,"uTexImage"), 0 );
-      glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1,
-                    vg_ui.colour );
-
-      struct ui_batch_shader_data_image *inf = shader_data;
-      GLuint *image = inf->resource;
-
-      glActiveTexture( GL_TEXTURE0 );
-      glBindTexture( GL_TEXTURE_2D, *image );
+      shader_vgui_image_use();
+      shader_vgui_image_uPv( vg_ui.pv );
+      shader_vgui_image_uTexImage( 0 );
+      shader_vgui_image_uColour( vg_ui.colour );
+      vg_tex_bind( GL_TEXTURE_2D, shader_data, 0 );
    }
    else if( shader == k_ui_shader_grad )
    {
-      glUseProgram( _shader_ui_image_grad.id );
-      glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image_grad.id, "uPv" ), 1, GL_FALSE, (float *)vg_ui.pv );
-      glUniform1i( glGetUniformLocation(_shader_ui_image_grad.id,"uTexImage"), 0 );
-      glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1, vg_ui.colour );
+      shader_vgui_image_grad_use();
+      shader_vgui_image_grad_uPv( vg_ui.pv );
+      shader_vgui_image_grad_uTexImage( 0 );
+      shader_vgui_image_grad_uColour( vg_ui.colour );
 
       struct ui_batch_shader_data_image_gradient *inf = shader_data;
-      glUniform1i( glGetUniformLocation(_shader_ui_image_grad.id,"uLog"), inf->log );
-      glUniform1f( glGetUniformLocation(_shader_ui_image_grad.id,"uScale"), inf->scale );
-
-      GLuint *image = inf->resource;
-
-      glActiveTexture( GL_TEXTURE0 );
-      glBindTexture( GL_TEXTURE_2D, *image );
+      shader_vgui_image_grad_uLog( inf->log );
+      shader_vgui_image_grad_uScale( inf->scale );
+      vg_tex_bind( GL_TEXTURE_2D, inf->tex, 0 );
    }
    else if( shader == k_ui_shader_hsv )
    {
       struct ui_batch_shader_data_hsv *inf = shader_data;
-      glUseProgram( _shader_ui_hsv.id );
-      glUniformMatrix3fv( glGetUniformLocation( _shader_ui_hsv.id, "uPv" ), 1, GL_FALSE, (float *)vg_ui.pv );
-      glUniform1f( glGetUniformLocation(_shader_ui_hsv.id,"uHue"), inf->hue );
+      shader_vgui_image_hsv_use();
+      shader_vgui_image_hsv_uPv( vg_ui.pv );
+      shader_vgui_image_hsv_uHue( inf->hue );
    }
    else
       vg_fatal_error( "Invalid UI shader (%d)\n", shader );
 
-       glDrawElements( GL_TRIANGLES, batch->indice_count, GL_UNSIGNED_SHORT, 
-                   (void *)((size_t)batch->indice_offset) );
+       glDrawElements( GL_TRIANGLES, batch->indice_count, GL_UNSIGNED_SHORT, (void *)((size_t)batch->indice_offset) );
 }
 
-void vg_ui_post_update(void)
+VG_API void vg_ui_post_update(void)
 {
-   if( vg_ui.ctx.wants_mouse )
-   {
-      SDL_SetWindowGrab( vg.window, SDL_FALSE );
-      SDL_SetRelativeMouseMode( SDL_FALSE );
-   }
-   else
-   {
-      SDL_SetWindowGrab( vg.window, SDL_TRUE );
-      SDL_SetRelativeMouseMode( SDL_TRUE );
-   }
-
+   _vg_window_set_fpv_mouse( !vg_ui.ctx.wants_mouse );
    SDL_SetCursor( vg_ui.cursor_map[ vg_ui.ctx.cursor ] );
    SDL_ShowCursor(1);
 }
 
 void vg_ui_set_mouse_pos( ui_px x, ui_px y )
 {
-   SDL_WarpMouseInWindow( vg.window, x, y );
+   _vg_window_set_mouse( x, y );
    vg_ui.ctx.mouse[0] = x;
    vg_ui.ctx.mouse[1] = y;
    vg_ui.ctx.mouse_pos_overriden = 1;
@@ -458,15 +217,12 @@ bool _wrap_sdl_hasclipboard_text(void)
    return SDL_HasClipboardText();
 }
 
-void vg_ui_init(void)
+VG_API void _vg_ui_init(void)
 {
-   vg_compile_shader( &_shader_ui );
-   vg_compile_shader( &_shader_ui_hsv );
-   vg_compile_shader( &_shader_ui_image );
-   vg_compile_shader( &_shader_ui_image_grad );
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
 
    u32 verts = 30000, indices = 20000;
-   ui_init( &vg_ui.ctx, malloc( sizeof(ui_vert)*verts ), verts, malloc( sizeof(u16)*indices ), indices );
+   ui_init( &vg_ui.ctx, vg_malloc( sizeof(ui_vert)*verts ), verts, vg_malloc( sizeof(u16)*indices ), indices );
 
    /* callbacks */
    vg_ui.ctx.render_batch = ui_impl_render_batch;
@@ -521,9 +277,7 @@ void vg_ui_init(void)
        data = 0;
 
    /* This is fince since its before the load thread even starts */
-   vg_stack_clear( &vg.scratch );
-   u8 *image = vg_stack_allocate( &vg.scratch, total, 8, NULL );
-   
+   u8 image[512*512];
    while( pixels < total )
    {
       for( int b = 31; b >= 0; b-- )
@@ -554,10 +308,6 @@ void vg_ui_init(void)
 
 void ui_free(void)
 {
-   vg_free_shader( &_shader_ui );
-   vg_free_shader( &_shader_ui_hsv );
-   vg_free_shader( &_shader_ui_image );
-   vg_free_shader( &_shader_ui_image_grad );
    free( vg_ui.ctx.indice_buffer );
    free( vg_ui.ctx.vertex_buffer );
 }
diff --git a/vg_ui/imgui_impl_opengl.h b/vg_ui/imgui_impl_opengl.h
new file mode 100644 (file)
index 0000000..5cc4cd4
--- /dev/null
@@ -0,0 +1,28 @@
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_ui/imgui_impl_opengl.c"
+#else
+
+struct vg_ui
+{
+   GLuint vao, vbo, ebo;
+   m3x3f pv;
+   ui_context ctx;
+   GLuint tex_glyphs;
+   v2f inverse_font_sheet;
+
+   SDL_Cursor *cursor_map[ k_ui_cursor_max ];
+
+   /* at some point this should be implementation specific? */
+   v4f colour;
+   f32 frosting;
+   v2f bg_inverse_ratio;
+
+   vg_tex *tex_bg;
+}
+extern vg_ui;
+
+VG_API void _vg_ui_init(void);
+VG_API void vg_ui_post_update(void);
+VG_API void vg_ui_set_screen( i32 width, i32 height );
+
+#endif
diff --git a/vg_vorbis.c b/vg_vorbis.c
new file mode 100644 (file)
index 0000000..266a167
--- /dev/null
@@ -0,0 +1,69 @@
+/* 
+ * adapted from stb_vorbis.h, since the original does not handle mono->stereo
+ */
+int stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, int len )
+{
+   int n = 0, c = 1;
+   if( f->channels < 2 ) c = 0;
+
+   while( n < len ) {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+
+      if( n+k >= len ) 
+         k = len - n;
+
+      for( int j=0; j < k; ++j ) {
+         *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j];
+         *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j];
+      }
+
+      n += k;
+      f->channel_buffer_start += k;
+
+      if( n == len )
+         break;
+
+      if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
+         break;
+   }
+
+   return n;
+}
+
+/* 
+ * ........ more wrecked code sorry!
+ */
+int stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, signed short *buffer, int len )
+{
+   int n = 0, c = 1;
+   if( f->channels < 2 ) c = 0;
+
+   while( n < len ) {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+
+      if( n+k >= len ) 
+         k = len - n;
+
+      for( int j=0; j < k; ++j ) {
+         float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j],
+               sr = f->channel_buffers[ c ][f->channel_buffer_start+j],
+               s  = 0.5f*(sl+sr);
+
+         if( s < -1.0f ) s = -1.0f;
+         if( s >  1.0f ) s =  1.0f;
+
+         *buffer++ = s * 32767.0f;
+      }
+
+      n += k;
+      f->channel_buffer_start += k;
+
+      if( n == len )
+         break;
+
+      if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
+         break;
+   }
+
+   return n;
+}
index b27bf4daff6e153222e3c862a3175bc068f19875..f1e60e9631ced8d6e954f88d8f2dae5a0704dc64 100644 (file)
@@ -1,12 +1,4 @@
-#define STB_VORBIS_HEADER_ONLY
-#define STB_VORBIS_MAX_CHANNELS 2
-#include "submodules/stb/stb_vorbis.c"
-#include "vg_platform.h"
-#include "vg_m.h"
+/* This is just an extension to submodules/stb/stb_vorbis.c, so that we don't have to patch the file directly. */
 
-/* extended by vg */
-int 
-stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, 
-                                                 int len );
-int 
-stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len );
+int stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, int len );
+int stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, signed short *buffer, int len );
diff --git a/vg_window.c b/vg_window.c
new file mode 100644 (file)
index 0000000..fc4d50f
--- /dev/null
@@ -0,0 +1,255 @@
+struct _vg_window _vg_window;
+
+void GLAPIENTRY MessageCallback( GLenum source,
+                 GLenum type,
+                 GLuint id,
+                 GLenum severity,
+                 GLsizei length,
+                 const GLchar* message,
+                 const void* userParam )
+{
+   if( severity == GL_DEBUG_SEVERITY_HIGH )
+   {
+      vg_error( "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+                  ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+                  type, severity, message );
+   }
+   else
+   {
+      vg_low( "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+                  ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+                  type, severity, message );
+   }
+}
+
+VG_API void _vg_window_register(void)
+{
+   vg_console_reg_var( "vg_screen_mode", &_vg_window.display_mode, k_var_dtype_i32, VG_VAR_PERSISTENT );
+   vg_console_reg_var( "vg_vsync", &_vg_window.vsync, k_var_dtype_i32, VG_VAR_PERSISTENT );
+}
+
+VG_API void _vg_window_init(void)
+{
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL|VG_THREAD_OWNS_OPENGL ) );
+   SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+#if 0
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
+#endif
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 );
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR, SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
+   SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
+   SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
+   SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
+   SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
+   SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
+
+   /* 
+    * Get monitor information 
+    */
+   vg_info( "Getting display count\n" );
+   i32 display_count = 0, 
+       display_index = 0, 
+       mode_index = 0;
+
+   SDL_DisplayMode video_mode;
+   if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) )
+   {
+      vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() );
+      SDL_Quit();
+      exit(0);
+   }
+
+   _vg_window.monitor_refresh_rate = video_mode.refresh_rate;
+   _vg_window.w = video_mode.w; 
+   _vg_window.h = video_mode.h; 
+   if( _vg_window.display_mode == k_vg_window_windowed )
+   {
+      _vg_window.w = 1280;
+      _vg_window.h = 720;
+   }
+
+#ifndef _WIN32
+       SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" );
+       SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" );
+       SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" );
+#endif
+
+   u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_GRABBED |
+               SDL_WINDOW_RESIZABLE;
+
+   if( _vg_window.display_mode == k_vg_window_fullscreen )
+      flags |= SDL_WINDOW_FULLSCREEN;
+   else if( _vg_window.display_mode == k_vg_window_fullscreen_desktop )
+      flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+
+   vg_info( "CreateWindow( %d %d %u )\n", _vg_window.w, _vg_window.h, flags );
+   if(( _vg_window.sdl_hwindow = SDL_CreateWindow( "Voyager Game Engine", 0, 0, _vg_window.w, _vg_window.h, flags )))
+   {
+      if( _vg_window.display_mode == k_vg_window_windowed )
+         SDL_SetWindowPosition( _vg_window.sdl_hwindow, video_mode.w-_vg_window.w, 0 );
+   }
+   else
+   {
+      vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
+      exit(0);
+   }
+
+   SDL_RaiseWindow( _vg_window.sdl_hwindow );
+   SDL_SetWindowMinimumSize( _vg_window.sdl_hwindow, 1280, 720 );
+   SDL_SetWindowMaximumSize( _vg_window.sdl_hwindow, 4096, 4096 );
+
+   vg_info( "CreateContext\n" );
+
+   /* ????? */
+   if( SDL_IsTextInputActive() ) 
+      SDL_StopTextInput();
+
+   /* 
+    * OpenGL loading 
+    */
+   if( ( _vg_window.sdl_hopengl = SDL_GL_CreateContext( _vg_window.sdl_hwindow ) ))
+   {
+      SDL_GL_GetDrawableSize( _vg_window.sdl_hwindow, &_vg_window.w, &_vg_window.h );
+      vg_success( "Window created (%dx%d)\n", _vg_window.w, _vg_window.h );
+   }
+   else
+   {
+      vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
+      SDL_Quit();
+      exit(0);
+   }
+
+   if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) 
+   {
+      vg_error( "Glad Failed to initialize\n" );
+      SDL_GL_DeleteContext( _vg_window.sdl_hopengl );
+      SDL_Quit();
+      exit(0);
+   }
+
+   const unsigned char* glver = glGetString( GL_VERSION );
+   vg_success( "Load setup complete, OpenGL version: %s\n", glver );
+
+   
+   glEnable              ( GL_DEBUG_OUTPUT );
+   glDebugMessageCallback( MessageCallback, 0 );
+
+   SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
+   SDL_DisplayMode dispmode;
+   if( !SDL_GetWindowDisplayMode( _vg_window.sdl_hwindow, &dispmode ) )
+      if( dispmode.refresh_rate )
+         _vg_window.monitor_refresh_rate = dispmode.refresh_rate;
+
+   if( _vg_window.monitor_refresh_rate < 25 || _vg_window.monitor_refresh_rate > 300 )
+      _vg_window.monitor_refresh_rate = 60;
+   vg_info( "Display refresh rate: %d, we chose: %d\n", dispmode.refresh_rate, _vg_window.monitor_refresh_rate );
+
+   SDL_SetRelativeMouseMode(1);
+}
+   
+VG_API void _vg_window_swap(void)
+{
+   /* Vsync switching 
+    * ----------------------------- */
+   if( _vg_window.vsync && (_vg_window.vsync_feature != k_vsync_feature_error) )
+   {
+      /* turn on vsync if not enabled */
+
+      enum vsync_feature requested = k_vsync_feature_enabled;
+      if( _vg_window.vsync < 0 ) 
+         requested = k_vsync_feature_enabled_adaptive;
+
+      if( _vg_window.vsync_feature != requested )
+      {
+         vg_info( "Setting swap interval\n" );
+         int swap_interval = 1;
+         if( requested == k_vsync_feature_enabled_adaptive ) 
+            swap_interval = -1;
+
+         if( SDL_GL_SetSwapInterval( swap_interval ) == -1 )
+         {
+            if( requested == k_vsync_feature_enabled )
+            {
+               ui_start_modal( &vg_ui.ctx, "Vsync not supported on this system.\n\n"
+                                           "You may be overriding it in your"
+                                           " graphics \ncontrol panel.\n",
+                                           NULL, UI_MODAL_BAD, NULL );
+            }
+            else
+            {
+               ui_start_modal( &vg_ui.ctx, "Adaptive Vsync is not supported by your system\n\n"
+                                           "You may be overriding it in your"
+                                           " graphics \ncontrol panel.\n",
+                                           NULL, UI_MODAL_BAD, NULL );
+            }
+            _vg_window.vsync_feature = k_vsync_feature_error;
+            _vg_window.vsync = 0;
+         }
+         else
+         {
+            vg_success( "Vsync enabled (%d)\n", requested );
+            _vg_window.vsync_feature = requested;
+         }
+      }
+   }
+   else 
+   {
+      if( _vg_window.vsync_feature != k_vsync_feature_disabled )
+      {
+         SDL_GL_SetSwapInterval( 0 );
+         _vg_window.vsync_feature = k_vsync_feature_disabled;
+      }
+   }
+
+   _vg_profiler_enter_block( vg.profiler, "vg_engine:swap" );
+   SDL_GL_SwapWindow( _vg_window.sdl_hwindow );
+   _vg_profiler_exit_block( vg.profiler );
+}
+
+VG_API void _vg_window_size_changed(void)
+{
+   i32 w, h;
+   SDL_GL_GetDrawableSize( _vg_window.sdl_hwindow, &w, &h );
+
+   if( !w || !h )
+   {
+      vg_warn( "Got a invalid framebuffer size: %dx%d... ignoring\n", w, h );
+   }
+   else
+   {
+
+      i32 delta[2] = { w - _vg_window.w, h - _vg_window.h };
+      _vg_magi_area_change( delta );
+      _vg_window.w = w;
+      _vg_window.h = h;
+      
+      vg_framebuffer_update_sizes();
+   }
+}
+
+VG_API void _vg_window_set_fpv_mouse( bool yes )
+{
+   if( yes )
+   {
+      SDL_SetWindowGrab( _vg_window.sdl_hwindow, SDL_TRUE );
+      SDL_SetRelativeMouseMode( SDL_TRUE );
+   }
+   else
+   {
+      SDL_SetWindowGrab( _vg_window.sdl_hwindow, SDL_FALSE );
+      SDL_SetRelativeMouseMode( SDL_FALSE );
+   }
+}
+
+VG_API void _vg_window_set_mouse( i32 x, i32 y )
+{
+   SDL_WarpMouseInWindow( _vg_window.sdl_hwindow, x, y );
+}
+
+VG_API void _vg_window_shutdown(void)
+{
+   SDL_GL_DeleteContext( _vg_window.sdl_hopengl );
+}
diff --git a/vg_window.h b/vg_window.h
new file mode 100644 (file)
index 0000000..f1b99b2
--- /dev/null
@@ -0,0 +1,42 @@
+#if defined( VG_IMPLEMENTATION )
+# include "vg/vg_window.c"
+#else
+
+enum vg_window_display_mode
+{
+   k_vg_window_fullscreen_desktop = 0,
+   k_vg_window_fullscreen = 1,
+   k_vg_window_windowed = 2
+};
+
+struct _vg_window
+{
+   SDL_Window     *sdl_hwindow;
+   SDL_GLContext  *sdl_hopengl;
+   i32 w, h, monitor_refresh_rate, vsync, display_index;
+
+   i32 display_mode;
+
+   enum vsync_feature
+   {
+      k_vsync_feature_disabled=0,
+      k_vsync_feature_enabled=1,
+      k_vsync_feature_enabled_adaptive=2,
+      k_vsync_feature_error=3
+   }
+   vsync_feature;
+
+   u32 swap_profiler;
+}
+extern _vg_window;
+
+VG_API void _vg_window_register(void);
+VG_API void _vg_window_init(void);
+
+VG_API void _vg_window_shutdown(void);
+VG_API void _vg_window_swap(void);
+VG_API void _vg_window_size_changed(void);
+VG_API void _vg_window_set_fpv_mouse( bool yes );
+VG_API void _vg_window_set_mouse( i32 x, i32 y );
+
+#endif