dsp reverb etc
authorhgn <hgodden00@gmail.com>
Fri, 10 Mar 2023 09:05:16 +0000 (09:05 +0000)
committerhgn <hgodden00@gmail.com>
Fri, 10 Mar 2023 09:05:16 +0000 (09:05 +0000)
vg_audio.h
vg_audio_dsp.h [new file with mode: 0644]
vg_mem.h
vg_profiler.h
vg_ui.h

index 8cccbd06a6515efff20850df5d8faddc90eea59e..fc0a278facf8c8c8756b866c8dab4c8a0110511a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
+/* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
 
 #ifndef VG_AUDIO_H
 #define VG_AUDIO_H
   #endif
 #endif
 
+#define AUDIO_FRAME_SIZE 512
+#define AUDIO_MIX_FRAME_SIZE 256
+
 #define AUDIO_CHANNELS        32
 #define AUDIO_LFOS            8
+#define AUDIO_FILTERS         16
 #define AUDIO_FLAG_LOOP       0x1
-#define AUDIO_FLAG_SPACIAL_3D 0x2
+#define AUDIO_FLAG_SPACIAL_3D 0x4
+#define AUDIO_FLAG_AUTO_START 0x8
 
 /* Vorbis will ALWAYS use the maximum amount of channels it can */
 //#define AUDIO_FLAG_MONO       0x100 NOTE: This is the default, so its not used
@@ -175,14 +180,18 @@ static struct vg_audio_system
 }
 vg_audio = { .volume_console = 1.0f };
 
+#include "vg/vg_audio_dsp.h"
 
 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,
-   vg_prof_audio_mix;
+   vg_prof_audio_mix,
+   vg_prof_audio_dsp;
 
 /* 
  * These functions are called from the main thread and used to prevent bad 
@@ -259,20 +268,21 @@ VG_STATIC void vg_audio_init(void)
    u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
    vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size );
 
+   vg_dsp_init();
+
    SDL_AudioSpec spec_desired, spec_got;
    spec_desired.callback = audio_mixer_callback;
    spec_desired.channels = 2;
    spec_desired.format   = AUDIO_F32;
    spec_desired.freq     = 44100;
    spec_desired.padding  = 0;
-   spec_desired.samples  = 512;
+   spec_desired.samples  = AUDIO_FRAME_SIZE;
    spec_desired.silence  = 0;
    spec_desired.size     = 0;
    spec_desired.userdata = NULL;
 
    vg_audio.sdl_output_device = 
-      SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,
-                                    SDL_AUDIO_ALLOW_SAMPLES_CHANGE );
+      SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,0 );
 
    if( vg_audio.sdl_output_device )
    {
@@ -293,6 +303,7 @@ VG_STATIC void vg_audio_init(void)
 
 VG_STATIC void vg_audio_free(void)
 {
+   vg_dsp_free();
    SDL_CloseAudioDevice( vg_audio.sdl_output_device );
 }
 
@@ -609,7 +620,7 @@ stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len )
    return n;
 }
 
-static float audio_lfo_pull_sample( audio_lfo *lfo )
+static inline float audio_lfo_pull_sample( audio_lfo *lfo )
 {
    lfo->time ++;
 
@@ -724,24 +735,18 @@ static void audio_channel_get_samples( audio_channel *ch,
    vg_profile_end( &_vg_prof_audio_decode );
 }
 
-static void audio_channel_mix( audio_channel *ch, 
-                               float *buffer, u32 frame_count )
+static void audio_channel_mix( audio_channel *ch, float *buffer )
 {
-   u32 frames_write = frame_count;
-
-   u32 buffer_length = frame_count;
+   u32 buffer_length = AUDIO_MIX_FRAME_SIZE;
    if( ch->_.sampling_rate != 1.0f )
    {
-      buffer_length = ceilf( (float)frame_count * ch->_.sampling_rate ) + 1;
+      float l = ceilf( (float)(AUDIO_MIX_FRAME_SIZE) * ch->_.sampling_rate );
+      buffer_length = l+1;
    }
 
-   float *pcf = alloca( buffer_length * 2 * sizeof(float) );
+   float pcf[ AUDIO_MIX_FRAME_SIZE * 2 * 2 ];
 
    audio_channel_get_samples( ch, buffer_length, pcf );
-   vg_profile_begin( &_vg_prof_audio_mix );
-
-   if( ch->_.lfo )
-      ch->_.lfo->time = ch->_.lfo->time_startframe;
 
    float framevol_l = 1.0f,
          framevol_r = 1.0f;
@@ -775,27 +780,43 @@ static void audio_channel_mix( audio_channel *ch,
       framevol_r *= (vol * 0.5f) * (1.0f + pan);
    }
 
-   for( u32 j=0; j<frame_count; j++ )
-   {
-      if( ch->volume_movement < ch->_.volume_rate )
-      {
-         ch->volume_movement ++;
+   vg_profile_begin( &_vg_prof_audio_mix );
 
-         float movement_t  = ch->volume_movement;
-               movement_t /= (float)ch->_.volume_rate;
+   float volume_movement = ch->volume_movement;
+   float const fvolume_rate = vg_maxf( 1.0f, ch->_.volume_rate );
+   const float inv_volume_rate = 1.0f/fvolume_rate;
 
-         ch->_.volume = vg_lerpf( ch->volume_movement_start,
-                                  ch->_.volume_target, 
-                                  movement_t );
-      }
+   float volume = ch->_.volume;
+   const float volume_start  = ch->volume_movement_start;
+   const float volume_target = ch->_.volume_target;
 
-      float vol_norm = ch->_.volume * ch->_.volume;
+   for( u32 j=0; j<AUDIO_MIX_FRAME_SIZE; j++ )
+   {
+      /*
+       * there is some REALLY weird behaviour with minss,
+       * i cannot begin to guess what the cause is, but the bahaviour when
+       * the second argument is not 1.0 would seemingly tripple or up to 
+       * eight times this routine.
+       *
+       * the times it would happen are when moving from empty space into areas
+       * with geometry. in the bvh for skate rift.
+       *
+       * it should be completely unrelated to this, but somehow -- it is
+       * effecting the speed of minss. and severely at that too.
+       **/
+
+      volume_movement += 1.0f;
+      float movement_t = volume_movement * inv_volume_rate;
+            movement_t = vg_minf( volume_movement, 1.0f );
+      volume           = vg_lerpf( volume_start, volume_target, movement_t );
+
+      float vol_norm = volume * volume;
 
       if( ch->_.lfo )
          vol_norm *= 1.0f + audio_lfo_pull_sample(ch->_.lfo) * ch->_.lfo_amount;
 
-      float vol_l     = vol_norm * framevol_l,
-            vol_r     = vol_norm * framevol_r,
+      float vol_l = vol_norm * framevol_l,
+            vol_r = vol_norm * framevol_r,
             sample_l,
             sample_r;
       
@@ -823,6 +844,10 @@ static void audio_channel_mix( audio_channel *ch,
       buffer[ j*2+1 ] += sample_r * vol_r;
    }
 
+   ch->volume_movement += AUDIO_MIX_FRAME_SIZE;
+   ch->volume_movement  = VG_MIN( ch->volume_movement, ch->_.volume_rate );
+   ch->_.volume = volume;
+
    vg_profile_end( &_vg_prof_audio_mix );
 }
 
@@ -956,7 +981,7 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count )
       lfo->editble_state_write_mask = 0x00;
    }
 
-
+   dsp_update_tunings();
    audio_unlock();
 
    /*
@@ -996,9 +1021,29 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count )
       audio_channel *ch = &vg_audio.channels[i];
 
       if( ch->activity == k_channel_activity_alive )
-         audio_channel_mix( ch, pOut32F, frame_count );
+      {
+         if( ch->_.lfo )
+            ch->_.lfo->time = ch->_.lfo->time_startframe;
+
+         u32 remaining = frame_count,
+             subpos    = 0;
+
+         while( remaining )
+         {
+            audio_channel_mix( ch, pOut32F+subpos );
+            remaining -= AUDIO_MIX_FRAME_SIZE;
+            subpos += AUDIO_MIX_FRAME_SIZE*2;
+         }
+      }
    }
 
+   vg_profile_begin( &_vg_prof_dsp );
+
+   for( int i=0; i<frame_count; i++ )
+      vg_dsp_process( pOut32F + i*2, pOut32F + i*2 );
+
+   vg_profile_end( &_vg_prof_dsp );
+
    /* 
     * Relinquishing conditions
     * ------------------------------------------------------------------
@@ -1009,10 +1054,19 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count )
     * ----------------------------------------------- */
    vg_profile_increment( &_vg_prof_audio_decode );
    vg_profile_increment( &_vg_prof_audio_mix );
+   vg_profile_increment( &_vg_prof_dsp );
+
    vg_prof_audio_mix = _vg_prof_audio_mix;
    vg_prof_audio_decode = _vg_prof_audio_decode;
+   vg_prof_audio_dsp = _vg_prof_dsp;
+
    vg_audio.samples_last = frame_count;
 
+   if( vg_audio.debug_ui )
+   {
+      vg_dsp_update_texture();
+   }
+
    audio_unlock();
 }
 
@@ -1109,6 +1163,11 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv )
 
    audio_lock();
 
+   glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture );
+   glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 256, 256, 
+                     GL_RGBA, GL_UNSIGNED_BYTE,
+                     vg_dsp.view_texture_buffer );
+
    /* 
     * Profiler
     * -----------------------------------------------------------------------
@@ -1116,18 +1175,22 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv )
 
    float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
    vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
-                                              &vg_prof_audio_mix }, 2, 
+                                              &vg_prof_audio_mix,
+                                              &vg_prof_audio_dsp}, 3, 
                      budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
-                                        250, 0 }, 3 );
+                                        512, 0 }, 3 );
 
 
    char perf[128];
        
    /* Draw UI */
-   vg_uictx.cursor[0] = 258;
-   vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12;
+   vg_uictx.cursor[0] = 512 + 8;
+   vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12+12;
    vg_uictx.cursor[2] = 150;
    vg_uictx.cursor[3] = 12;
+
+   ui_rect view_thing = { 4, vg.window_y-512-4, 512, 512 };
+   ui_push_image( view_thing, vg_dsp.view_texture );
    
    float mb1      = 1024.0f*1024.0f,
          usage    = vg_linear_get_cur( vg_audio.audio_pool )      / mb1,
diff --git a/vg_audio_dsp.h b/vg_audio_dsp.h
new file mode 100644 (file)
index 0000000..4c7d85c
--- /dev/null
@@ -0,0 +1,300 @@
+#ifndef VG_AUDIO_DSP_H
+#define VG_AUDIO_DSP_H
+
+#define VG_GAME
+#include "vg/vg.h"
+
+static struct vg_dsp
+{
+   float            *buffer;
+   u32               allocations;
+
+   u8               *view_texture_buffer;
+   GLuint            view_texture;
+
+   float             echo_distances[14],
+                     echo_tunings[8],
+                     reverb_wet_mix,
+                     reverb_dry_mix;
+}
+vg_dsp;
+
+static float *dsp_allocate( u32 samples )
+{
+   samples = vg_align4( samples );
+
+   if( vg_dsp.allocations + samples > (1024*1024)/4 )
+      vg_fatal_exit_loop( "too much dsp" );
+
+   float *buf = &vg_dsp.buffer[ vg_dsp.allocations ];
+   vg_dsp.allocations += samples;
+
+   return buf;
+}
+
+
+/*
+ * filters
+ * ----------------------------------------------
+ */
+
+struct dsp_delay
+{
+   u32 length, cur;
+   float *buffer;
+};
+
+struct dsp_lpf
+{
+   float exponent;
+   float *buffer;
+};
+
+struct dsp_schroeder
+{
+   struct dsp_delay M;
+   float gain;
+};
+
+static inline void dsp_read_delay( struct dsp_delay *delay, float *s )
+{
+   u32 index = delay->cur+1;
+
+   if( index >= delay->length )
+      index = 0;
+
+   *s = delay->buffer[ index ];
+}
+
+static inline void dsp_write_delay( struct dsp_delay *delay, float *s )
+{
+   u32 index = delay->cur;
+   delay->buffer[ index ] = *s;
+
+   delay->cur ++;
+
+   if( delay->cur >= delay->length )
+      delay->cur = 0;
+}
+
+static void dsp_init_delay( struct dsp_delay *delay, float length )
+{
+   delay->length = 44100.0f * length;
+   delay->cur = 0;
+   delay->buffer = dsp_allocate( delay->length );
+
+   for( int i=0; i<delay->length; i++ )
+      delay->buffer[i] = 0.0f;
+}
+
+static void dsp_update_lpf( struct dsp_lpf *lpf, float freq )
+{
+   lpf->exponent = 1.0f-expf( -(1.0f/44100.0f) * 2.0f * VG_PIf * freq );
+}
+
+static void dsp_init_lpf( struct dsp_lpf *lpf, float freq )
+{
+   lpf->buffer = dsp_allocate( 4 );
+   lpf->buffer[0] = 0.0f;
+   dsp_update_lpf( lpf, freq );
+}
+
+static inline void dsp_write_lpf( struct dsp_lpf *lpf, float *s )
+{
+   float diff = *s - lpf->buffer[0];
+   lpf->buffer[0] += diff * lpf->exponent;
+}
+
+static inline void dsp_read_lpf( struct dsp_lpf *lpf, float *s )
+{
+   *s = lpf->buffer[0];
+}
+
+static void dsp_init_schroeder( struct dsp_schroeder *sch, float length,
+                                float gain )
+{
+   dsp_init_delay( &sch->M, length );
+   sch->gain = gain;
+}
+
+static inline void dsp_process_schroeder( struct dsp_schroeder *sch,
+                                          float *input, float *output )
+{
+   float dry = *input;
+
+   float delay_output;
+   dsp_read_delay( &sch->M, &delay_output );
+
+   float feedback_attenuated = delay_output * sch->gain,
+         input_feedback_sum  = dry + feedback_attenuated;
+
+   dsp_write_delay( &sch->M, &input_feedback_sum );
+
+   *output = delay_output - input_feedback_sum*sch->gain;
+}
+
+/* temporary global design */
+static struct dsp_lpf __lpf_mud_free;
+static struct dsp_delay __echos[8];
+static struct dsp_lpf   __echos_lpf[8];
+static struct dsp_schroeder __diffusion_chain[8];
+
+static void vg_dsp_init( void )
+{
+   vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 );
+   vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 );
+
+   vg_acquire_thread_sync();
+   glGenTextures( 1, &vg_dsp.view_texture );
+   glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture );
+   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, 
+                 GL_RGBA, GL_UNSIGNED_BYTE, vg_dsp.view_texture_buffer );
+   vg_tex2d_nearest();
+   vg_release_thread_sync();
+
+
+   /* temporary global design */
+
+
+   dsp_init_lpf( &__lpf_mud_free, 125.0f );
+
+   float sizes[] = 
+         { 2.0f, 4.0f, 8.0f, 16.0f,   32.0f, 64.0f, 128.0f, 256.0f };
+
+   float reflection_variance = 0.1f;
+
+   for( int i=0; i<8; i++ )
+   {
+      float reflection_time = ((sizes[i])/343.0f) * 1000.0f;
+
+      float var   = 1.0f + (vg_randf()*2.0f - 1.0f) * reflection_variance,
+            total = reflection_time * var;
+
+      dsp_init_delay( &__echos[i], total / 1000.0f );
+
+      float freq = vg_lerpf( 800.0f, 350.0f, sizes[i] / 256.0f );
+      dsp_init_lpf( &__echos_lpf[i], freq );
+   }
+
+   float diffusions[] = { 187.0f, 159.0f, 143.0f, 121.0f, 
+                          79.0f,  57.0f,  27.0f,  11.0f };
+
+   for( int i=0; i<8; i++ )
+   {
+      dsp_init_schroeder( __diffusion_chain+i, diffusions[i]/1000.0f, 0.7f );
+   }
+}
+
+static void vg_dsp_process( float *stereo_in, float *stereo_out )
+{
+   float in_total = (stereo_in[0]+stereo_in[1])*0.5f;
+   float recieved = 0.0f;
+
+   for( int i=0; i<8; i++ )
+   {
+      float echo;
+      dsp_read_delay(  __echos+i, &echo );
+      dsp_write_lpf( __echos_lpf+i, &echo );
+      dsp_read_lpf(  __echos_lpf+i, &echo );
+
+      recieved += echo * vg_dsp.echo_tunings[i]*0.98;
+   }
+
+   float diffused = recieved;
+
+   for( int i=0; i<8; i++ )
+   {
+      dsp_process_schroeder( __diffusion_chain+i, &diffused, &diffused );
+   }
+
+   float diffuse_mix = vg_dsp.reverb_wet_mix;
+         diffuse_mix = vg_lerpf( recieved, diffused, diffuse_mix );
+   float total = in_total + diffuse_mix;
+
+   float low_mud;
+   dsp_write_lpf( &__lpf_mud_free, &total );
+   dsp_read_lpf(  &__lpf_mud_free, &low_mud );
+
+   total -= low_mud;
+
+   for( int i=0; i<8; i++ )
+      dsp_write_delay( __echos+i, &total );
+
+   stereo_out[0]  = stereo_in[0]*vg_dsp.reverb_dry_mix;
+   stereo_out[1]  = stereo_in[1]*vg_dsp.reverb_dry_mix;
+   stereo_out[0] += diffuse_mix*2.0f*vg_dsp.reverb_wet_mix;
+   stereo_out[1] += diffuse_mix*2.0f*vg_dsp.reverb_wet_mix;
+}
+
+static void dsp_update_tunings(void)
+{
+   float sizes[] = 
+         { 2.0f, 4.0f, 8.0f, 16.0f,   32.0f, 64.0f, 128.0f, 256.0f };
+
+   float avg_distance = 0.0f;
+
+   for( int i=0; i<8; i++ )
+      vg_dsp.echo_tunings[i] = 0.5f;
+
+   for( int j=0; j<14; j++ )
+   {
+      float d = vg_dsp.echo_distances[j];
+      
+      for( int i=0; i<7; i++ )
+      {
+         if( d < sizes[i+1] )
+         {
+            float range = sizes[i+1]-sizes[i];
+            float t = vg_clampf( (d - sizes[i])/range, 0.0f, 1.0f );
+
+            vg_dsp.echo_tunings[i  ] += 1.0f-t;
+            vg_dsp.echo_tunings[i+1] += t;
+
+            break;
+         }
+      }
+
+      avg_distance += d;
+   }
+   avg_distance /= 14.0f;
+   
+
+   vg_dsp.reverb_wet_mix =1.0f-vg_clampf((avg_distance-30.0f)/200.0f,0.0f,1.0f);
+   vg_dsp.reverb_dry_mix =1.0f-vg_dsp.reverb_wet_mix*0.4f;
+
+   float total = 0.0f;
+   for( int i=0; i<8; i++ )
+      total += vg_dsp.echo_tunings[i];
+
+   if( total > 0.0f )
+   {
+      float inverse = 1.0f/total;
+
+      for( int i=0;i<8; i++ )
+      {
+         vg_dsp.echo_tunings[i] *= inverse;
+      }
+   }
+
+   for( int i=0; i<8; i++ )
+   {
+      float freq = vg_lerpf( 200.0f, 500.0f, vg_dsp.echo_tunings[i] );
+      dsp_update_lpf( &__echos_lpf[i], freq );
+   }
+}
+
+static void vg_dsp_free( void )
+{
+   glDeleteTextures( 1, &vg_dsp.view_texture );
+}
+
+static void vg_dsp_update_texture( void )
+{
+   for( int i=0; i<512*512; i++ )
+   {
+      float v = vg_clampf( vg_dsp.buffer[i] * 0.5f + 0.5f, 0.0f, 1.0f );
+      vg_dsp.view_texture_buffer[i] = v * 255.0f;
+   }
+}
+
+#endif /* VG_AUDIO_DSP_H */
index 5edbd9c68ebc60560860f98a53a62687bfb6dc4b..ccb652c70bc424c9457258651fe5452ef2de5dcd 100644 (file)
--- a/vg_mem.h
+++ b/vg_mem.h
@@ -96,6 +96,12 @@ VG_STATIC u32 vg_align8( u32 s )
    return m << 3;
 }
 
+VG_STATIC u32 vg_align4( u32 s )
+{
+   u32 m = (s + 3) >> 2;
+   return m << 2;
+}
+
 /* Returns allocator structure from data pointer */
 static vg_linear_allocator *vg_linear_header( void *data )
 {
index c8db76dab3c7ad8bfcf3322eeec37e2e0659e3a3..5c0690c9186b6010a0ecee9515fc9528cc2397a0 100644 (file)
@@ -13,8 +13,8 @@ struct vg_profile
 {
    const char *name;
 
-   float samples[ VG_PROFILE_SAMPLE_COUNT ];
-   u32   buffer_count, buffer_current;
+   u64    samples[ VG_PROFILE_SAMPLE_COUNT ];
+   u32    buffer_count, buffer_current;
 
    enum profile_mode
    {
@@ -41,7 +41,7 @@ VG_STATIC void vg_profile_increment( struct vg_profile *profile )
    if( profile->buffer_current >= VG_PROFILE_SAMPLE_COUNT )
       profile->buffer_current = 0;
 
-   profile->samples[ profile->buffer_current ] = 0.0f;
+   profile->samples[ profile->buffer_current ] = 0;
 }
 
 VG_STATIC void vg_profile_end( struct vg_profile *profile )
@@ -51,7 +51,7 @@ VG_STATIC void vg_profile_end( struct vg_profile *profile )
 
    if( profile->mode == k_profile_mode_frame )
    {
-      profile->samples[ profile->buffer_current ] = (float)delta;
+      profile->samples[ profile->buffer_current ] = delta;
       vg_profile_increment( profile );
    }
    else
@@ -60,12 +60,6 @@ VG_STATIC void vg_profile_end( struct vg_profile *profile )
    }
 }
 
-VG_STATIC void vg_profile_graph_sample( struct vg_profile *profile, float s )
-{
-   profile->samples[ profile->buffer_current ] = s;
-   vg_profile_increment( profile );
-}
-
 VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count,
                               float budget, ui_rect panel, u32 colour_offset )
 {
@@ -84,8 +78,8 @@ VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count,
    ui_fill_rect( panel, 0xa0000000 );
 
    assert( count <= 8 );
-   float avgs[8];
-   int   ptrs[8];
+   double avgs[8];
+   int    ptrs[8];
 
    for( int i=0; i<count; i++ )
    {
@@ -96,11 +90,11 @@ VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count,
    u32 colours[] = { 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000,
                      0xffff00ff, 0xffffff00 };
 
-   float rate_mul = 1000.0f / (float)SDL_GetPerformanceFrequency();
+   double rate_mul = 1000.0 / (double)SDL_GetPerformanceFrequency();
 
    for( int i=0; i<VG_PROFILE_SAMPLE_COUNT-1; i++ )
    {
-      float total = 0.0f;
+      double total = 0.0;
 
       for( int j=0; j<count; j++ )
       {
@@ -109,9 +103,9 @@ VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count,
          if( ptrs[j] < 0 )
             ptrs[j] = VG_PROFILE_SAMPLE_COUNT-1;
 
-         float sample  = profiles[j]->samples[ptrs[j]] * rate_mul,
-               px      = (total  / (budget)) * sw,
-               wx      = (sample / (budget)) * sw;
+         double sample  = (double)profiles[j]->samples[ptrs[j]] * rate_mul,
+                px      = (total  / (budget)) * sw,
+                wx      = (sample / (budget)) * sw;
 
          ui_rect block = { panel[0] + px, panel[1] + (float)i*sh,
                            wx,            sh };
@@ -126,6 +120,13 @@ VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count,
 
    char infbuf[64];
 
+   snprintf( infbuf, 64, "accuracy: %.7fms", rate_mul );
+   ui_text( (ui_rect){ panel[0] + 4,
+                       panel[1] + 0 * 14, 0, 0 }, 
+                       infbuf,
+                       1,
+                       k_text_align_left );
+
    for( int i=0; i<count; i++ )
    {
       snprintf( infbuf, 64, "%.4fms %s", 
diff --git a/vg_ui.h b/vg_ui.h
index c86e4c809c5b407d6002718524266ed9c6809990..dc0cceec4288b3dbeb7365d29846e273ad72a918 100644 (file)
--- a/vg_ui.h
+++ b/vg_ui.h
@@ -74,6 +74,66 @@ static struct vg_shader _shader_ui =
    }
 };
 
+static struct vg_shader _shader_ui_image =
+{
+   .name = "[vg] ui_image",
+   .link = NULL,
+   .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;"
+       "layout (location=3) in vec4 a_clip;"
+       "uniform mat3 uPv;"
+       ""
+       "out vec2 aTexCoords;"
+       "out vec4 aColour;"
+       "out vec2 aWsp;"
+       "out vec4 aClip;"
+       ""
+       "void main()"
+       "{"
+               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
+               "aTexCoords = a_uv * 0.0078125;"
+               "aColour = a_colour;"
+               
+               "aWsp = a_co;"
+               "aClip = a_clip;"
+       "}",
+   },
+   .fs = 
+   {
+      .orig_file = NULL,
+      .static_src = 
+       "uniform sampler2D uTexImage;"
+       "out vec4 FragColor;"
+       ""
+       "in vec2 aTexCoords;"
+       "in vec4 aColour;"
+       ""
+       "in vec2 aWsp;"
+       "in vec4 aClip;"
+
+       "void main()"
+       "{"
+               "float clip_blend = step( aWsp.x, aClip.z ) *"
+                         "step( aWsp.y, aClip.w ) *"
+                         "step( aClip.x, aWsp.x ) *"
+                         "step( aClip.y, aWsp.y ); "
+
+               "vec4 colour = texture( uTexImage, aTexCoords );"
+      "float value = dot(vec4(1.0),colour)*0.25;"
+
+      "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
+                  "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
+
+               "FragColor = vec4( col*4.0, clip_blend );"
+       "}"
+   }
+};
+
 typedef i16                            ui_px;
 typedef u32                            ui_colour;
 typedef ui_px                          ui_rect[4];
@@ -170,7 +230,8 @@ static ui_colourset ui_default_colours = {
 
 VG_STATIC void _vg_ui_init(void)
 {
-   if( !vg_shader_compile( &_shader_ui ) ) 
+   if( !vg_shader_compile( &_shader_ui ) ||
+       !vg_shader_compile( &_shader_ui_image ) ) 
       vg_fatal_exit_loop( "Failed to compile ui shader" );
 
    /*
@@ -322,6 +383,15 @@ VG_STATIC void ui_draw( m3x3f view_override )
        
        glDrawElements( GL_TRIANGLES, num_indices_normal, 
          GL_UNSIGNED_SHORT, (void*)(0) );
+
+
+   /* images */
+   glUseProgram( _shader_ui_image.id );
+       glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1, 
+         GL_FALSE, (float *)view_override );
+
+       glActiveTexture( GL_TEXTURE1 );
+       glUniform1i( glGetUniformLocation( _shader_ui_image.id, "uTexImage" ), 1 );
        
        /* Draw image elements */
        for( int i = 0; i < vg_uictx.image_count; i ++ )