From 522d022b70a01eaa31ce1c8c00c452d4262f5f39 Mon Sep 17 00:00:00 2001 From: Jarcode Date: Sat, 3 Feb 2018 11:08:28 -0800 Subject: [PATCH] Added smooth pass option, improved texture lookup performance, fixes #10 (and fixed issues with config copying) --- glava.c | 4 +- render.c | 274 +++++++++++++++++++++++---------- shaders/bars.glsl | 2 - shaders/bars/1.frag | 2 +- shaders/graph.glsl | 2 - shaders/graph/1.frag | 2 +- shaders/radial.glsl | 2 - shaders/radial/1.frag | 2 +- shaders/smooth_parameters.glsl | 11 ++ shaders/util/smooth.glsl | 22 ++- shaders/util/smooth_pass.frag | 16 ++ 11 files changed, 240 insertions(+), 99 deletions(-) create mode 100644 shaders/util/smooth_pass.frag diff --git a/glava.c b/glava.c index 811aea6..a651db4 100644 --- a/glava.c +++ b/glava.c @@ -103,7 +103,7 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) { fprintf(stderr, "failed to open (source) '%s': %s\n", p, strerror(errno)); goto cleanup; } - if ((dest = open(f, O_WRONLY | O_CREAT, ACCESSPERMS)) < 0) { + if ((dest = open(f, O_TRUNC | O_WRONLY | O_CREAT, ACCESSPERMS)) < 0) { fprintf(stderr, "failed to open (destination) '%s': %s\n", f, strerror(errno)); goto cleanup; } @@ -126,7 +126,7 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) { } break; case 2: - if (symlink(p, f)) + if (symlink(p, f) && errno != EEXIST) fprintf(stderr, "failed to symlink '%s' -> '%s': %s\n", p, f, strerror(errno)); else if (verbose) printf("symlink '%s' -> '%s'\n", p, f); diff --git a/render.c b/render.c index ff15e4d..41f574c 100644 --- a/render.c +++ b/render.c @@ -36,6 +36,74 @@ #define VERTEX_SHADER_SRC \ "layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos.x, pos.y, 0.0F, 1.0F); }" +/* GLSL bind source */ + +struct gl_bind_src { + const char* name; + int type; + int src_type; +}; + +/* function that can be applied to uniform binds */ + +struct gl_transform { + const char* name; + int type; + void (*apply)(struct gl_data*, void**, void* data); +}; + +/* data for sampler1D */ + +struct gl_sampler_data { + float* buf; + size_t sz; +}; + +/* GLSL uniform bind */ + +struct gl_bind { + const char* name; + GLuint uniform; + int type; + int src_type; + void (**transformations)(struct gl_data*, void**, void* data); + size_t t_sz; + void* udata; +}; + +/* GL screen framebuffer object */ + +struct gl_sfbo { + GLuint fbo, tex, shader; + bool valid; + const char* name; + struct gl_bind* binds; + size_t binds_sz; +}; + +/* data for screen-space overlay (quad) */ + +struct overlay_data { + GLuint vbuf, vao; +}; + +struct gl_data { + struct gl_sfbo* stages; + struct overlay_data overlay; + GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog; + size_t stages_sz, bufscale, avg_frames; + GLFWwindow* w; + int lww, lwh, lwx, lwy; /* last window dimensions */ + int rate; /* framerate */ + double tcounter; + int fcounter, ucounter, kcounter; + bool print_fps, avg_window, interpolate, force_geometry, copy_desktop, smooth_pass; + void** t_data; + float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio, smooth_factor, fft_scale, fft_cutoff; + float* interpolate_buf[6]; + int geometry[4]; +}; + /* load shader file */ static GLuint shaderload(const char* rpath, GLenum type, @@ -43,7 +111,8 @@ static GLuint shaderload(const char* rpath, const char* config, struct request_handler* handlers, int shader_version, - bool raw) { + bool raw, + struct gl_data* gl) { size_t s_len = strlen(shader); @@ -73,7 +142,11 @@ static GLuint shaderload(const char* rpath, const GLchar* map = raw ? shader : mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - static const GLchar* header_fmt = "#version %d\n#define UNIFORM_LIMIT %d\n"; + static const GLchar* header_fmt = + "#version %d\n" + "#define UNIFORM_LIMIT %d\n" + "#define PRE_SMOOTHED_AUDIO %d\n" + "#define SMOOTH_FACTOR %.6f\n"; struct glsl_ext ext = { .source = raw ? NULL : map, @@ -88,9 +161,10 @@ static GLuint shaderload(const char* rpath, /* If this is raw input, skip processing */ if (!raw) ext_process(&ext, rpath); - size_t blen = strlen(header_fmt) + 28; + size_t blen = strlen(header_fmt) + 42; GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len); - int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms); + int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms, + gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor); if (written < 0) { fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path); return 0; @@ -173,9 +247,10 @@ static GLuint shaderlink_f(GLuint* arr) { } /* load shaders */ -#define shaderbuild(shader_path, c, r, v, ...) \ - shaderbuild_f(shader_path, c, r, v, (const char*[]) {__VA_ARGS__, 0}) -static GLuint shaderbuild_f(const char* shader_path, +#define shaderbuild(gl, shader_path, c, r, v, ...) \ + shaderbuild_f(gl, shader_path, c, r, v, (const char*[]) {__VA_ARGS__, 0}) +static GLuint shaderbuild_f(struct gl_data* gl, + const char* shader_path, const char* config, struct request_handler* handlers, int shader_version, @@ -193,7 +268,7 @@ static GLuint shaderbuild_f(const char* shader_path, if (!strcmp(path + t + 1, "frag") || !strcmp(path + t + 1, "glsl")) { if (!(shaders[i] = shaderload(path, GL_FRAGMENT_SHADER, shader_path, config, handlers, - shader_version, false))) { + shader_version, false, gl))) { return 0; } } else if (!strcmp(path + t + 1, "vert")) { @@ -213,7 +288,7 @@ static GLuint shaderbuild_f(const char* shader_path, } } /* load builtin vertex shader */ - shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, handlers, shader_version, true); + shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, handlers, shader_version, true, gl); fflush(stdout); return shaderlink_f(shaders); } @@ -247,48 +322,8 @@ static void update_1d_tex(GLuint tex, size_t w, float* data) { #define BIND_SAMPLER1D 8 #define BIND_SAMPLER2D 9 -/* GLSL bind source */ - -struct gl_bind_src { - const char* name; - int type; - int src_type; -}; - -/* function that can be applied to uniform binds */ - -struct gl_transform { - const char* name; - int type; - void (*apply)(struct gl_data*, void**, void* data); -}; - -struct gl_sampler_data { - float* buf; - size_t sz; -}; - -/* GLSL uniform bind */ - -struct gl_bind { - const char* name; - GLuint uniform; - int type; - int src_type; - void (**transformations)(struct gl_data*, void**, void* data); - size_t t_sz; -}; - /* setup screen framebuffer object and its texture */ -struct gl_sfbo { - GLuint fbo, tex, shader; - bool valid; - const char* name; - struct gl_bind* binds; - size_t binds_sz; -}; - static void setup_sfbo(struct gl_sfbo* s, int w, int h) { GLuint tex = s->valid ? s->tex : ({ glGenTextures(1, &s->tex); s->tex; }); GLuint fbo = s->valid ? s->fbo : ({ glGenFramebuffers(1, &s->fbo); s->fbo; }); @@ -311,10 +346,6 @@ static void setup_sfbo(struct gl_sfbo* s, int w, int h) { glBindFramebuffer(GL_FRAMEBUFFER, 0); } -struct overlay_data { - GLuint vbuf, vao; -}; - static void overlay(struct overlay_data* d) { GLfloat buf[18]; buf[0] = -1.0f; buf[1] = -1.0f; buf[2] = 0.0f; @@ -352,23 +383,6 @@ static void drawoverlay(const struct overlay_data* d) { #define TRANSFORM_FFT 1 #define TRANSFORM_WINDOW 2 -struct gl_data { - struct gl_sfbo* stages; - struct overlay_data overlay; - GLuint audio_tex_r, audio_tex_l, bg_tex; - size_t stages_sz, bufscale, avg_frames; - GLFWwindow* w; - int lww, lwh, lwx, lwy; /* last window dimensions */ - int rate; /* framerate */ - double tcounter; - int fcounter, ucounter, kcounter; - bool print_fps, avg_window, interpolate, force_geometry, copy_desktop; - void** t_data; - float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio, fft_scale, fft_cutoff; - float* interpolate_buf[6]; - int geometry[4]; -}; - #ifdef GLAD_DEBUG struct err_msg { @@ -677,10 +691,13 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force .gravity_step = 4.2, .interpolate = true, .force_geometry = false, + .smooth_factor = 0.025, .smooth_distance = 0.01, .smooth_ratio = 4, .bg_tex = 0, + .sm_prog = 0, .copy_desktop = true, + .smooth_pass = true, .fft_scale = 10.2F, .fft_cutoff = 0.3F, .geometry = { 0, 0, 500, 400 } @@ -814,6 +831,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force .handler = RHANDLER(name, args, { gl->avg_window = *(bool*) args[0]; }) }, { .name = "setgravitystep", .fmt = "f", .handler = RHANDLER(name, args, { gl->gravity_step = *(float*) args[0]; }) }, + { .name = "setsmoothpass", .fmt = "b", + .handler = RHANDLER(name, args, { gl->smooth_pass = *(bool*) args[0]; }) }, + { .name = "setsmoothfactor", .fmt = "f", + .handler = RHANDLER(name, args, { gl->smooth_factor = *(float*) args[0]; }) }, { .name = "setsmooth", .fmt = "f", .handler = RHANDLER(name, args, { gl->smooth_distance = *(float*) args[0]; }) }, { .name = "setsmoothratio", .fmt = "f", @@ -887,7 +908,8 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force .type = src->type, .src_type = src->src_type, .transformations = malloc(1), - .t_sz = 0 + .t_sz = 0, + .udata = NULL }; }) }, @@ -1038,7 +1060,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force }; current = s; - GLuint id = shaderbuild(shaders, data, handlers, shader_version, d->d_name); + GLuint id = shaderbuild(gl, shaders, data, handlers, shader_version, d->d_name); if (!id) { abort(); } @@ -1073,6 +1095,18 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force } } + { + const char* util_folder = "util"; + size_t u_len = strlen(util_folder); + size_t usz = d_len + u_len + 2; + char util[usz]; /* module pack path to use */ + snprintf(util, usz, "%s/%s", data, util_folder); + + if (!(gl->sm_prog = shaderbuild(gl, util, data, handlers, shader_version, "smooth_pass.frag"))) { + abort(); + } + } + gl->stages = stages; gl->stages_sz = count; @@ -1243,9 +1277,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi "}" "\n"; if (!setup) { bg_prog = shaderlink(shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, - NULL, NULL, 330, true), + NULL, NULL, 330, true, gl), shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader, - NULL, NULL, 330, true)); + NULL, NULL, 330, true, gl)); bg_utex = glGetUniformLocation(bg_prog, "tex"); bg_screen = glGetUniformLocation(bg_prog, "screen"); glBindFragDataLocation(bg_prog, 1, "fragment"); @@ -1278,7 +1312,7 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi struct gl_bind* bind = ¤t->binds[b]; /* Handle transformations and bindings for 1D samplers */ - void handle_1d_tex(GLuint tex, float* buf, float* ubuf, size_t sz, int offset) { + void handle_1d_tex(GLuint tex, float* buf, float* ubuf, size_t sz, int offset, bool audio) { /* Only apply transformations if the buffers we were given are newly copied from PA */ @@ -1294,10 +1328,90 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi transform types are added) */ } } + + /* Update texture with our data */ + update_1d_tex(tex, sz, gl->interpolate ? (ubuf ? ubuf : buf) : buf); + + /* Apply pre-smoothing shader pass if configured */ + if (audio && gl->smooth_pass) { + static bool setup = false; + static GLuint sm_utex, sm_usz, sm_uw; + + /* Compile preprocess shader and handle uniform locations */ + if (!setup) { + sm_utex = glGetUniformLocation(gl->sm_prog, "tex"); + sm_usz = glGetUniformLocation(gl->sm_prog, "sz"); + sm_uw = glGetUniformLocation(gl->sm_prog, "w"); + glBindFragDataLocation(gl->sm_prog, 1, "fragment"); + setup = true; + } + + /* Per-bind data containing the framebuffer and 1D texture to render to for + this smoothing step. */ + + struct sm_fb { + GLuint fbo, tex; + }; + + /* Allocate and setup our per-bind data, if needed */ + + struct sm_fb* sm; + if (!bind->udata) { + sm = malloc(sizeof(struct sm_fb)); + bind->udata = sm; + + glGenTextures(1, &sm->tex); + glGenFramebuffers(1, &sm->fbo); + + /* 1D texture parameters */ + glBindTexture(GL_TEXTURE_1D, sm->tex); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, sz, 0, GL_RED, GL_FLOAT, NULL); + + /* setup and bind framebuffer to texture */ + glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo); + glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_1D, sm->tex, 0); + + switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) { + case GL_FRAMEBUFFER_COMPLETE: break; + default: + fprintf(stderr, "error in frambuffer state\n"); + abort(); + } + } else { + /* Just bind our data if it was already allocated and setup */ + sm = (struct sm_fb*) bind->udata; + glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo); + glBindTexture(GL_TEXTURE_1D, sm->tex); + } + + glUseProgram(gl->sm_prog); + glActiveTexture(GL_TEXTURE0 + offset); + glBindTexture(GL_TEXTURE_1D, tex); + glUniform1i(sm_uw, sz); /* target texture width */ + glUniform1i(sm_usz, sz); /* source texture width */ + glUniform1i(sm_utex, offset); + glDisable(GL_BLEND); + glViewport(0, 0, sz, 1); + drawoverlay(&gl->overlay); + glViewport(0, 0, ww, wh); + glEnable(GL_BLEND); + + /* Return state */ + glUseProgram(current->shader); + if (current->valid) + glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); + else + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + tex = sm->tex; /* replace input texture with our processed one */ + } if (!needed[offset]) { glActiveTexture(GL_TEXTURE0 + offset); - update_1d_tex(tex, sz, gl->interpolate ? (ubuf ? ubuf : buf) : buf); glBindTexture(GL_TEXTURE_1D, tex); needed[offset] = true; } @@ -1319,10 +1433,10 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi } glUniform1i(bind->uniform, 0); break; - case SRC_AUDIO_L: handle_1d_tex(gl->audio_tex_l, lb, ilb, bsz, 1); break; - case SRC_AUDIO_R: handle_1d_tex(gl->audio_tex_r, rb, irb, bsz, 2); break; - case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break; - case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break; + case SRC_AUDIO_L: handle_1d_tex(gl->audio_tex_l, lb, ilb, bsz, 1, true); break; + case SRC_AUDIO_R: handle_1d_tex(gl->audio_tex_r, rb, irb, bsz, 2, true); break; + case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break; + case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break; } } diff --git a/shaders/bars.glsl b/shaders/bars.glsl index 170ba14..29b179c 100644 --- a/shaders/bars.glsl +++ b/shaders/bars.glsl @@ -18,6 +18,4 @@ #define DIRECTION 0 /* Whether to switch left/right audio buffers */ #define INVERT 0 -/* Smoothing factor, in normalized width */ -#define SMOOTH 0.025 diff --git a/shaders/bars/1.frag b/shaders/bars/1.frag index ab7a10f..6ca28b7 100644 --- a/shaders/bars/1.frag +++ b/shaders/bars/1.frag @@ -41,7 +41,7 @@ void main() { float p = int(dx / section) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */ p += sign(p) * ((0.5F + center) / screen.x); /* index center of bar position */ /* Apply smooth function and index texture */ - #define smooth_f(tex, p) smooth_audio(tex, audio_sz, p, SMOOTH) + #define smooth_f(tex, p) smooth_audio(tex, audio_sz, p) float v; /* ignore out of bounds values */ if (p > 1.0F || p < -1.0F) { diff --git a/shaders/graph.glsl b/shaders/graph.glsl index b92b081..560bda0 100644 --- a/shaders/graph.glsl +++ b/shaders/graph.glsl @@ -3,8 +3,6 @@ #define VSCALE 450 /* Rendering direction, either -1 (outwards) or 1 (inwards). */ #define DIRECTION 1 -/* Smoothing factor, in normalized width */ -#define SMOOTH 0.008 /* The `RCOL_OFF`, `LCOL_OFF` AND `LSTEP` definitions are used to calculate the `COLOR` macro definition for output. You can remove all these values diff --git a/shaders/graph/1.frag b/shaders/graph/1.frag index c6ebda9..682a7ae 100644 --- a/shaders/graph/1.frag +++ b/shaders/graph/1.frag @@ -84,7 +84,7 @@ float half_w; void render_side(in sampler1D tex, float idx) { highp float pixel = 1.0F / float(screen.x); - float s = smooth_audio_adj(tex, audio_sz, idx / half_w, SMOOTH, pixel); + float s = smooth_audio_adj(tex, audio_sz, idx / half_w, pixel); /* scale the data upwards so we can see it */ s *= VSCALE; /* clamp far ends of the screen down to make the ends of the graph smoother */ diff --git a/shaders/radial.glsl b/shaders/radial.glsl index e701b0b..7db987a 100644 --- a/shaders/radial.glsl +++ b/shaders/radial.glsl @@ -21,5 +21,3 @@ #define ROTATE (PI / 2) /* Whether to switch left/right audio buffers */ #define INVERT 0 -/* Smoothing factor, in normalized width */ -#define SMOOTH 0.025 diff --git a/shaders/radial/1.frag b/shaders/radial/1.frag index 14da78f..1e31dae 100644 --- a/shaders/radial/1.frag +++ b/shaders/radial/1.frag @@ -50,7 +50,7 @@ void main() { if (INVERT > 0) idx = -idx; /* Invert if needed */ float pos = int(abs(idx) / section) / float(NBARS / 2); /* bar position, [0, 1) */ - #define smooth_f(tex) smooth_audio(tex, audio_sz, pos, SMOOTH) /* smooth function format */ + #define smooth_f(tex) smooth_audio(tex, audio_sz, pos) /* smooth function format */ float v; if (idx > 0) v = smooth_f(audio_l); /* left buffer */ else v = smooth_f(audio_r); /* right buffer */ diff --git a/shaders/smooth_parameters.glsl b/shaders/smooth_parameters.glsl index fcc7fcd..1ae7129 100644 --- a/shaders/smooth_parameters.glsl +++ b/shaders/smooth_parameters.glsl @@ -45,3 +45,14 @@ val -= (gravitystep) * (seconds per update) */ #request setgravitystep 4.2 + +/* Smoothing factor. Larger values mean more smoothing in the output, + however high values can be expensive to compute. Values are in + normalized width: [0.0, 1.0) */ +#request setsmoothfactor 0.025 + +/* Whether to use a separate pass for audio data while smoothing. On + most hardware, this will improve performance, but involves doing a + separate render step for each audio texture and will add some driver + (CPU) overhead. */ +#request setsmoothpass true diff --git a/shaders/util/smooth.glsl b/shaders/util/smooth.glsl index 51d9bd3..df8458e 100644 --- a/shaders/util/smooth.glsl +++ b/shaders/util/smooth.glsl @@ -29,28 +29,34 @@ float iscale_audio(float idx) { return -log((SAMPLE_RANGE) * idx) / (SAMPLE_SCALE); } -float smooth_audio(in sampler1D tex, int tex_sz, highp float idx, float r) { +/* Note: the SMOOTH_FACTOR macro is defined by GLava itself, from `#request setsmoothfactor`*/ + +float smooth_audio(in sampler1D tex, int tex_sz, highp float idx) { + #if PRE_SMOOTHED_AUDIO < 1 float - smin = scale_audio(clamp(idx - r, 0, 1)) * tex_sz, - smax = scale_audio(clamp(idx + r, 0, 1)) * tex_sz, + smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz, + smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz, avg = 0, s, weight = 0; float m = ((smax - smin) / 2.0F); float rm = smin + m; /* middle */ for (s = smin; s <= smax; s += 1.0F) { float w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1)); weight += w; - avg += texture(tex, float(s) / float(tex_sz)).r * w; + avg += texelFetch(tex, int(round(s)), 0).r * w; } avg /= weight; return avg; + #else + return texelFetch(tex, int(round(idx * tex_sz)), 0).r; + #endif } /* Applies the audio smooth sampling function three times to the adjacent values */ -float smooth_audio_adj(in sampler1D tex, int tex_sz, highp float idx, float r, highp float pixel) { +float smooth_audio_adj(in sampler1D tex, int tex_sz, highp float idx, highp float pixel) { float - al = smooth_audio(tex, tex_sz, max(idx - pixel, 0.0F), r), - am = smooth_audio(tex, tex_sz, idx, r), - ar = smooth_audio(tex, tex_sz, min(idx + pixel, 1.0F), r); + al = smooth_audio(tex, tex_sz, max(idx - pixel, 0.0F)), + am = smooth_audio(tex, tex_sz, idx), + ar = smooth_audio(tex, tex_sz, min(idx + pixel, 1.0F)); return (al + am + ar) / 3.0F; } diff --git a/shaders/util/smooth_pass.frag b/shaders/util/smooth_pass.frag new file mode 100644 index 0000000..b55d0f6 --- /dev/null +++ b/shaders/util/smooth_pass.frag @@ -0,0 +1,16 @@ + +uniform sampler1D tex; +uniform int sz; +uniform int w; + +out vec4 fragment; +in vec4 gl_FragCoord; + +#undef PRE_SMOOTHED_AUDIO +#define PRE_SMOOTHED_AUDIO 0 + +#include ":util/smooth.glsl" + +void main() { + fragment = vec4(smooth_audio(tex, sz, gl_FragCoord.x / w), 0, 0, 0); +}