Added smooth pass option, improved texture lookup performance, fixes #10 (and fixed issues with config copying)

This commit is contained in:
Jarcode
2018-02-03 11:08:28 -08:00
parent dbcecc9def
commit 522d022b70
11 changed files with 240 additions and 99 deletions

View File

@@ -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);

274
render.c
View File

@@ -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 = &current->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;
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
}