Hello community, here is the log from the commit of package glava for openSUSE:Factory checked in at 2019-03-12 09:54:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/glava (Old) and /work/SRC/openSUSE:Factory/.glava.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "glava" Tue Mar 12 09:54:39 2019 rev:3 rq:683773 version:1.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/glava/glava.changes 2019-02-25 17:55:49.978413153 +0100 +++ /work/SRC/openSUSE:Factory/.glava.new.28833/glava.changes 2019-03-12 09:54:39.907523105 +0100 @@ -1,0 +2,13 @@ +Mon Mar 11 11:47:52 UTC 2019 - [email protected] + +- Update to 1.6.0: + * FIFO support has been added. See README.md for details, also see #78 + * Added sampling modes for the GLSL smoothing pass to allow for more accurate output + * Added support for disabling shader passes through GLSL, see #97 + * Enhanced rendering and options for graph, courtesy of @arch1t3cht30[3], see #97 + * Fixed clickthrough on Openbox (and potentially other window managers), see #80 + * Fixed the two lowest-frequency bars having the same value while running the bars module + * Fixed some build issues on Ubuntu, musl, see #94, #91 + * Fixed a bug with circle that only manifested on some integrated graphics chips, see #70 + +------------------------------------------------------------------- Old: ---- v1.5.8.tar.gz New: ---- v1.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ glava.spec ++++++ --- /var/tmp/diff_new_pack.XJeXcd/_old 2019-03-12 09:54:40.391523009 +0100 +++ /var/tmp/diff_new_pack.XJeXcd/_new 2019-03-12 09:54:40.395523008 +0100 @@ -17,7 +17,7 @@ Name: glava -Version: 1.5.8 +Version: 1.6.0 Release: 0 Summary: OpenGL audio spectrum visualizer License: GPL-3.0-only ++++++ v1.5.8.tar.gz -> v1.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/.gitignore new/glava-1.6.0/.gitignore --- old/glava-1.5.8/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/glava-1.6.0/.gitignore 2019-03-10 01:55:03.000000000 +0100 @@ -0,0 +1,3 @@ +*.o +build_state +glava diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/Makefile new/glava-1.6.0/Makefile --- old/glava-1.5.8/Makefile 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/Makefile 2019-03-10 01:55:03.000000000 +0100 @@ -1,3 +1,5 @@ +SHELL := /bin/bash + src = $(wildcard *.c) obj = $(src:.c=.o) @@ -39,6 +41,9 @@ ifndef SHADERDIR ifdef XDG_CONFIG_DIRS SHADERDIR = /$(firstword $(subst :, ,$(XDG_CONFIG_DIRS)))/glava/ + ifeq ($(wildcard $(SHADERDIR)/..),) + SHADERDIR = /etc/xdg/glava/ + endif else SHADERDIR = /etc/xdg/glava/ endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/README.md new/glava-1.6.0/README.md --- old/glava-1.5.8/README.md 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/README.md 2019-03-10 01:55:03.000000000 +0100 @@ -3,7 +3,7 @@ **GLava** is an OpenGL audio spectrum visualizer. Its primary use case is for desktop windows or backgrounds. Displayed to the left is the `radial` shader module, and [here is a demonstration video](https://streamable.com/dgpj8). Development is active, and reporting issues is encouranged. -**Compiling** (Or use the Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or the [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/))**:** +**Compiling:** ```bash $ git clone https://github.com/wacossusca34/glava @@ -31,9 +31,16 @@ **Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava: ```bash -sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libxext6 libxext-dev libxcomposite-dev python make gcc +sudo apt-get install libpulse0 libpulse-dev libxext6 libxext-dev libxrender-dev libxcomposite-dev make gcc ``` +## Installation +Some distributions have a package for `glava`. If your distribution is not listed please use the compilation instructions above. + +- Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/) +- NixOS [package](https://github.com/NixOS/nixpkgs/blob/release-18.09/pkgs/applications/misc/glava/default.nix) +- openSUSE [package](https://build.opensuse.org/package/show/X11:Utilities/glava) + ## [Configuration](https://github.com/wacossusca34/glava/wiki) GLava will start by looking for an entry point in the user configuration folder (`~/.config/glava/rc.glsl`\*), and will fall back to loading from the shader installation folder (`/etc/xdg/glava`\*). The entry point will specify a module to load and should set global configuration variables. Configuration for specific modules can be done in their respective `.glsl` files, which the module itself will include. @@ -68,6 +75,21 @@ Note that some WMs listed without issues have specific overrides when using the `--desktop` flag. See `shaders/env_*.glsl` files for details. +## Reading from MPD's FIFO output + +Add the following to your `~/.config/mpd.conf`: + +``` +audio_output { + type "fifo" + name "glava_fifo" + path "/tmp/mpd.fifo" + format "22050:16:2" +} +``` + +Note the `22050` sample rate -- this is the reccommended setting for GLava. Restart MPD (if nessecary) and start GLava with `glava --audio=fifo`. + ## Licensing GLava is licensed under the terms of the GPLv3, with the exemption of `khrplatform.h`, which is licensed under the terms in its header. GLava includes some (heavily modified) source code that originated from [cava](https://github.com/karlstav/cava), which was initially provided under the MIT license. The source files that originated from cava are the following: @@ -86,7 +108,3 @@ The below copyright applies for the modifications to the files listed above, and the remaining sources in the repository: `Copyright (c) 2017 Levi Webb` - -## Porting - -GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken most of the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/fifo.c new/glava-1.6.0/fifo.c --- old/glava-1.5.8/fifo.c 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/fifo.c 2019-03-10 01:55:03.000000000 +0100 @@ -7,74 +7,123 @@ #include <fcntl.h> #include <math.h> #include <time.h> +#include <string.h> +#include <errno.h> +#include <poll.h> #include "fifo.h" -void* input_fifo(void* data) { - struct audio_data* audio = (struct audio_data *) data; - int fd; - int n = 0; - signed char buf[1024]; - int tempr, templ, lo; - int q, i; - int t = 0; - int size = 1024; - int bytes = 0; - int flags; - struct timespec req = { .tv_sec = 0, .tv_nsec = 10000000 }; - - fd = open(audio->source, O_RDONLY); - flags = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); +/* Implementation struct storage */ + +typeof(*audio_impls) audio_impls[sizeof(audio_impls) / sizeof(struct audio_impl*)] = {}; +size_t audio_impls_idx = 0; + +/* FIFO backend */ + +static void init(struct audio_data* audio) { + if (!audio->source) { + audio->source = strdup("/tmp/mpd.fifo"); + } +} + +static void* entry(void* data) { + struct audio_data* audio = (struct audio_data *) data; - while (1) { - bytes = read(fd, buf, sizeof(buf)); + float* bl = (float*) audio->audio_out_l; + float* br = (float*) audio->audio_out_r; + size_t fsz = audio->audio_buf_sz; + size_t ssz = audio->sample_sz; + + int fd; + int16_t buf[ssz / 2]; + size_t q; + int timeout = 50; + + struct timespec tv_last = {}, tv; + bool measured = false; + + if ((fd = open(audio->source, O_RDONLY)) == -1) { + fprintf(stderr, "failed to open FIFO audio source \"%s\": %s\n", audio->source, strerror(errno)); + exit(EXIT_FAILURE); + } + + struct pollfd pfd = { + .fd = fd, + .events = POLLIN + }; + + size_t buffer_offset = (fsz - (ssz / 4)); + + while (true) { + + /* The poll timeout is set to accommodate an approximate UPS, but has little purpose except + for effectively setting the rate of empty samples in the event of the FIFO descriptor + blocking for long periods of time. */ - if (bytes == -1) { /* if no bytes read, sleep 10ms and zero shared buffer */ - nanosleep (&req, NULL); - t++; - if (t > 10) { - for (i = 0; i < 2048; i++)audio->audio_out_l[i] = 0; - for (i = 0; i < 2048; i++)audio->audio_out_r[i] = 0; - t = 0; - } - } else { /* if bytes read, go ahead */ - t = 0; - for (q = 0; q < (size / 4); q++) { - - tempr = (buf[4 * q + 3] << 2); - - lo = (buf[4 * q + 2] >> 6); - if (lo < 0) lo = abs(lo) + 1; - if (tempr >= 0) tempr = tempr + lo; - else tempr = tempr - lo; - - templ = (buf[4 * q + 1] << 2); - - lo = (buf[4 * q] >> 6); - if (lo < 0) lo = abs(lo) + 1; - if (templ >= 0) templ = templ + lo; - else templ = templ - lo; - - if (audio->channels == 1) audio->audio_out_l[n] = (tempr + templ) / 2; - - /* stereo storing channels in buffer */ - if (audio->channels == 2) { - audio->audio_out_l[n] = templ; - audio->audio_out_r[n] = tempr; - } + switch (poll(&pfd, 1, timeout)) { + case -1: + fprintf(stderr, "FIFO backend: poll() failed (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + case 0: + pthread_mutex_lock(&audio->mutex); + + memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float)); + memmove(br, &br[ssz / 4], buffer_offset * sizeof(float)); + + for (q = 0; q < (ssz / 4); ++q) bl[buffer_offset + q] = 0; + for (q = 0; q < (ssz / 4); ++q) br[buffer_offset + q] = 0; + + audio->modified = true; + + pthread_mutex_unlock(&audio->mutex); + break; + default: { + read(fd, buf, sizeof(buf)); + clock_gettime(CLOCK_REALTIME, measured ? &tv : &tv_last); + if (measured) { + /* Set the timeout slightly higher than the delay between samples to prevent empty writes */ + timeout = (((tv.tv_sec - tv_last.tv_sec) * 1000) + ((tv.tv_nsec - tv_last.tv_nsec) / 1000000)) + 1; + tv_last = tv; + } else measured = true; + + pthread_mutex_lock(&audio->mutex); + + memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float)); + memmove(br, &br[ssz / 4], buffer_offset * sizeof(float)); + + for (size_t n = 0, q = 0; q < (ssz / 2); q += 2) { + + size_t idx = (fsz - (ssz / 4)) + n; + + if (audio->channels == 1) { + float sample = ((buf[q] + buf[q + 1]) / 2) / (float) 65535; + bl[idx] = sample; + br[idx] = sample; + } + + if (audio->channels == 2) { + bl[idx] = buf[q] / (float) 65535; + br[idx] = buf[q + 1] / (float) 65535; + } - n++; - if (n == 2048 - 1)n = 0; - } - } + n++; + } + + audio->modified = true; + + pthread_mutex_unlock(&audio->mutex); + break; + } + } - if (audio->terminate == 1) { - close(fd); - break; - } + if (audio->terminate == 1) { + close(fd); + break; + } - } + } - return 0; + return 0; } + +AUDIO_ATTACH(fifo); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/fifo.h new/glava-1.6.0/fifo.h --- old/glava-1.5.8/fifo.h 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/fifo.h 2019-03-10 01:55:03.000000000 +0100 @@ -14,4 +14,26 @@ pthread_mutex_t mutex; }; -void* input_fifo(void* data); +struct audio_impl { + const char* name; + void (*init)(struct audio_data* data); + void* (*entry)(void* data); +}; + +#define AUDIO_FUNC(F) \ + .F = (typeof(((struct audio_impl*) NULL)->F)) &F + +extern struct audio_impl* audio_impls[4]; +extern size_t audio_impls_idx; + +static inline void register_audio_impl(struct audio_impl* impl) { audio_impls[audio_impls_idx++] = impl; } + +#define AUDIO_ATTACH(N) \ + static struct audio_impl N##_var = { \ + .name = #N, \ + AUDIO_FUNC(init), \ + AUDIO_FUNC(entry), \ + }; \ + void __attribute__((constructor)) _##N##_construct(void) { \ + register_audio_impl(&N##_var); \ + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/glava.c new/glava-1.6.0/glava.c --- old/glava-1.5.8/glava.c 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/glava.c 2019-03-10 01:55:03.000000000 +0100 @@ -69,6 +69,10 @@ #error "Unsupported target system" #endif +#ifndef ACCESSPERMS +#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */ +#endif + /* Copy installed shaders/configuration from the installed location (usually /etc/xdg). Modules (folders) will be linked instead of copied. */ @@ -177,6 +181,7 @@ "-b, --backend specifies a window creation backend to use. By default, the most\n" " appropriate backend will be used for the underlying windowing\n" " system.\n" + "-a, --audio=BACKEND specifies an audio input backend to use.\n" "-V, --version print application version and exit\n" "\n" "The REQUEST argument is evaluated identically to the \'#request\' preprocessor directive\n" @@ -188,13 +193,17 @@ "The FILE argument may be any file path. All specified file paths are relative to the\n" "active configuration root (usually ~/.config/glava if present).\n" "\n" + "The BACKEND argument may be any of the following strings (for this particular build):\n" + "%s" + "\n" GLAVA_VERSION_STRING "\n"; -static const char* opt_str = "dhvVe:Cm:b:r:"; +static const char* opt_str = "dhvVe:Cm:b:r:a:"; static struct option p_opts[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"desktop", no_argument, 0, 'd'}, + {"audio", required_argument, 0, 'a'}, {"request", required_argument, 0, 'r'}, {"entry", required_argument, 0, 'e'}, {"force-mod", required_argument, 0, 'm'}, @@ -222,11 +231,12 @@ /* Evaluate these macros only once, since they allocate */ const char - * install_path = SHADER_INSTALL_PATH, - * user_path = SHADER_USER_PATH, - * entry = "rc.glsl", - * force = NULL, - * backend = NULL; + * install_path = SHADER_INSTALL_PATH, + * user_path = SHADER_USER_PATH, + * entry = "rc.glsl", + * force = NULL, + * backend = NULL, + * audio_impl_name = "pulseaudio"; const char* system_shader_paths[] = { user_path, install_path, NULL }; char** requests = malloc(1); @@ -241,19 +251,26 @@ case 'C': copy_mode = true; break; case 'd': desktop = true; break; case 'r': append_buf(requests, &requests_sz, optarg); break; - case 'e': entry = optarg; break; - case 'm': force = optarg; break; - case 'b': backend = optarg; break; + case 'e': entry = optarg; break; + case 'm': force = optarg; break; + case 'b': backend = optarg; break; + case 'a': audio_impl_name = optarg; break; case '?': exit(EXIT_FAILURE); break; case 'V': puts(GLAVA_VERSION_STRING); exit(EXIT_SUCCESS); break; default: - case 'h': - printf(help_str, argc > 0 ? argv[0] : "glava"); + case 'h': { + char buf[2048]; + size_t bsz = 0; + for (size_t t = 0; t < audio_impls_idx; ++t) + bsz += snprintf(buf + bsz, sizeof(buf) - bsz, "\t\"%s\"%s\n", audio_impls[t]->name, + !strcmp(audio_impls[t]->name, audio_impl_name) ? " (default)" : ""); + printf(help_str, argc > 0 ? argv[0] : "glava", buf); exit(EXIT_SUCCESS); break; + } } } @@ -306,13 +323,27 @@ .sample_sz = rd->samplesize_request, .modified = false }; - if (!audio.source) { - get_pulse_default_sink(&audio); - if (verbose) printf("Using default PulseAudio sink: %s\n", audio.source); + + struct audio_impl* impl = NULL; + + for (size_t t = 0; t < audio_impls_idx; ++t) { + if (!strcmp(audio_impls[t]->name, audio_impl_name)) { + impl = audio_impls[t]; + break; + } + } + + if (!impl) { + fprintf(stderr, "The specified audio backend (\"%s\") is not available.\n", audio_impl_name); + exit(EXIT_FAILURE); } + impl->init(&audio); + + if (verbose) printf("Using audio source: %s\n", audio.source); + pthread_t thread; - pthread_create(&thread, NULL, input_pulse, (void*) &audio); + pthread_create(&thread, NULL, impl->entry, (void*) &audio); float lb[rd->bufsize_request], rb[rd->bufsize_request]; while (rd->alive) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/glx_wcb.c new/glava-1.6.0/glx_wcb.c --- old/glava-1.5.8/glx_wcb.c 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/glx_wcb.c 2019-03-10 01:55:03.000000000 +0100 @@ -193,8 +193,8 @@ maximized = false; transparent = false; - void* hgl = dlopen("libGL.so", RTLD_LAZY); - void* hglx = dlopen("libGLX.so", RTLD_LAZY); + void* hgl = dlopen("libGL.so.1", RTLD_LAZY); + void* hglx = dlopen("libGLX.so.0", RTLD_LAZY); if (!hgl && !hglx) { fprintf(stderr, "Failed to load GLX functions (libGL and libGLX do not exist!)\n"); @@ -254,15 +254,37 @@ } } +static bool find_parent(Window w, Window* parent) { + Window root, *children = NULL; + unsigned int num_children; + + if(!XQueryTree(display, w, &root, parent, &children, &num_children)) + return false; + + if (children) + XFree(children); + + return *parent != None; +} + static void apply_clickthrough(struct glxwin* w) { if (w->clickthrough) { int ignored; if (XShapeQueryExtension(display, &ignored, &ignored)) { - Region region; - if ((region = XCreateRegion())) { - XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet); - XDestroyRegion(region); + Window root = DefaultRootWindow(display); + Window win = w->w; + while (win != None) { + Region region; + if ((region = XCreateRegion())) { + XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + } + Window parent; + find_parent(win, &parent); + win = (parent == root ? None : parent); } + } else { + fprintf(stderr, "Warning: XShape extension not available\n"); } } } @@ -278,6 +300,10 @@ w->should_close = true; } break; + case MapNotify: + apply_clickthrough(w); + XFlush(display); + break; case VisibilityNotify: switch (ev.xvisibility.state) { case VisibilityFullyObscured: @@ -514,7 +540,6 @@ static void set_visible(struct glxwin* w, bool visible) { if (visible) { XMapWindow(display, w->w); - apply_clickthrough(w); switch (w->override_state) { case '+': XRaiseWindow(display, w->w); break; case '-': XLowerWindow(display, w->w); break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/pulse_input.c new/glava-1.6.0/pulse_input.c --- old/glava-1.5.8/pulse_input.c 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/pulse_input.c 2019-03-10 01:55:03.000000000 +0100 @@ -54,7 +54,9 @@ } -void get_pulse_default_sink(struct audio_data* audio) { +static void init(struct audio_data* audio) { + + if (audio->source) return; pa_mainloop_api* mainloop_api; pa_context* pulseaudio_context; @@ -106,7 +108,7 @@ #error "Unsupported float format (requires 32 bit IEEE (little or big endian) floating point support)" #endif -void* input_pulse(void* data) { +static void* entry(void* data) { struct audio_data* audio = (struct audio_data*) data; int i, n; size_t ssz = audio->sample_sz; @@ -188,3 +190,5 @@ return 0; } + +AUDIO_ATTACH(pulseaudio); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/render.c new/glava-1.6.0/render.c --- old/glava-1.5.8/render.c 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/render.c 2019-03-10 01:55:03.000000000 +0100 @@ -91,7 +91,7 @@ struct gl_sfbo { GLuint fbo, tex, shader; - bool valid, nativeonly; + bool indirect, nativeonly; const char* name; struct gl_bind* binds; size_t binds_sz; @@ -139,6 +139,7 @@ struct request_handler* handlers, int shader_version, bool raw, + bool* skipped, struct gl_data* gl) { size_t s_len = strlen(shader); @@ -218,14 +219,33 @@ if (ret == GL_FALSE) { glGetShaderiv(s, GL_INFO_LOG_LENGTH, &ilen); if (ilen) { - GLchar ebuf[ilen]; + GLchar* ebuf = malloc(sizeof(GLchar) * ilen); glGetShaderInfoLog(s, ilen, NULL, ebuf); + + /* check for `#error __disablestage` and flag `*skipped` accordingly */ + if (skipped != NULL) { + bool last = false; + static const char* skip_keyword = "__disablestage"; + size_t sksz = sizeof(skip_keyword); + for(size_t t = 0; t < (size_t) ilen; ++t) { + if (ebuf[t] == '_') { + if (last && !strncmp(ebuf + t - 1, skip_keyword, sksz)) { + *skipped = true; + goto free_ebuf; + } else last = true; + } else last = false; + } + } + fprintf(stderr, "Shader compilation failed for '%s':\n", path); fwrite(ebuf, sizeof(GLchar), ilen - 1, stderr); #ifdef GLAVA_DEBUG fprintf(stderr, "Processed shader source for '%s':\n", path); fwrite(buf, sizeof(GLchar), sl, stderr); #endif + + free_ebuf: + free(ebuf); return 0; } else { fprintf(stderr, "Shader compilation failed for '%s', but no info was available\n", path); @@ -282,14 +302,16 @@ } /* load shaders */ -#define shaderbuild(gl, shader_path, c, d, r, v, ...) \ - shaderbuild_f(gl, shader_path, c, d, r, v, (const char*[]) {__VA_ARGS__, 0}) +#define shaderbuild(gl, shader_path, c, d, r, v, s, ...) \ + shaderbuild_f(gl, shader_path, c, d, r, v, s, (const char*[]) {__VA_ARGS__, 0}) static GLuint shaderbuild_f(struct gl_data* gl, const char* shader_path, const char* config, const char* defaults, struct request_handler* handlers, int shader_version, + bool* skipped, const char** arr) { + if (skipped) *skipped = false; const char* str; int i = 0, sz = 0, t; while ((str = arr[i++]) != NULL) ++sz; @@ -303,7 +325,7 @@ if (!strcmp(path + t + 1, "frag") || !strcmp(path + t + 1, "glsl")) { if (!(shaders[i] = shaderload(path, GL_FRAGMENT_SHADER, shader_path, config, defaults, handlers, - shader_version, false, gl))) { + shader_version, false, skipped, gl))) { return 0; } } else if (!strcmp(path + t + 1, "vert")) { @@ -323,7 +345,7 @@ } } /* load builtin vertex shader */ - shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, NULL, handlers, shader_version, true, gl); + shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, NULL, handlers, shader_version, true, NULL, gl); fflush(stdout); return shaderlink_f(shaders); } @@ -358,9 +380,9 @@ /* setup screen framebuffer object and its texture */ 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; }); - s->valid = true; + GLuint tex = s->indirect ? s->tex : ({ glGenTextures(1, &s->tex); s->tex; }); + GLuint fbo = s->indirect ? s->fbo : ({ glGenFramebuffers(1, &s->fbo); s->fbo; }); + s->indirect = true; /* bind texture and setup space */ glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -617,48 +639,48 @@ float wtemp, wr, wpr, wpi, wi, theta; float tempr, tempi; - // reverse-binary reindexing - n = nn<<1; - j=1; - for (i=1; i<n; i+=2) { - if (j>i) { + /* reverse-binary reindexing */ + n = nn << 1; + j = 1; + for (i = 1; i < n; i += 2) { + if (j > i) { swap(data[j-1], data[i-1]); swap(data[j], data[i]); } m = nn; - while (m>=2 && j>m) { + while (m >= 2 && j > m) { j -= m; m >>= 1; } j += m; }; - // here begins the Danielson-Lanczos section - mmax=2; - while (n>mmax) { - istep = mmax<<1; - theta = -(2*M_PI/mmax); - wtemp = sin(0.5*theta); - wpr = -2.0*wtemp*wtemp; + /* here begins the Danielson-Lanczos section */ + mmax = 2; + while (n > mmax) { + istep = mmax << 1; + theta = -(2 * M_PI / mmax); + wtemp = sin(0.5 * theta); + wpr = -2.0 * wtemp * wtemp; wpi = sin(theta); wr = 1.0; wi = 0.0; - for (m=1; m < mmax; m += 2) { - for (i=m; i <= n; i += istep) { - j=i+mmax; - tempr = wr*data[j-1] - wi*data[j]; - tempi = wr * data[j] + wi*data[j-1]; + for (m = 1; m < mmax; m += 2) { + for (i = m; i <= n; i += istep) { + j= i + mmax; + tempr = wr * data[j-1] - wi * data[j]; + tempi = wr * data[j] + wi * data[j-1]; data[j-1] = data[i-1] - tempr; - data[j] = data[i] - tempi; + data[j] = data[i] - tempi; data[i-1] += tempr; - data[i] += tempi; + data[i] += tempi; } - wtemp=wr; - wr += wr*wpr - wi*wpi; - wi += wi*wpr + wtemp*wpi; + wtemp = wr; + wr += wr * wpr - wi * wpi; + wi += wi * wpr + wtemp * wpi; } - mmax=istep; + mmax = istep; } /* abs and log scale */ @@ -1264,37 +1286,40 @@ *s = (struct gl_sfbo) { .name = strdup(d->d_name), .shader = 0, - .valid = false, + .indirect = false, .nativeonly = false, .binds = malloc(1), .binds_sz = 0 }; current = s; - GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, d->d_name); - if (!id) { + bool skip; + GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, &skip, d->d_name); + if (skip && verbose) printf("disabled: '%s'\n", d->d_name); + if (!id && !skip) abort(); - } s->shader = id; - /* Only setup a framebuffer and texture if this isn't the final step, - as it can rendered directly */ - if (idx != count) { - int w, h; - gl->wcb->get_fbsize(gl->w, &w, &h); - setup_sfbo(&stages[idx - 1], w, h); - } + if (id) { + /* Only setup a framebuffer and texture if this isn't the final step, + as it can rendered directly */ + if (idx != count) { + int w, h; + gl->wcb->get_fbsize(gl->w, &w, &h); + setup_sfbo(&stages[idx - 1], w, h); + } - glUseProgram(id); + glUseProgram(id); - /* Setup uniform bindings */ - size_t b; - for (b = 0; b < s->binds_sz; ++b) { - s->binds[b].uniform = glGetUniformLocation(id, s->binds[b].name); + /* Setup uniform bindings */ + size_t b; + for (b = 0; b < s->binds_sz; ++b) { + s->binds[b].uniform = glGetUniformLocation(id, s->binds[b].name); + } + glBindFragDataLocation(id, 1, "fragment"); + glUseProgram(0); } - glBindFragDataLocation(id, 1, "fragment"); - glUseProgram(0); found = true; } @@ -1311,16 +1336,13 @@ { struct gl_sfbo* final = NULL; - if (!gl->premultiply_alpha) { - for (size_t t = 0; t < gl->stages_sz; ++t) { - if (!gl->stages[t].nativeonly) { - final = &gl->stages[t]; - } + for (size_t t = 0; t < gl->stages_sz; ++t) { + if (gl->stages[t].shader && (gl->premultiply_alpha || !gl->stages[t].nativeonly)) { + final = &gl->stages[t]; } } - /* Invalidate framebuffer and use direct rendering if it was instantiated - due to a following `nativeonly` shader pass. */ - if (final) final->valid = false; + /* Use dirct rendering on final pass */ + if (final) final->indirect = false; } /* Compile smooth pass shader */ @@ -1332,7 +1354,7 @@ char util[usz]; /* module pack path to use */ snprintf(util, usz, "%s/%s", data, util_folder); loading_smooth_pass = true; - if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, "smooth_pass.frag"))) + if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, NULL, "smooth_pass.frag"))) abort(); loading_smooth_pass = false; } @@ -1454,7 +1476,7 @@ /* Resize screen textures if needed */ if (ww != gl->lww || wh != gl->lwh) { for (t = 0; t < gl->stages_sz; ++t) { - if (gl->stages[t].valid) { + if (gl->stages[t].indirect) { setup_sfbo(&gl->stages[t], ww, wh); } } @@ -1483,16 +1505,16 @@ /* Current shader program */ struct gl_sfbo* current = &gl->stages[t]; - if (current->nativeonly && !gl->premultiply_alpha) + if (!current->shader || (current->nativeonly && !gl->premultiply_alpha)) continue; /* Bind framebuffer if this is not the final pass */ - if (current->valid) + if (current->indirect) glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); glClear(GL_COLOR_BUFFER_BIT); - if (!current->valid && gl->copy_desktop) { + if (!current->indirect && gl->copy_desktop) { /* Self-contained code for drawing background image */ static GLuint bg_prog, bg_utex, bg_screen; static bool setup = false; @@ -1509,9 +1531,9 @@ "}" "\n"; if (!setup) { bg_prog = shaderlink(shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, - NULL, NULL, NULL, 330, true, gl), + NULL, NULL, NULL, 330, true, NULL, gl), shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader, - NULL, NULL, NULL, 330, true, gl)); + NULL, NULL, NULL, 330, true, NULL, gl)); bg_utex = glGetUniformLocation(bg_prog, "tex"); bg_screen = glGetUniformLocation(bg_prog, "screen"); glBindFragDataLocation(bg_prog, 1, "fragment"); @@ -1629,7 +1651,7 @@ /* Return state */ glUseProgram(current->shader); - if (current->valid) + if (current->indirect) glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); else glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -1668,7 +1690,7 @@ drawoverlay(&gl->overlay); /* Fullscreen quad (actually just two triangles) */ /* Reset some state */ - if (current->valid) + if (current->indirect) glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/bars/1.frag new/glava-1.6.0/shaders/bars/1.frag --- old/glava-1.5.8/shaders/bars/1.frag 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/bars/1.frag 2019-03-10 01:55:03.000000000 +0100 @@ -49,13 +49,15 @@ #else float d = AREA_HEIGHT - AREA_Y; #endif - float nbars = floor((AREA_WIDTH * 0.5F) / float(BAR_WIDTH + BAR_GAP)) * 2; float section = BAR_WIDTH + BAR_GAP; /* size of section for each bar (including gap) */ float center = section / 2.0F; /* half section, distance to center */ float m = abs(mod(dx, section)); /* position in section */ float md = m - center; /* position in section from center line */ + float nbars = floor((AREA_WIDTH * 0.5F) / section) * 2; + float p, s; if (md < ceil(float(BAR_WIDTH) / 2) && md >= -floor(float(BAR_WIDTH) / 2)) { /* if not in gap */ - float p = int(dx / section) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */ + s = dx / section; + p = (sign(s) == 1.0 ? ceil(s) : floor(s)) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */ p += sign(p) * ((0.5F + center) / AREA_WIDTH); /* index center of bar position */ /* Apply smooth function and index texture */ #define smooth_f(tex, p) smooth_audio(tex, audio_sz, p) @@ -66,7 +68,7 @@ return; } /* handle user options and store result of indexing in 'v' */ - if (p >= 0.0F) { + if (p > 0.0F) { #if DIRECTION == 1 p = 1.0F - p; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/circle/1.frag new/glava-1.6.0/shaders/circle/1.frag --- old/glava-1.5.8/shaders/circle/1.frag 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/circle/1.frag 2019-03-10 01:55:03.000000000 +0100 @@ -32,6 +32,8 @@ /* This shader is based on radial.glsl, refer to it for more commentary */ float apply_smooth(float theta) { + + fragment = vec4(0, 0, 0, 0); float idx = theta + ROTATE; float dir = mod(abs(idx), TWOPI); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/graph/1.frag new/glava-1.6.0/shaders/graph/1.frag --- old/glava-1.5.8/shaders/graph/1.frag 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/graph/1.frag 2019-03-10 01:55:03.000000000 +0100 @@ -81,16 +81,31 @@ #define TWOPI 6.28318530718 float half_w; +float middle; +highp float pixel = 1.0F / float(screen.x); -void render_side(in sampler1D tex, float idx) { - highp float pixel = 1.0F / float(screen.x); +float get_line_height(in sampler1D tex, float idx) { 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 */ - s *= clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F); + + float fact = clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F); + #if JOIN_CHANNELS > 0 + fact = -2 * pow(fact, 3) + 3 * pow(fact, 2); /* To avoid spikes */ + s = fact * s + (1 - fact) * middle; + #else + s *= fact; + #endif + s *= clamp((min(gl_FragCoord.x, screen.x - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F); + return s; +} + +void render_side(in sampler1D tex, float idx) { + float s = get_line_height(tex, idx); + /* and finally set fragment color if we are in range */ #if INVERT > 0 float pos = float(screen.y) - gl_FragCoord.y; @@ -106,6 +121,9 @@ void main() { half_w = (screen.x / 2); + + middle = VSCALE * (smooth_audio_adj(audio_l, audio_sz, 1, pixel) + smooth_audio_adj(audio_r, audio_sz, 0, pixel)) / 2; + if (gl_FragCoord.x < half_w) { render_side(audio_l, LEFT_IDX); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/graph/2.frag new/glava-1.6.0/shaders/graph/2.frag --- old/glava-1.5.8/shaders/graph/2.frag 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/graph/2.frag 2019-03-10 01:55:03.000000000 +0100 @@ -9,6 +9,10 @@ #include "@graph.glsl" #include ":graph.glsl" +#if DRAW_OUTLINE == 0 && DRAW_HIGHLIGHT == 0 +#error __disablestage +#endif + void main() { fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/graph/3.frag new/glava-1.6.0/shaders/graph/3.frag --- old/glava-1.5.8/shaders/graph/3.frag 1970-01-01 01:00:00.000000000 +0100 +++ new/glava-1.6.0/shaders/graph/3.frag 2019-03-10 01:55:03.000000000 +0100 @@ -0,0 +1,104 @@ + +in vec4 gl_FragCoord; + +#request uniform "screen" screen +uniform ivec2 screen; /* screen dimensions */ + +#request uniform "prev" tex +uniform sampler2D tex; /* screen texture */ + +out vec4 fragment; /* output */ + +#include "@graph.glsl" +#include ":graph.glsl" + +#if ANTI_ALIAS == 0 +#error __disablestage +#endif + +/* Moves toward the border of the graph, gives the + y coordinate of the last colored pixel */ +float get_col_height_up(float x, float oy) { + float y = oy; + #if INVERT > 0 + while (y >= 0) { + #else + while (y < screen.y) { + #endif + vec4 f = texelFetch(tex, ivec2(x, y), 0); + if (f.a <= 0) { + #if INVERT > 0 + y += 1; + #else + y -= 1; + #endif + break; + } + #if INVERT > 0 + y -= 1; + #else + y += 1; + #endif + } + + return y; +} + +/* Moves toward the base of the graph, gives the + y coordinate of the first colored pixel */ +float get_col_height_down(float x, float oy) { + float y = oy; + #if INVERT > 0 + while (y < screen.y) { + #else + while (y >= 0) { + #endif + vec4 f = texelFetch(tex, ivec2(x, y), 0); + if (f.a > 0) { + break; + } + #if INVERT > 0 + y += 1; + #else + y -= 1; + #endif + } + + return y; +} + +void main() { + fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0); + + #if ANTI_ALIAS > 0 + + if (fragment.a <= 0) { + bool left_done = false; + float h2; + float a_fact = 0; + + if (texelFetch(tex, ivec2(gl_FragCoord.x - 1, gl_FragCoord.y), 0).a > 0) { + float h1 = get_col_height_up(gl_FragCoord.x - 1, gl_FragCoord.y); + h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y); + fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0); + + a_fact = clamp(abs((h1 - gl_FragCoord.y) / (h2 - h1)), 0.0, 1.0); + + left_done = true; + } + if (texelFetch(tex, ivec2(gl_FragCoord.x + 1, gl_FragCoord.y), 0).a > 0) { + if (!left_done) { + h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y); + fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0); + } + float h3 = get_col_height_up(gl_FragCoord.x + 1, gl_FragCoord.y); + + a_fact = max(a_fact, clamp(abs((h3 - gl_FragCoord.y) / (h2 - h3)), 0.0, 1.0)); + } + + fragment.a *= a_fact; + + } + + #endif +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/graph.glsl new/glava-1.6.0/shaders/graph.glsl --- old/glava-1.5.8/shaders/graph.glsl 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/graph.glsl 2019-03-10 01:55:03.000000000 +0100 @@ -13,8 +13,14 @@ #define DRAW_OUTLINE 0 /* 1 to draw edge highlight, 0 to disable */ #define DRAW_HIGHLIGHT 1 +/* Whether to anti-alias the border of the graph, creating a smoother curve. + This may have a small impact on performance. + Note: requires `xroot` or `none` opacity to be set */ +#define ANTI_ALIAS 0 /* outline color */ #define OUTLINE #262626 +/* 1 to join the two channels together in the middle, 0 to clamp both down to zero */ +#define JOIN_CHANNELS 0 /* 1 to invert (vertically), 0 otherwise */ #define INVERT 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/rc.glsl new/glava-1.6.0/shaders/rc.glsl --- old/glava-1.5.8/shaders/rc.glsl 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/rc.glsl 2019-03-10 01:55:03.000000000 +0100 @@ -98,9 +98,14 @@ default when GLava itself is a desktop window. */ #request setclickthrough false -/* PulseAudio source. Can be a number or a name of an audio - sink or device to record from. Set to "auto" to use the - default output device. */ +/* Audio source + + When the "pulseaudio" backend is set, this can be a number or + a name of an audio sink or device to record from. Set to "auto" + to use the default output device. + + When the "fifo" backend is set, "auto" is interpreted as + "/tmp/mpd.fifo". Otherwise, a valid path should be provided. */ #request setsource "auto" /* Buffer swap interval (vsync), set to '0' to prevent @@ -189,7 +194,11 @@ Lower sample rates also can make output more choppy, when not using interpolation. It's generally OK to leave this - value unless you have a strange PulseAudio configuration. */ + value unless you have a strange PulseAudio configuration. + + This option does nothing when using the "fifo" audio + backend. Instead, an ideal rate should be be configured + in the application generating the output. */ #request setsamplerate 22050 /* ** DEPRECATED ** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/smooth_parameters.glsl new/glava-1.6.0/shaders/smooth_parameters.glsl --- old/glava-1.5.8/shaders/smooth_parameters.glsl 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/smooth_parameters.glsl 2019-03-10 01:55:03.000000000 +0100 @@ -13,10 +13,25 @@ - circular heavily rounded points - sinusoidal rounded at both low and high weighted values like a sine wave - - linear not rounded at all, just use linear distance + - linear not rounded at all; linear distance */ #define ROUND_FORMULA sinusoidal +/* The sampling mode for processing raw FFT input: + + - average averages all the inputs in the sample range for + a given point. Produces smooth output, but peaks + are not well represented + - maximum obtains the best value from the closest peak in + the sample range. Very accurate peaks, but + output is jagged and sporadic. + - hybrid uses the results from both `average` and `maximum` + with the weight provided in `SAMPLE_HYBRID_WEIGHT` */ +#define SAMPLE_MODE average +/* Weight should be provided in the range (0, 1). Higher values favour + averaged results. `hybrid` mode only. */ +#define SAMPLE_HYBRID_WEIGHT 0.65 + /* Factor used to scale frequencies. Lower values allows lower frequencies to occupy more space. */ #define SAMPLE_SCALE 8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glava-1.5.8/shaders/util/smooth.glsl new/glava-1.6.0/shaders/util/smooth.glsl --- old/glava-1.5.8/shaders/util/smooth.glsl 2018-11-03 00:27:54.000000000 +0100 +++ new/glava-1.6.0/shaders/util/smooth.glsl 2019-03-10 01:55:03.000000000 +0100 @@ -21,6 +21,10 @@ /* take value x that scales linearly between [0, 1) and return its circlar curve */ #define circular(x) sqrt(1 - (((x) - 1) * ((x) - 1))) +#define average 0 +#define maximum 1 +#define hybrid 2 + float scale_audio(float idx) { return -log((-(SAMPLE_RANGE) * idx) + 1) / (SAMPLE_SCALE); } @@ -32,20 +36,42 @@ /* 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 - 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); + smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz, + smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz; + float m = ((smax - smin) / 2.0F), s, w; float rm = smin + m; /* middle */ + #if SAMPLE_MODE == average + float avg = 0, weight = 0; for (s = smin; s <= smax; s += 1.0F) { - float w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1)); + w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1)); weight += w; avg += texelFetch(tex, int(round(s)), 0).r * w; } avg /= weight; return avg; + #elif SAMPLE_MODE == hybrid + float vmax = 0, avg = 0, weight = 0, v; + for (s = smin; s < smax; s += 1.0F) { + w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1)); + weight += w; + v = texelFetch(tex, int(round(s)), 0).r * w; + avg += v; + if (vmax < v) + vmax = v; + } + return (vmax * (1 - SAMPLE_HYBRID_WEIGHT)) + ((avg / weight) * SAMPLE_HYBRID_WEIGHT); + #elif SAMPLE_MODE == maximum + float vmax = 0, v; + for (s = smin; s < smax; s += 1.0F) { + w = texelFetch(tex, int(round(s)), 0).r * ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1)); + if (vmax < w) + vmax = w; + } + return vmax; + #endif #else return texelFetch(tex, int(round(idx * tex_sz)), 0).r; #endif
