9 Commits

Author SHA1 Message Date
klemek a7bc58e94e refactor: store init params in main program
C-lang CI / build-no-video (push) Failing after 32s
C-lang CI / lint (push) Failing after 36s
C-lang CI / build-release (push) Has been skipped
2026-05-11 08:42:36 +02:00
klemek dfefe879c9 feat: video reconnect cli arg 2026-05-11 08:36:32 +02:00
klemek 7d99c617ef refactor: check every gl error 2026-05-11 08:30:21 +02:00
klemek cdc7df3e56 refactor: review format and imports 2026-05-11 07:56:26 +02:00
klemek 8f0da378b0 fix: forks should exit 2026-05-11 07:40:34 +02:00
klemek 7d03c9719e feat: video reconnect (wip egl error 3003) 2026-05-11 07:40:20 +02:00
klemek dd20515e2b style: format glsl files and fix inc_src 2026-05-11 07:34:47 +02:00
klemek 53c5639f92 docs: small linux dev/video help 2026-05-11 07:28:38 +02:00
klemek d85c5b47b6 tools: add more formatting config 2026-05-11 07:28:08 +02:00
59 changed files with 642 additions and 347 deletions
+4
View File
@@ -0,0 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/clang-format-21.x.json
---
BasedOnStyle: LLVM
IndentWidth: 2
+19
View File
@@ -0,0 +1,19 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{*.c,*.h}]
indent_size = 2
[{Makefile.*,Makefile}]
indent_style = tab
indent_size = 2
+4 -1
View File
@@ -25,6 +25,10 @@ build:
-o build/$(TARGET) \
-g -Og
.PHONY: format
format:
clang-format -i src/*
.PHONY: run
run: build
./build/$(TARGET) $(TEST_ARGS) --monitor-only --internal-size=480 --video-size=240 --hot-reload
@@ -101,4 +105,3 @@ release-arch: clean
mkdir -p build
cp PKGBUILD build
cd build && makepkg
+12 -4
View File
@@ -160,10 +160,10 @@ When running, the following hotkeys are available:
| Hotkey | Function |
| ------ | -------- |
| <kbd>Esc</kbd> | Exit FORGE |
| <kbd>Esc</kbd> | Exit FORGE |
| <kbd>R</kbd> | Randomize internal values |
| <kbd>Shift</kbd> + <kbd>R</kbd> | Reset internal values to 0 |
| <kbd>D</kbd> | Demo mode On/Off |
| <kbd>D</kbd> | Demo mode On/Off |
| <kbd>A</kbd> | Auto Random mode On/Off |
| <kbd>&larr;</kbd> / <kbd>&rarr;</kbd> | Auto Random Cycle -/+ 1 |
| <kbd>&uarr;</kbd> / <kbd>&darr;</kbd> | BPM +/- 1 |
@@ -175,14 +175,16 @@ These are configurable in the [`forge_project.cfg`](#forge_projectcfg).
### CLI arguments
```txt
usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-vi=FILE] [-vs=SIZE] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-tm] [-tf]
forge steel-dev
usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-vi=FILE] [-vs=SIZE] [-vr / -nvr] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-tm] [-tf]
Fusion Of Real-time Generative Effects.
options:
-h, --help show this help message and exit
-v, --version print version
-p, --project forge project directory (default: /usr/share/forge/default)
-p, --project forge project directory (default: ./default)
-c, --config config file name (default: forge_project.cfg)
-hr, --hot-reload hot reload of shaders scripts
-s, --screen output screen number (default: primary)
@@ -196,6 +198,8 @@ options:
-arc, --auto-random-cycle auto random cycle length (default: 4)
-vi, --video-in path to video capture device (multiple allowed)
-vs, --video-size video capture desired height (default: internal texture height)
-vr, --video-reconnect auto-reconnect video (default)
-nvr, --no-video-reconnect do not auto-reconnect video
-is, --internal-size internal texture height (default: 720)
-ls, --load-state load saved state (default)
-nls, --no-load-state do not load saved state
@@ -490,3 +494,7 @@ The code is available in the default project [default/inc_yuyv.glsl](./default/i
The sentences are defined in [default/inc_sentences.glsl](./default/inc_sentences.glsl).
The script [scripts/sentences.py](./scripts/sentences.py) can help you update them.
### My video device keeps changing ID each time it's unplugged
You should [bind your device to a fixed interface](https://linuxvox.com/blog/linux-bind-camera-to-fixed-devvideo/).
+1 -1
View File
@@ -27,4 +27,4 @@ void main() {
} else {
fragColor = texture(iTex0, vUV);
}
}
}
+35 -35
View File
@@ -22,7 +22,7 @@ uniform int iInputFPS2;
float s(vec2 uv, float x0, float y0) {
return step(x0, uv.x) * step(-x0 - 1, -uv.x) * step(y0, uv.y) *
step(-y0 - 1, -uv.y);
step(-y0 - 1, -uv.y);
}
const int texts[12][5] = {
@@ -49,16 +49,16 @@ void main() {
vec2 uv3 = uv1 * 60;
vec4 c = vec4(0);
c += s(uv2,1,2) * texture(iTex7, uv2);
c += s(uv2,2,2) * texture(iTex9, uv2);
c += s(uv2,1,0) * texture(iTex8, uv2);
c += s(uv2,2,0) * texture(iTex10, uv2);
c += s(uv2, 1, 2) * texture(iTex7, uv2);
c += s(uv2, 2, 2) * texture(iTex9, uv2);
c += s(uv2,0,1) * debug(mod(uv2, 1));
c += s(uv2,1,1) * texture(iTex11, uv2);
c += s(uv2,2,1) * texture(iTex0, uv2);
c += s(uv2, 1, 0) * texture(iTex8, uv2);
c += s(uv2, 2, 0) * texture(iTex10, uv2);
c += s(uv2, 0, 1) * debug(mod(uv2, 1));
c += s(uv2, 1, 1) * texture(iTex11, uv2);
c += s(uv2, 2, 1) * texture(iTex0, uv2);
float sel = 0;
@@ -74,65 +74,65 @@ void main() {
float t = 0;
f += rect(uv3, vec2(-35, 28.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(-37,28), texts[0]);
if (iInputResolution1.x > 0) {
c += s(uv2,0,2) * texture(iTex5, uv2);
t += write_5(uv3, vec2(-37, 28), texts[0]);
if (iInputFormat1 == YUYV_FOURCC) {
c += s(uv2, 0, 2) * texture(iTex5, uv2);
f += rect(uv3, vec2(-35, 26.75), vec2(2.8, 0.7));
t += write_int(uv3, vec2(-37.6,26.1), iInputFPS1, 2);
t += write_5(uv3, vec2(-35.1,26.1), texts[8]);
t += write_int(uv3, vec2(-37.6, 26.1), iInputFPS1, 2);
t += write_5(uv3, vec2(-35.1, 26.1), texts[8]);
} else {
f += rect(uv3, vec2(-35, 26.75), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-36.5,26.1), texts[9]);
t += write_5(uv3, vec2(-36.5, 26.1), texts[9]);
}
f += rect(uv3, vec2(-35, -11.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(-37,-12), texts[1]);
if (iInputResolution2.x > 0) {
c += s(uv2,0,0) * texture(iTex6, uv2);
f += rect(uv3, vec2(-35, -11.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(-37, -12), texts[1]);
if (iInputFormat2 == YUYV_FOURCC) {
c += s(uv2, 0, 0) * texture(iTex6, uv2);
f += rect(uv3, vec2(-35, -13.25), vec2(2.8, 0.7));
t += write_int(uv3, vec2(-37.6,-13.9), iInputFPS2, 2);
t += write_5(uv3, vec2(-35.1,-13.9), texts[8]);
t += write_int(uv3, vec2(-37.6, -13.9), iInputFPS2, 2);
t += write_5(uv3, vec2(-35.1, -13.9), texts[8]);
} else {
f += rect(uv3, vec2(-35, -13.25), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-36.5,-13.9), texts[9]);
t += write_5(uv3, vec2(-36.5, -13.9), texts[9]);
}
f += rect(uv3, vec2(-35, 8.5), vec2(2.1, 0.7));
if (iDemo > 0) {
t += write_5(uv3, vec2(-37,8), texts[10]);
t += write_5(uv3, vec2(-37, 8), texts[10]);
} else {
t += write_5(uv3, vec2(-37,8), texts[11]);
t += write_5(uv3, vec2(-37, 8), texts[11]);
}
f += rect(uv3, vec2(0, 28.5), vec2(2.6, 0.7));
f += iActive1 == 1 ? h_rect(uv3, vec2(0, 28.5), vec2(3, 1), 0.2) : 0;
t += write_5(uv3, vec2(-2.5,28), texts[2]);
t += write_5(uv3, vec2(-2.5, 28), texts[2]);
f += rect(uv3, vec2(0, -11.5), vec2(2.6, 0.7));
f += iActive1 == 2 ? h_rect(uv3, vec2(0, -11.5), vec2(3, 1), 0.2) : 0;
t += write_5(uv3, vec2(-2.5,-12), texts[3]);
t += write_5(uv3, vec2(-2.5, -12), texts[3]);
f += rect(uv3, vec2(35, 28.5), vec2(2.1, 0.7));
f += iActive2 == 1 ? h_rect(uv3, vec2(35, 28.5), vec2(2.5, 1), 0.2) : 0;
t += write_5(uv3, vec2(33,28), texts[4]);
t += write_5(uv3, vec2(33, 28), texts[4]);
f += rect(uv3, vec2(35, -11.5), vec2(2.1, 0.7));
f += iActive2 == 2 ? h_rect(uv3, vec2(35, -11.5), vec2(2.5, 1), 0.2) : 0;
t += write_5(uv3, vec2(33,-12), texts[5]);
t += write_5(uv3, vec2(33, -12), texts[5]);
f += rect(uv3, vec2(0, 8.5), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-1.5,8), texts[6]);
t += write_5(uv3, vec2(-1.5, 8), texts[6]);
f += rect(uv3, vec2(35, 8.5), vec2(1.6, 0.7));
f += iActive2 == 3 ? h_rect(uv3, vec2(35, 8.5), vec2(2, 1), 0.2) : 0;
t += write_5(uv3, vec2(33.5,8), texts[7]);
t += write_5(uv3, vec2(33.5, 8), texts[7]);
f += rect(uv3, vec2(35, 6.75), vec2(2.8, 0.7));
t += write_int(uv3, vec2(32.4,6.1), iFPS, 2);
t += write_5(uv3, vec2(34.9,6.1), texts[8]);
t += write_int(uv3, vec2(32.4, 6.1), iFPS, 2);
t += write_5(uv3, vec2(34.9, 6.1), texts[8]);
fragColor = mix(c, vec4(min(1, f) - t), min(1, f));
}
}
+1 -1
View File
@@ -27,4 +27,4 @@ void main() {
} else {
fragColor = texture(iTex0, vUV);
}
}
}
+1 -1
View File
@@ -17,4 +17,4 @@ uniform vec3 iMidi1_1[6];
void main() {
fragColor = src_stage(vUV, iSeed3, iMidi1_1[0], iMidi1_1[1].xy, iMidi1_1[2], iMidi1_1[3].xy, iMidi1_1[4], iMidi1_1[5].xy);
}
}
+1 -1
View File
@@ -16,4 +16,4 @@ uniform vec3 iMidi1_2[6];
void main() {
fragColor = src_stage(vUV, iSeed4, iMidi1_2[0], iMidi1_2[1].xy, iMidi1_2[2], iMidi1_2[3].xy, iMidi1_2[4], iMidi1_2[5].xy);
}
}
+1 -1
View File
@@ -18,4 +18,4 @@ uniform vec3 iMidi2_1[7];
void main() {
fragColor = fx_stage(vUV, iTex7, iTex9, iSeed5, iMidi2_1[0], iMidi2_1[1].xy, iMidi2_1[2], iMidi2_1[3].xy, iMidi2_1[4], iMidi2_1[5].xy, iMidi2_1[6]);
}
}
+1 -1
View File
@@ -18,4 +18,4 @@ uniform vec3 iMidi2_2[7];
void main() {
fragColor = fx_stage(vUV, iTex8, iTex10, iSeed6, iMidi2_2[0], iMidi2_2[1].xy, iMidi2_2[2], iMidi2_2[3].xy, iMidi2_2[4], iMidi2_2[5].xy, iMidi2_2[6]);
}
}
+2 -2
View File
@@ -30,8 +30,8 @@ void main() {
mix_value = mix(mix_value, mix_value * 0.9 + 0.05, iDemo);
if (mix_type) {
fragColor = mix(color_a, color_b, step(mix_value, k));
fragColor = mix(color_a, color_b, step(mix_value, k));
} else {
fragColor = mix(color_b, color_a, mix_value);
}
}
}
+1 -1
View File
@@ -10,4 +10,4 @@ uniform sampler2D iTex0;
void main() {
fragColor = texture(iTex0, vUV);
}
}
+5 -5
View File
@@ -94,7 +94,7 @@ const int cp437[] = {
0xE333, 0x1333, 0x0ECC, 0x0100, // 0x59, Y
0x813F, 0x1367, 0x0F6C, 0x0764, // 0x5A, Z
0x666E, 0x0001, 0x0E66, 0x0100, // 0x5B, [
0x8C63, 0x1000, 0x0000, 0x0463, // 0x5C,
0x8C63, 0x1000, 0x0000, 0x0463, // 0x5C,
0x888E, 0x1111, 0x0E88, 0x0111, // 0x5D, ]
0x36C8, 0x6310, 0x0000, 0x0000, // 0x5E, ^
0x0000, 0x0000, 0xF000, 0xF000, // 0x5F, _
@@ -392,7 +392,7 @@ int read(sampler2D tex, vec2 uv, float k, int d, float t)
vec2 tex_uv = floor(uv * k) * inv_k;
tex_uv += vec2(d % 2, floor(d * 0.5)) * 0.5 * inv_k;
return
((mean(reframe(tex, tex_uv + vec2(0, 3) * inv_k * 0.125)) > t) ? 1 : 0) +
((mean(reframe(tex, tex_uv + vec2(0, 3) * inv_k * 0.125)) > t) ? 1 : 0) +
((mean(reframe(tex, tex_uv + vec2(0, 2) * inv_k * 0.125)) > t) ? 2 : 0) +
((mean(reframe(tex, tex_uv + vec2(0, 1) * inv_k * 0.125)) > t) ? 4 : 0) +
((mean(reframe(tex, tex_uv + vec2(0, 0) * inv_k * 0.125)) > t) ? 8 : 0) +
@@ -411,7 +411,7 @@ int read(sampler2D tex, vec2 uv, float k, int d, float t)
}
// https://web.archive.org/web/20151229003112/http://blogs.msdn.com/b/jeuge/archive/2005/06/08/hakmem-bit-count.aspx
int bit_count(int u)
int bit_count(int u)
{
int c;
c = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
@@ -424,7 +424,7 @@ int guess_char(sampler2D tex, vec2 uv, float k, float t)
int b1 = read(tex, uv, k, 1, t);
int b2 = read(tex, uv, k, 2, t);
int b3 = read(tex, uv, k, 3, t);
int mc = 0;
int mb = 100;
int i;
@@ -444,4 +444,4 @@ int guess_char(sampler2D tex, vec2 uv, float k, float t)
return mc;
}
#endif
#endif
+3 -3
View File
@@ -126,7 +126,7 @@ vec4 debug(vec2 vUV)
vec2 uv3 = uv1 * 30;
// base frame
float f =
float f =
h_rect(uv2, vec2(-5, -2), vec2(1), 0.1) +
h_rect(uv2, vec2(-2, -2), vec2(1), 0.1) +
rect(uv2, vec2(-3.5, -2), vec2(0.5, 0.1)) +
@@ -143,7 +143,7 @@ vec4 debug(vec2 vUV)
rect(uv2, vec2(6.8, 0), vec2(0.75, 0.1)) +
h_circle(uv2, vec2(7.8, 0), 0.3, 0.1)
;
if (iDemo < 1 && iInputFormat1 == YUYV_FOURCC) {
f += circle(uv2, vec2(-9, 2), 0.3);
}
@@ -258,4 +258,4 @@ vec4 debug(vec2 vUV)
return vec4(f);
}
#endif
#endif
+5 -5
View File
@@ -188,10 +188,10 @@ vec2 v_pos(float i) {
float x0 = rand(i + 823 + iTimeId);
float y0 = rand(i + 328 + iTimeId);
float x1 = rand(i + 823 + iTimeId + 1);
float y1 = rand(i + 328 + iTimeId + 1);
return vec2(
mix(x0, x1, ease(ease(iTimeV))),
mix(y0, y1, ease(ease(iTimeV)))
@@ -257,13 +257,13 @@ float line(vec2 uv, vec2 p1, vec2 p2, float thick) {
vec2 p = p2 - p1;
uv -= p1;
vec2 k;
if (abs(p.y) > abs(p.x)) {
k = vec2(
uv.x - p.x * uv.y / p.y,
uv.y / p.y
);
return step(k.x, thick * 0.5)
* step(-k.x, thick * 0.5)
* step(k.y, 1)
@@ -273,7 +273,7 @@ float line(vec2 uv, vec2 p1, vec2 p2, float thick) {
uv.x / p.x,
uv.y - p.y * uv.x / p.x
);
return step(k.y, thick * 0.5)
* step(-k.y, thick * 0.5)
* step(k.x, 1)
+14 -14
View File
@@ -60,9 +60,9 @@ vec4 fx_shift(vec2 vUV, sampler2D src0, sampler2D src1, int seed, vec3 b1, vec2
float y_shift = magic(f3, b3, seed + 30);
// logic
vec3 c0 = texture(src0, uv0).xyz;
vec2 uv2 = uv1;
uv2 = mix(uv2 * (1 + zoom * 2), uv2 * (zoom), step(0.5, zoom));
uv2 += vec2(x_shift * ratio, y_shift) * 2;
@@ -127,7 +127,7 @@ subroutine(fx_stage_sub) vec4 fx_5(vec2 vUV, sampler2D previous, sampler2D feedb
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
float pixel = (1 - pixel_size) * 250 + 25;
uv2 = round(uv2 * pixel) / pixel;
@@ -199,9 +199,9 @@ subroutine(fx_stage_sub) vec4 fx_7(vec2 vUV, sampler2D previous, sampler2D feedb
float zoom = magic(f3, b3, seed + 30);
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
float k1 = lens_v * 0.5;
uv2 *= 1 + zoom * 2;
@@ -235,9 +235,9 @@ subroutine(fx_stage_sub) vec4 fx_8(vec2 vUV, sampler2D previous, sampler2D feedb
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
uv2 = mix(uv2, kal2(uv2 * rot(0.25), floor(axes * 9 + 1)) * vec2(1, -2) + vec2(0, -0.5), axes_trigger);
uv2 = mix(uv2, kal2(uv2 * rot(0.25), floor(axes * 9 + 1)) * vec2(1, -2) + vec2(0, -0.5), axes_trigger);
uv2 *= rot(rotation);
uv2.x = (saw(uv2.x / ratio + 0.5 + h_scroll * 2) - 0.5) * ratio;
vec3 c = reframe(previous, uv2).xyz;
@@ -262,12 +262,12 @@ subroutine(fx_stage_sub) vec4 fx_9(vec2 vUV, sampler2D previous, sampler2D feedb
vec2 charset = magic_f(f2, b2, seed + 20);
vec3 charset_ctrl = magic_b(b2, seed + 20);
float char_delta = magic(f3, b3, seed + 30);
float t = magic(seed + 40);
float t = magic(seed + 40);
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
float k1 = 100 * (1 - zoom) + 10;
float inv_k = 1 / k1;
@@ -306,7 +306,7 @@ subroutine(fx_stage_sub) vec4 fx_10(vec2 vUV, sampler2D previous, sampler2D feed
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
uv2 *= 1 + zoom * 2;
uv2 = lens(uv2, -lens_v2 * 10, lens_v1 * 10);
@@ -331,7 +331,7 @@ subroutine(fx_stage_sub) vec4 fx_11(vec2 vUV, sampler2D previous, sampler2D feed
float angle = magic(f3, b3, seed + 30);
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
@@ -363,7 +363,7 @@ subroutine(fx_stage_sub) vec4 fx_12(vec2 vUV, sampler2D previous, sampler2D feed
float fb = magic(f3, b3, seed + 30);
// logic
vec3 c0 = texture(previous, uv0).xyz;
vec2 uv2 = uv1;
@@ -452,7 +452,7 @@ subroutine(fx_stage_sub) vec4 fx_12(vec2 vUV, sampler2D previous, sampler2D feed
} else if (rule == 11) { // B4678/S35678 (Anneal)
alive = n >= 6 || !alive && n == 4 || alive && (n == 3 || n == 5);
}
vec3 cout = vec3(alive ? 1 : 0);
return fx_master(c0, cout, seed, m0);
@@ -559,4 +559,4 @@ subroutine(fx_stage_sub) vec4 fx_15(vec2 vUV, sampler2D previous, sampler2D feed
}
}
#endif
#endif
+1 -1
View File
@@ -74,4 +74,4 @@ float magic_reverse(float i)
return magic_reverse(vec2(0), vec3(0, 0, 1), i);
}
#endif
#endif
+1 -1
View File
@@ -11,4 +11,4 @@ float rand(vec2 n){
return rand(n.x * 1234 + n.y * 9876);
}
#endif
#endif
+1 -1
View File
@@ -4,4 +4,4 @@
uniform vec2 iResolution;
uniform vec2 iTexResolution;
#endif
#endif
+1 -1
View File
@@ -26,4 +26,4 @@ const int sentences[SENTENCE_COUNT][20] = {
const int lengths[SENTENCE_COUNT] = {
5, 5, 10, 6, 2, 9, 10, 7, 18, 14, 4, 9, 10, 1, 6, 11, 11
};
#endif
#endif
+55 -55
View File
@@ -8,8 +8,8 @@
uniform int iDemo;
uniform sampler2D iTex0;
uniform sampler2D iTex3;
uniform sampler2D iTex4;
uniform sampler2D iTex5;
uniform sampler2D iTex6;
subroutine vec4 src_stage_sub(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3);
@@ -17,7 +17,7 @@ vec4 src_thru(vec2 vUV, sampler2D tex, int seed, vec3 b1, vec2 f1, vec3 b2, vec2
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
// controls
@@ -36,21 +36,21 @@ vec4 src_thru(vec2 vUV, sampler2D tex, int seed, vec3 b1, vec2 f1, vec3 b2, vec2
// output
return vec4(c, 1.);
return vec4(c, 1.);
}
// SRC 1: feedback + thru
subroutine(src_stage_sub) vec4 src_1(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_1(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
return src_thru(vUV, iTex0, seed, b1, f1, b2, f2, b3, f3);
}
// SRC 2 : lines
subroutine(src_stage_sub) vec4 src_2(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_2(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -68,16 +68,16 @@ subroutine(src_stage_sub) vec4 src_2(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
float k = thickness * 2;
uv2.y = cmod(uv2.y, k * 2 + 0.1);
float f = istep(k * 0.125 + 0.05, uv2.y) * istep(k * 0.125 + 0.01, -uv2.y);
return vec4(f);
}
// SRC 3 : dots
subroutine(src_stage_sub) vec4 src_3(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_3(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -97,16 +97,16 @@ subroutine(src_stage_sub) vec4 src_3(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
float k = zoom * 0.1 + 0.05;
uv2 = cmod(uv2, k * 2);
float f = istep(k / (1 + length(uv1) * 2), length(uv2));
return vec4(f);
}
// SRC 4 : circuit
subroutine(src_stage_sub) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -199,12 +199,12 @@ subroutine(src_stage_sub) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
f += line(uv1, vec2(-t * 2), vec2(t * 2), t);
f += line(uv1, vec2(-t * 2, t * 2), vec2(t * 2, -t * 2), t);
} else if (k < 50) { // inductor
f += istep(t * 2, length(uv1 - vec2(t * 2.5,0)));
f += istep(t * 2, length(uv1 - vec2(0,0)));
f += istep(t * 2, length(uv1 - vec2(-t * 2.5,0)));
f -= 2 * istep(t, length(uv1 - vec2(t * 2.5,0)));
f -= 2 * istep(t, length(uv1 - vec2(0,0)));
f -= 2 * istep(t, length(uv1 - vec2(-t * 2.5,0)));
f += istep(t * 2, length(uv1 - vec2(t * 2.5, 0)));
f += istep(t * 2, length(uv1 - vec2(0, 0)));
f += istep(t * 2, length(uv1 - vec2(-t * 2.5, 0)));
f -= 2 * istep(t, length(uv1 - vec2(t * 2.5, 0)));
f -= 2 * istep(t, length(uv1 - vec2(0, 0)));
f -= 2 * istep(t, length(uv1 - vec2(-t * 2.5, 0)));
f *= step(-t * 0.5, uv1.y);
} else if (k < 60) { // switch
f += istep(t, length(uv1 - vec2(t * 2.5, 0)));
@@ -229,16 +229,16 @@ subroutine(src_stage_sub) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
f += line(uv1, vec2(t * 0.25, -t * 3.25), vec2(-t * 3, -t), t);
}
}
return vec4(f);
}
// SRC 5 : noise
subroutine(src_stage_sub) vec4 src_5(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_5(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -259,28 +259,28 @@ subroutine(src_stage_sub) vec4 src_5(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
f = sin(f * PI * (details * 20)) * 0.5 + 1;
int nf = int(noise_factor * 6);
f *= mix(1, noise_f(uv2, nf - 1), step(0.0, float(nf)));
return vec4(f);
}
// SRC 6 : video in 1 + thru
subroutine(src_stage_sub) vec4 src_6(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_6(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
if (iDemo > 0) {
return src_2(vUV, seed, b1, f1, b2, f2, b3, f3);
}
return src_thru(vUV, iTex3, seed, b1, f1, b2, f2, b3, f3);
return src_thru(vUV, iTex5, seed, b1, f1, b2, f2, b3, f3);
}
#include inc_cp437.glsl
// SRC 7 : cp437
subroutine(src_stage_sub) vec4 src_7(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_7(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -304,7 +304,7 @@ subroutine(src_stage_sub) vec4 src_7(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
int code = ((charset_ctrl.y < 1 || (uv2i.x % 2 ^ uv2i.y % 2) > 0) ? 1 : 0) * (start_char + int((rand(uv2i) + char_delta) * char_span) % char_span);
uv2 = mod(uv2, 1);
float f = char(uv2, code) ? 1 : 0;
return vec4(f);
}
@@ -312,11 +312,11 @@ subroutine(src_stage_sub) vec4 src_7(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
#include inc_sentences.glsl
subroutine(src_stage_sub) vec4 src_8(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_8(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -336,16 +336,16 @@ subroutine(src_stage_sub) vec4 src_8(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
uv2.x += floor(uv2.y) * (h_delta - 0.5) * 2;
uv2.y = mix(uv2.y, mod(uv2.y, 1), h_delta_b.x);
float f = write_20(uv2, vec2(-float(lengths[s]) * 0.5, 0), sentences[s]);
return vec4(f);
}
// SRC 9 : sentences repeat
subroutine(src_stage_sub) vec4 src_9(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_9(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -376,11 +376,11 @@ subroutine(src_stage_sub) vec4 src_9(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
}
// SRC 10 : isometric grid
subroutine(src_stage_sub) vec4 src_10(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_10(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -408,7 +408,7 @@ subroutine(src_stage_sub) vec4 src_10(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
vec2 u2 = mod(floor(uv3 + vec2(0, 1)), umax);
vec2 u3 = mod(floor(uv3 + vec2(-1, 0)), umax);
vec2 u4 = mod(floor(uv3 + vec2(0, -1)), umax);
float e0 = (rand(floor(u0)) * 2 - 1) * max_elevation;
float e1 = (rand(floor(u1)) * 2 - 1) * max_elevation;
float e2 = (rand(floor(u2)) * 2 - 1) * max_elevation;
@@ -420,29 +420,29 @@ subroutine(src_stage_sub) vec4 src_10(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
float f = 0;
f = line(uv3, vec2(0, 0) - iso_z(e0), vec2(1, 0) - iso_z(e1), thick)
+ line(uv3, vec2(0, 0) - iso_z(e0), vec2(0, 1) - iso_z(e2), thick)
+ line(uv3, vec2(0, 0) - iso_z(e0), vec2(-1, 0) - iso_z(e3), thick)
+ line(uv3, vec2(0, 0) - iso_z(e0), vec2(0, -1) - iso_z(e4), thick);
+ line(uv3, vec2(0, 0) - iso_z(e0), vec2(0, 1) - iso_z(e2), thick)
+ line(uv3, vec2(0, 0) - iso_z(e0), vec2(-1, 0) - iso_z(e3), thick)
+ line(uv3, vec2(0, 0) - iso_z(e0), vec2(0, -1) - iso_z(e4), thick);
return vec4(f);
}
// SRC 11 : video in 2 + thru
subroutine(src_stage_sub) vec4 src_11(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_11(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
if (iDemo > 0) {
return src_3(vUV, seed, b1, f1, b2, f2, b3, f3);
}
return src_thru(vUV, iTex4, seed, b1, f1, b2, f2, b3, f3);
return src_thru(vUV, iTex6, seed, b1, f1, b2, f2, b3, f3);
}
// SRC 12 : Scales
subroutine(src_stage_sub) vec4 src_12(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_12(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -481,11 +481,11 @@ subroutine(src_stage_sub) vec4 src_12(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
}
// SRC 13 : Credenza
subroutine(src_stage_sub) vec4 src_13(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_13(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -500,7 +500,7 @@ subroutine(src_stage_sub) vec4 src_13(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
vec2 uv2 = uv1;
uv2 *= zoom;
uv2 = mod(uv2, 2);
uv2 = abs(uv2 - 1);
@@ -508,16 +508,16 @@ subroutine(src_stage_sub) vec4 src_13(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
float f = istep(1, length(uv2)) * istep(1, length(1 - uv2));
f *= istep(0.5, saw((length(uv2) + shape + 0.5) * repeat)) * istep(0.5, saw((length(1 - uv2) + shape + 0.5) * repeat));
return vec4(f);
}
// SRC 14 : Cursor
subroutine(src_stage_sub) vec4 src_14(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_14(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -551,15 +551,15 @@ subroutine(src_stage_sub) vec4 src_14(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
uv2.y += 0.1;
f -= istep(0, (uv2.x + uv2.y * 3) * (uv2.x - uv2.y * 3)) * istep(0, uv2.y);
f -= istep(0, (uv2.x + uv2.y * 3) * (uv2.x - uv2.y * 3)) * istep(0, uv2.y);
f += rect(uv2, vec2(0, -0.04), vec2(0.01, 0.04));
return vec4(f);
}
// SRC 15 : Random
subroutine(src_stage_sub) vec4 src_15(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
subroutine ( src_stage_sub ) vec4 src_15(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{
int src = int(randTime(seed + 100) * 14);
@@ -594,4 +594,4 @@ subroutine(src_stage_sub) vec4 src_15(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
}
}
#endif
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef INC_TEMPLATE
#define INC_TEMPLATE
#endif
#endif
+1 -1
View File
@@ -50,4 +50,4 @@ float cosTime(float k)
return cos(modTime(k, 0.5) * 2 * PI);
}
#endif
#endif
+2 -2
View File
@@ -7,7 +7,7 @@ const mat3x3 yuyv_to_rgb = {{1,1,1},{0,-0.39465,2.03211},{1.13983,-0.5806,0}};
vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) {
float w = base_width - 1;
int x = int(vUV.x * w);
int xU = x - x % 2;
@@ -25,4 +25,4 @@ vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) {
return vec4(yuyv_to_rgb * yuv, 1.0);
}
#endif
#endif
+17 -2
View File
@@ -1,4 +1,3 @@
#include <bsd/string.h>
#include <limits.h>
#include <log.h>
#include <stdbool.h>
@@ -33,6 +32,7 @@ static void print_help(int status_code) {
#ifdef VIDEO_IN
"[-vi=FILE] "
"[-vs=SIZE] "
"[-vr / -nvr] "
#endif /* VIDEO_IN */
"[-is=SIZE] "
"[-ls / -nls] "
@@ -64,6 +64,8 @@ static void print_help(int status_code) {
"allowed)\n"
" -vs, --video-size video capture desired height (default: "
"internal texture height)\n"
" -vr, --video-reconnect auto-reconnect video (default)\n"
" -nvr, --no-video-reconnect do not auto-reconnect video\n"
#endif /* VIDEO_IN */
" -is, --internal-size internal texture height (default: 720)\n"
" -ls, --load-state load saved state (default)\n"
@@ -131,6 +133,7 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->auto_random_cycle = 4;
params->video_in.length = 0;
params->video_size = 0;
params->video_reconnect = true;
params->internal_size = 720;
params->load_state = true;
params->save_state = true;
@@ -196,6 +199,18 @@ void args_parse(Parameters *params, int argc, char **argv) {
}
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-vr") || is_arg(arg, "--video-reconnect")) {
#ifdef VIDEO_IN
params->video_reconnect = true;
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-nvr") || is_arg(arg, "--no-video-reconnect")) {
#ifdef VIDEO_IN
params->video_reconnect = false;
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-is") || is_arg(arg, "--internal-size")) {
params->internal_size = parse_uint(arg, value);
@@ -228,4 +243,4 @@ void args_parse(Parameters *params, int argc, char **argv) {
if (params->video_size == 0) {
params->video_size = params->internal_size;
}
}
}
+1 -1
View File
@@ -5,4 +5,4 @@
void args_parse(Parameters *params, int argc, char **argv);
#endif /* ARGS_H */
#endif /* ARGS_H */
+1 -1
View File
@@ -7,4 +7,4 @@ unsigned int arr_uint_index_of(UintArray array, unsigned int value);
unsigned int arr_uint_remap_index(UintArray offsets, unsigned int *index);
#endif /* ARR_H */
#endif /* ARR_H */
+1 -1
View File
@@ -87,4 +87,4 @@
#define MAX_TAP_VALUES 10
#endif
#endif /* CONFIG_H */
#endif /* CONFIG_H */
+1 -2
View File
@@ -1,4 +1,3 @@
#include <bsd/string.h>
#include <hashmap.h>
#include <log.h>
#include <stdlib.h>
@@ -139,4 +138,4 @@ unsigned int config_file_get_int(const ConfigFile *config, const char *key,
return (unsigned int)atoi(item->value);
}
void config_file_free(const ConfigFile *config) { hashmap_free(config->map); }
void config_file_free(const ConfigFile *config) { hashmap_free(config->map); }
-2
View File
@@ -1,4 +1,3 @@
#include <bsd/string.h>
#include <log.h>
#include <stdbool.h>
#include <stdio.h>
@@ -10,7 +9,6 @@
#include "types.h"
#include "file.h"
#include "string.h"
static time_t get_file_time(const File *file) {
struct stat attr;
+1 -1
View File
@@ -15,4 +15,4 @@ void file_dump(const char *path, const char *content);
void file_free(const File *file);
#endif /* FILE_H */
#endif /* FILE_H */
+102 -42
View File
@@ -4,13 +4,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "types.h"
#include "config.h"
#include "config_file.h"
#include "file.h"
#include "forge.h"
#include "midi.h"
#include "project.h"
@@ -22,24 +21,24 @@
#include "video.h"
#include "window.h"
static Parameters init_params;
static SharedContext *context;
static ShaderProgram program;
static Window *window_output;
static Window *window_monitor;
static VideoCaptureArray inputs;
static Timer timer;
static MidiDevice midi;
static bool trace_midi;
static Project project;
static void compute_fps(bool trace_fps) {
static void compute_fps() {
double fps;
char title[STR_LEN];
if (timer_inc(&timer)) {
fps = timer_reset(&timer);
if (trace_fps) {
if (init_params.trace_fps) {
log_trace("(main) %.2ffps", fps);
}
@@ -57,18 +56,16 @@ static void compute_fps(bool trace_fps) {
}
}
static void init_context(const Parameters *params) {
static void init_context() {
context = shared_init_context("/" PACKAGE "_context");
context->stop = false;
state_init(context, &project.state_config, params->demo, params->auto_random,
params->auto_random_cycle, params->base_tempo, params->load_state);
state_init(context, &project.state_config, init_params.demo,
init_params.auto_random, init_params.auto_random_cycle,
init_params.base_tempo, init_params.load_state);
memset(context->input_resolutions, 0, sizeof(context->input_resolutions));
memset(context->input_formats, 0, sizeof(context->input_formats));
memset(context->input_fps, 0, sizeof(context->input_fps));
memset(context->input_swap, 0, sizeof(context->input_swap));
}
static void free_context() { shared_close_context(context); }
@@ -78,37 +75,83 @@ static void reload_shader(unsigned int i) {
}
#ifdef VIDEO_IN
static void init_inputs(const StringArray *video_in, unsigned int video_size) {
inputs.length = video_in->length;
static void init_inputs() {
context->inputs.length = init_params.video_in.length;
for (unsigned int i = 0; i < video_in->length; i++) {
video_init(&inputs.values[i], video_in->values[i], video_size);
for (unsigned int i = 0; i < init_params.video_in.length; i++) {
video_init(&context->inputs.values[i], init_params.video_in.values[i],
init_params.video_size);
if (!inputs.values[i].error) {
context->input_resolutions[i][0] = inputs.values[i].width;
context->input_resolutions[i][1] = inputs.values[i].height;
context->input_formats[i] = inputs.values[i].pixelformat;
if (!context->inputs.values[i].error) {
context->input_resolutions[i][0] = context->inputs.values[i].width;
context->input_resolutions[i][1] = context->inputs.values[i].height;
context->inputs.values[i].needs_reload = false;
context->inputs.values[i].disconnected = false;
}
}
}
static bool start_video_captures(unsigned int video_count, bool trace_fps) {
for (unsigned int i = 0; i < video_count; i++) {
if (!inputs.values[i].error &&
!video_background_read(&inputs.values[i], context, i, trace_fps)) {
return false;
static bool reconnect_video_captures() {
for (unsigned int i = 0; i < init_params.video_in.length; i++) {
if (context->inputs.values[i].disconnected) {
video_init(&context->inputs.values[i], init_params.video_in.values[i],
init_params.video_size);
if (!context->inputs.values[i].error) {
context->input_resolutions[i][0] = context->inputs.values[i].width;
context->input_resolutions[i][1] = context->inputs.values[i].height;
if (!video_background_read(&context->inputs.values[i], context, i,
init_params.trace_fps)) {
return false;
}
context->inputs.values[i].needs_reload = true;
context->inputs.values[i].disconnected = false;
}
}
}
return true;
}
static void free_video_captures(unsigned int video_count) {
for (unsigned int i = 0; i < video_count; i++) {
shaders_free_input(&program, &inputs.values[i]);
static bool start_video_captures() {
pid_t pid;
for (unsigned int i = 0; i < context->inputs.length; i++) {
if (!context->inputs.values[i].error &&
!video_background_read(&context->inputs.values[i], context, i,
init_params.trace_fps)) {
return false;
}
}
if (!init_params.video_reconnect) {
return true;
}
pid = fork();
if (pid < 0) {
log_error("Could not create subprocess");
return false;
}
if (pid == 0) {
return true;
}
log_info("background reconnect acquisition started (pid: %d)", pid);
while (!context->stop) {
sleep(1);
if (!reconnect_video_captures()) {
return false;
}
}
exit(EXIT_SUCCESS);
}
video_free(&inputs.values[i]);
static void free_video_captures() {
for (unsigned int i = 0; i < context->inputs.length; i++) {
shaders_free_input(&program, i);
video_free(&context->inputs.values[i]);
}
}
#endif /* VIDEO_IN */
static void error_callback(int error, const char *description) {
@@ -140,18 +183,20 @@ static void midi_callback(unsigned char code, unsigned char value) {
}
static bool init(const Parameters *params) {
init_params = *params;
project_init(&project, params->project_path, params->config_file);
if (project.error) {
return false;
}
init_context(params);
init_context();
#ifdef VIDEO_IN
init_inputs(&params->video_in, params->video_size);
init_inputs();
if (!start_video_captures(params->video_in.length, params->trace_fps)) {
if (!start_video_captures()) {
return false;
}
#endif /* VIDEO_IN */
@@ -200,7 +245,7 @@ static bool init(const Parameters *params) {
}
#ifdef VIDEO_IN
shaders_link_inputs(&program, &project, &inputs);
shaders_link_inputs(&program, &project, &context->inputs);
#endif /* VIDEO_IN */
if (program.error) {
@@ -221,12 +266,23 @@ static bool should_close() {
(window_monitor != NULL && window_should_close(window_monitor));
}
static void loop(bool hr, bool trace_fps) {
if (hr) {
static bool loop() {
if (init_params.hot_reload) {
project_reload(&project, reload_shader);
}
compute_fps(trace_fps);
#ifdef VIDEO_IN
if (init_params.video_reconnect) {
for (unsigned int i = 0; i < context->inputs.length; i++) {
if (context->inputs.values[i].needs_reload) {
shaders_relink_input(&program, &project, &context->inputs, i);
context->inputs.values[i].needs_reload = false;
}
}
}
#endif /* VIDEO_IN */
compute_fps();
context->time = window_get_time();
context->tempo_total = (float)tempo_total(&context->tempo);
@@ -248,12 +304,14 @@ static void loop(bool hr, bool trace_fps) {
}
window_events();
return true;
}
static void shutdown(const Parameters *params) {
static void shutdown() {
context->stop = true;
if (params->save_state) {
if (init_params.save_state) {
state_save(context, &project.state_config);
}
@@ -268,11 +326,11 @@ static void shutdown(const Parameters *params) {
if (window_monitor != NULL) {
window_use(window_monitor, context);
shaders_free_window(&program, params->output);
shaders_free_window(&program, init_params.output);
}
#ifdef VIDEO_IN
free_video_captures(params->video_in.length);
free_video_captures();
#endif /* VIDEO_IN */
free_context();
@@ -288,8 +346,10 @@ void forge_run(const Parameters *params) {
}
while (!should_close()) {
loop(params->hot_reload, params->trace_fps);
if (!loop()) {
return;
}
}
shutdown(params);
}
shutdown();
}
+1 -1
View File
@@ -5,4 +5,4 @@
void forge_run(const Parameters *params);
#endif /* FORGE_H */
#endif /* FORGE_H */
+1 -1
View File
@@ -17,7 +17,7 @@ int main(int argc, char **argv) {
puts(PACKAGE " " VERSION);
set_seed((unsigned long)time(NULL));
rand_set_seed((unsigned long)time(NULL));
forge_run(&params);
+1 -1
View File
@@ -3,4 +3,4 @@
int main(int argc, char **argv);
#endif /* MAIN_H */
#endif /* MAIN_H */
+3 -4
View File
@@ -1,11 +1,10 @@
#include <GLFW/glfw3.h>
#include <alsa/asoundlib.h>
#include <bsd/string.h>
#include <log.h>
#include <stdlib.h>
#include "types.h"
#include "config.h"
#include "log.h"
void midi_open(MidiDevice *device, const char *name) {
strlcpy(device->name, name, STR_LEN);
@@ -61,5 +60,5 @@ bool midi_background_listen(const MidiDevice *device,
log_info("(%s) background acquisition stopped by main thread (pid: %d)",
device->name, pid);
return false;
exit(EXIT_SUCCESS);
}
+1 -1
View File
@@ -11,4 +11,4 @@ bool midi_background_listen(const MidiDevice *device,
void (*event_callback)(unsigned char code,
unsigned char value));
#endif /* MIDI_H */
#endif /* MIDI_H */
+2 -3
View File
@@ -1,4 +1,4 @@
#include <bsd/string.h>
#include <log.h>
#include <string.h>
#include "types.h"
@@ -6,7 +6,6 @@
#include "config.h"
#include "config_file.h"
#include "file.h"
#include "log.h"
#include "project.h"
#include "state.h"
#include "string.h"
@@ -141,4 +140,4 @@ void project_free(const Project *project) {
}
config_file_free(&project->config);
}
}
+1 -1
View File
@@ -10,4 +10,4 @@ void project_reload(Project *project, void (*reload_callback)(unsigned int));
void project_free(const Project *project);
#endif /* PROJECT_H */
#endif /* PROJECT_H */
+2 -2
View File
@@ -13,11 +13,11 @@ static unsigned long rand(void) {
return (unsigned long)(x >> (22 + count));
}
void set_seed(unsigned long long seed) {
void rand_set_seed(unsigned long long seed) {
mcg_state = 2 * seed + 1;
(void)rand();
}
unsigned int rand_uint(const unsigned int max) {
return max == 0 ? 0 : (unsigned int)(rand() % max);
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
#ifndef RAND_H
#define RAND_H
void set_seed(unsigned long long seed);
void rand_set_seed(unsigned long long seed);
unsigned int rand_uint(unsigned int max);
#endif
#endif
+283 -101
View File
@@ -26,20 +26,49 @@
static const GLuint unused_uniform = (GLuint)-1;
bool check_glerror(ShaderProgram *program) {
bool check_glerror_ro(const char *context) {
unsigned int code;
code = glGetError();
if (code > 0) {
log_warn("GL Error: %04x", code);
program->error = true;
log_warn("GL Error: %04x (%s)", code, context);
return true;
}
return code > 0;
return false;
}
static void init_gl(ShaderProgram *program) {
bool check_glerror(ShaderProgram *program, const char *context) {
if (check_glerror_ro(context)) {
program->error = true;
return true;
}
return false;
}
bool check_eglerror_ro(const char *context) {
unsigned int code;
code = eglGetError();
if (code > 0 && code != EGL_SUCCESS) {
log_warn("EGL Error: %04x (%s)", code, context);
return true;
}
return false;
}
bool check_eglerror(ShaderProgram *program, const char *context) {
if (check_eglerror_ro(context)) {
program->error = true;
return true;
}
return false;
}
static bool init_gl(ShaderProgram *program) {
gladLoadGL(glfwGetProcAddress);
#ifdef VIDEO_IN
@@ -47,48 +76,88 @@ static void init_gl(ShaderProgram *program) {
if (program->egl_display == EGL_NO_DISPLAY) {
log_error("error: glfwGetEGLDisplay no EGLDisplay returned");
program->error = true;
return;
return false;
}
gladLoadEGL(program->egl_display, glfwGetProcAddress);
#endif /* VIDEO_IN */
return !check_glerror(program, "init_gl") &&
!check_eglerror(program, "init_gl");
}
static void init_textures(ShaderProgram *program,
static bool init_textures(ShaderProgram *program,
const SharedContext *context) {
glGenTextures(program->tex_count, program->textures);
if (check_glerror(program, "init_textures/glGenTextures")) {
return false;
}
for (unsigned int i = 0; i < program->tex_count; i++) {
// selects which texture unit subsequent texture state calls will affect
glActiveTexture(GL_TEXTURE0 + i);
if (check_glerror(program, "init_textures/glActiveTexture")) {
return false;
}
glBindTexture(GL_TEXTURE_2D, program->textures[i]);
if (check_glerror(program, "init_textures/glBindTexture")) {
return false;
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
if (check_glerror(program, "init_textures/glDisable")) {
return false;
}
// define texture image as empty
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context->tex_resolution[0],
context->tex_resolution[1], 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
if (check_glerror(program, "init_textures/glTexImage2D")) {
return false;
}
// setup mipmap context
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (check_glerror(program, "init_textures/glTexParameteri")) {
return false;
}
log_info("Texture %d initialized", i);
}
return true;
}
static void rebind_textures(const ShaderProgram *program) {
for (unsigned int i = 0; i < program->tex_count; i++) {
glActiveTexture(GL_TEXTURE0 + i);
check_glerror_ro("rebind_textures/glActiveTexture");
glBindTexture(GL_TEXTURE_2D, program->textures[i]);
check_glerror_ro("rebind_textures/glBindTexture");
}
}
#ifdef VIDEO_IN
static void link_input_to_texture(ShaderProgram *program, VideoCapture *input,
unsigned int texture_index, bool swap) {
static bool link_input_to_texture(ShaderProgram *program, VideoCapture *input,
unsigned int input_index,
unsigned int texture_index, bool swap,
bool reload) {
if (reload) {
glDeleteTextures(1, program->textures + texture_index);
if (check_glerror(program, "link_input_to_texture/glDeleteTextures")) {
return false;
}
glGenTextures(1, program->textures + texture_index);
if (check_glerror(program, "link_input_to_texture/glGenTextures")) {
return false;
}
}
EGLImageKHR dma_image;
dma_image = EGL_NO_IMAGE_KHR;
@@ -110,59 +179,81 @@ static void link_input_to_texture(ShaderProgram *program, VideoCapture *input,
dma_image = eglCreateImageKHR(program->egl_display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, attrib_list);
if (swap) {
input->dma_image_swap = dma_image;
} else {
input->dma_image = dma_image;
if (check_eglerror(program, "link_input_to_texture/eglCreateImageKHR")) {
return false;
}
if (dma_image == EGL_NO_IMAGE_KHR) {
log_error("(%s) eglCreateImageKHR failed %04x", input->name, eglGetError());
return;
if (swap) {
program->dma_images_swap[input_index] = dma_image;
} else {
program->dma_images[input_index] = dma_image;
}
glActiveTexture(GL_TEXTURE0 + texture_index);
if (check_glerror(program, "link_input_to_texture/glActiveTexture")) {
return false;
}
glBindTexture(GL_TEXTURE_2D, program->textures[texture_index]);
if (check_glerror(program, "link_input_to_texture/glBindTexture")) {
return false;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, input->width, input->height, 0, GL_RGB,
GL_UNSIGNED_BYTE, 0);
if (check_glerror(program, "link_input_to_texture/glTexImage2D")) {
return false;
}
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_EGL_image_storage.txt
glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, input->dma_image, NULL);
glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, dma_image, NULL);
if (check_eglerror(program,
"link_input_to_texture/glEGLImageTargetTexStorageEXT")) {
return false;
}
log_info("Texture %d linked to %s", texture_index, input->name);
return true;
}
static void init_input(ShaderProgram *program, const ConfigFile *config,
VideoCaptureArray *inputs) {
static bool init_input(ShaderProgram *program, const ConfigFile *config,
VideoCaptureArray *inputs, unsigned int i, bool reload) {
unsigned int tex_i;
char name[STR_LEN];
for (unsigned int i = 0; i < program->in_count; i++) {
if (i < inputs->length && !inputs->values[i].error) {
snprintf(name, STR_LEN, "IN_%d_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0);
link_input_to_texture(program, &inputs->values[i], tex_i, false);
if (inputs->values[i].with_swap) {
snprintf(name, STR_LEN, "IN_%d_SWAP_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0);
link_input_to_texture(program, &inputs->values[i], tex_i, true);
}
} else {
log_warn("Cannot link input %d", i + 1);
if (i < inputs->length && !inputs->values[i].error) {
snprintf(name, STR_LEN, "IN_%d_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0);
if (!link_input_to_texture(program, &inputs->values[i], i, tex_i, false,
reload)) {
return false;
}
if (inputs->values[i].with_swap) {
snprintf(name, STR_LEN, "IN_%d_SWAP_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0);
if (!link_input_to_texture(program, &inputs->values[i], i, tex_i, true,
reload)) {
return false;
}
}
} else {
log_warn("Cannot link input %d", i + 1);
}
return true;
}
#endif /* VIDEO_IN */
static void init_framebuffers(ShaderProgram *program,
static bool init_framebuffers(ShaderProgram *program,
const ConfigFile *config) {
unsigned int tex_i;
char name[STR_LEN];
GLenum framebuffer_status;
glGenFramebuffers(program->frag_count, program->frame_buffers);
if (check_glerror(program, "init_framebuffers/glGenFramebuffers")) {
return false;
}
for (unsigned int i = 0; i < program->frag_count; i++) {
if (i == program->frag_output_index || i == program->frag_monitor_index) {
@@ -170,48 +261,87 @@ static void init_framebuffers(ShaderProgram *program,
}
glBindFramebuffer(GL_FRAMEBUFFER, program->frame_buffers[i]);
if (check_glerror(program, "init_framebuffers/glBindFramebuffer")) {
return false;
}
snprintf(name, STR_LEN, "FRAG_%d_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
program->textures[tex_i], 0);
if (check_glerror(program, "init_framebuffers/glFramebufferTexture2D")) {
return false;
}
// check framebuffer status
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
log_error("Framebuffer %d is KO: %x", i + 1,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (check_glerror(program, "init_framebuffers/glCheckFramebufferStatus")) {
return false;
}
if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE) {
log_error("Framebuffer %d is KO: %x", i + 1, framebuffer_status);
program->error = true;
return;
return false;
}
log_info("Framebuffer %d initialized", i);
}
return;
return true;
}
static void init_vertices(ShaderProgram *program) {
static bool init_vertices(ShaderProgram *program) {
glGenBuffers(1, &program->vertex_buffer);
if (check_glerror(program, "init_vertices/glGenBuffers")) {
return false;
}
glBindBuffer(GL_ARRAY_BUFFER, program->vertex_buffer);
if (check_glerror(program, "init_vertices/glBindBuffer")) {
return false;
}
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
if (check_glerror(program, "init_vertices/glBufferData")) {
return false;
}
return true;
}
static void bind_vertices(ShaderProgram *program, unsigned int index) {
static bool bind_vertices(ShaderProgram *program, unsigned int index) {
glBindBuffer(GL_ARRAY_BUFFER, program->vertex_buffer);
if (check_glerror(program, "bind_vertices/glBindBuffer")) {
return false;
}
glGenVertexArrays(1, &program->vertex_array[index]);
if (check_glerror(program, "bind_vertices/glGenVertexArrays")) {
return false;
}
glBindVertexArray(program->vertex_array[index]);
if (check_glerror(program, "bind_vertices/glBindVertexArray")) {
return false;
}
for (unsigned int i = 0; i < program->frag_count; i++) {
// enable attribute pointer
glEnableVertexAttribArray(program->vpos_locations[i]);
if (check_glerror(program, "bind_vertices/glEnableVertexAttribArray")) {
return false;
}
// specify the location and data format of the array of generic vertex
// attributes to use when rendering
glVertexAttribPointer(program->vpos_locations[i], 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (void *)offsetof(Vertex, pos));
if (check_glerror(program, "bind_vertices/glVertexAttribPointer")) {
return false;
}
}
return true;
}
static bool compile_shader(GLuint shader_id, const char *name,
@@ -223,13 +353,18 @@ static bool compile_shader(GLuint shader_id, const char *name,
// update shader source code
glShaderSource(shader_id, 1, &source_code, NULL);
check_glerror_ro("compile_shader/glShaderSource");
// compile shader
glCompileShader(shader_id);
check_glerror_ro("compile_shader/glCompileShader");
// get compilation status
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &status_params);
check_glerror_ro("compile_shader/glGetShaderiv");
glGetShaderInfoLog(shader_id, 1024, NULL, (GLchar *)&log);
check_glerror_ro("compile_shader/glGetShaderInfoLog");
if (status_params == GL_FALSE) {
log_error("Failed to compile\n%s", log);
@@ -241,9 +376,13 @@ static bool compile_shader(GLuint shader_id, const char *name,
return status_params == GL_TRUE;
}
static void init_shaders(ShaderProgram *program, const Project *project) {
static bool init_shaders(ShaderProgram *program, const Project *project) {
// compile vertex shader
program->vertex_shader = glCreateShader(GL_VERTEX_SHADER);
if (check_glerror(program, "init_shaders/glCreateShader")) {
return false;
}
program->error = program->error || !compile_shader(program->vertex_shader,
"internal vertex shader",
vertex_shader_text);
@@ -251,18 +390,24 @@ static void init_shaders(ShaderProgram *program, const Project *project) {
// compile fragment shaders
for (unsigned int i = 0; i < program->frag_count; i++) {
program->fragment_shaders[i] = glCreateShader(GL_FRAGMENT_SHADER);
if (check_glerror(program, "init_shaders/glCreateShader")) {
return false;
}
program->error = program->error ||
!compile_shader(program->fragment_shaders[i],
project->fragment_shaders[i][0].path,
project->fragment_shaders[i][0].content);
if (program->error) {
return;
return false;
}
}
return true;
}
static void init_single_program(ShaderProgram *program, unsigned int i,
static bool init_single_program(ShaderProgram *program, unsigned int i,
const ConfigFile *config,
const StateConfig *state_config) {
unsigned int index1;
@@ -271,10 +416,20 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
const char *prefix;
program->programs[i] = glCreateProgram();
if (check_glerror(program, "init_single_program/glCreateProgram")) {
return false;
}
glAttachShader(program->programs[i], program->vertex_shader);
glAttachShader(program->programs[i], program->fragment_shaders[i]);
if (check_glerror(program, "init_single_program/glAttachShader")) {
return false;
}
glLinkProgram(program->programs[i]);
if (check_glerror(program, "init_single_program/glLinkProgram")) {
return false;
}
// create uniforms pointers
program->itime_locations[i] = glGetUniformLocation(
@@ -354,18 +509,6 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
glGetUniformLocation(program->programs[i], name);
}
for (unsigned int j = 0; j < program->sub_type_count; j++) {
snprintf(name, STR_LEN, "SUB_%d_PREFIX", j + 1);
prefix = config_file_get_str(config, name, 0);
for (unsigned int k = 0; k < program->sub_variant_count; k++) {
snprintf(name, STR_LEN, "%s%d", prefix, k + 1);
program->sub_locations[i * program->sub_variant_count *
program->sub_type_count +
j * program->sub_variant_count + k] =
glGetSubroutineIndex(program->programs[i], GL_FRAGMENT_SHADER, name);
}
}
prefix = config_file_get_str(config, "UNIFORM_ACTIVE_PREFIX", "iActive");
for (unsigned int j = 0; j < program->active_count; j++) {
snprintf(name, STR_LEN, "%s%d", prefix, j + 1);
@@ -404,18 +547,48 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
glGetUniformLocation(program->programs[i], name);
}
if (check_glerror(program, "init_single_program/glGetUniformLocation")) {
return false;
}
for (unsigned int j = 0; j < program->sub_type_count; j++) {
snprintf(name, STR_LEN, "SUB_%d_PREFIX", j + 1);
prefix = config_file_get_str(config, name, 0);
for (unsigned int k = 0; k < program->sub_variant_count; k++) {
snprintf(name, STR_LEN, "%s%d", prefix, k + 1);
program->sub_locations[i * program->sub_variant_count *
program->sub_type_count +
j * program->sub_variant_count + k] =
glGetSubroutineIndex(program->programs[i], GL_FRAGMENT_SHADER, name);
}
}
if (check_glerror(program, "init_single_program/glGetSubroutineIndex")) {
return false;
}
// create attribute pointer
program->vpos_locations[i] =
glGetAttribLocation(program->programs[i], "vPos");
if (check_glerror(program, "init_single_program/glGetAttribLocation")) {
return false;
}
log_info("Program %d initialized", i + 1);
return true;
}
static void init_programs(ShaderProgram *program, const ConfigFile *config,
static bool init_programs(ShaderProgram *program, const ConfigFile *config,
const StateConfig *state_config) {
for (unsigned int i = 0; i < program->frag_count; i++) {
init_single_program(program, i, config, state_config);
if (!init_single_program(program, i, config, state_config)) {
return false;
}
}
return true;
}
static void update_viewport(ShaderProgram *program,
@@ -426,8 +599,12 @@ static void update_viewport(ShaderProgram *program,
// clean and resize all textures
for (unsigned int i = 0; i < program->tex_count; i++) {
glActiveTexture(GL_TEXTURE0 + i);
check_glerror_ro("update_viewport/glActiveTexture");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context->tex_resolution[0],
context->tex_resolution[1], 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
check_glerror_ro(
"update_viewport/glTexImage2D"); // TODO dont remap input textures
}
program->last_resolution[0] = context->resolution[0];
program->last_resolution[1] = context->resolution[1];
@@ -507,11 +684,11 @@ static void use_program(const ShaderProgram *program, int i, bool output,
write_uniform_2f(program->iinres_locations[i * program->in_count + j],
&context->input_resolutions[j]);
write_uniform_1i(program->iinfmt_locations[i * program->in_count + j],
context->input_formats[j]);
context->inputs.values[j].pixelformat);
write_uniform_1i(program->iinfps_locations[i * program->in_count + j],
context->input_fps[j]);
context->inputs.values[j].fps);
write_uniform_1i(program->iinswap_locations[i * program->in_count + j],
context->input_swap[j] ? 1 : 0);
context->inputs.values[j].swap ? 1 : 0);
}
// set seeds uniforms
@@ -581,71 +758,64 @@ void shaders_init(ShaderProgram *program, const Project *project,
program->active_count = project->state_config.midi_active_counts.length;
program->midi_lengths.length = 0;
init_gl(program);
#ifdef VIDEO_IN
memset(program->dma_images, 0, sizeof(program->dma_images));
memset(program->dma_images_swap, 0, sizeof(program->dma_images_swap));
#endif /* VIDEO_IN */
if (check_glerror(program)) {
if (!init_gl(program)) {
return;
}
init_shaders(program, project);
if (program->error || check_glerror(program)) {
if (!init_shaders(program, project)) {
return;
}
init_textures(program, context);
if (check_glerror(program)) {
if (!init_textures(program, context)) {
return;
}
init_framebuffers(program, &project->config);
if (check_glerror(program)) {
if (!init_framebuffers(program, &project->config)) {
return;
}
init_programs(program, &project->config, &project->state_config);
if (check_glerror(program)) {
if (!init_programs(program, &project->config, &project->state_config)) {
return;
}
init_vertices(program);
if (check_glerror(program)) {
if (!init_vertices(program)) {
return;
}
}
bind_vertices(program, rebind ? 1 : 0);
if (check_glerror(program)) {
if (!bind_vertices(program, rebind ? 1 : 0)) {
return;
}
}
void shaders_relink_input(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs, unsigned int i) {
#ifdef VIDEO_IN
// shaders_free_input(program, i);
init_input(program, &project->config, inputs, i, true);
#endif /* VIDEO_IN */
}
void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs) {
#ifdef VIDEO_IN
init_input(program, &project->config, inputs);
if (check_glerror(program)) {
return;
for (unsigned int i = 0; i < program->in_count; i++) {
init_input(program, &project->config, inputs, i, false);
}
#endif /* VIDEO_IN */
}
void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project) {
bool result;
result = compile_shader(program->fragment_shaders[i], fragment_shader->path,
fragment_shader->content);
if (result) {
init_single_program(program, i, &project->config, &project->state_config);
if (compile_shader(program->fragment_shaders[i], fragment_shader->path,
fragment_shader->content) &&
init_single_program(program, i, &project->config,
&project->state_config)) {
log_info("Program %d updated", i + 1);
}
}
@@ -672,30 +842,42 @@ void shaders_compute(ShaderProgram *program, const SharedContext *context,
monitor ? program->frag_monitor_index
: program->frag_output_index,
true, context);
check_glerror(program, "shaders_compute");
}
void shaders_free(const ShaderProgram *program) {
for (unsigned int i = 0; i < program->frag_count; i++) {
glDeleteProgram(program->programs[i]);
}
check_glerror_ro("shaders_free/glDeleteProgram");
glDeleteFramebuffers(program->frag_count, program->frame_buffers);
check_glerror_ro("shaders_free/glDeleteFramebuffers");
glDeleteTextures(program->tex_count, program->textures);
check_glerror_ro("shaders_free/glDeleteTextures");
glDeleteBuffers(1, &program->vertex_buffer);
check_glerror_ro("shaders_free/glDeleteBuffers");
}
void shaders_free_window(const ShaderProgram *program, bool secondary) {
glDeleteVertexArrays(1, &program->vertex_array[secondary ? 1 : 0]);
check_glerror_ro("shaders_free_window/glDeleteVertexArrays");
}
void shaders_free_input(const ShaderProgram *program,
const VideoCapture *input) {
void shaders_free_input(ShaderProgram *program, unsigned int input_index) {
#ifdef VIDEO_IN
if (!input->error && input->dma_image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, input->dma_image);
if (program->dma_images[input_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, program->dma_images[input_index]);
program->dma_images[input_index] = EGL_NO_IMAGE_KHR;
}
if (!input->error && input->dma_image_swap != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, input->dma_image_swap);
if (program->dma_images_swap[input_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display,
program->dma_images_swap[input_index]);
program->dma_images_swap[input_index] = EGL_NO_IMAGE_KHR;
}
check_eglerror_ro("shaders_free_input/eglDestroyImageKHR");
#endif /* VIDEO_IN */
}
}
+5 -3
View File
@@ -6,6 +6,9 @@
void shaders_init(ShaderProgram *program, const Project *project,
const SharedContext *context, bool rebind);
void shaders_relink_input(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs, unsigned int i);
void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs);
@@ -19,7 +22,6 @@ void shaders_free(const ShaderProgram *program);
void shaders_free_window(const ShaderProgram *program, bool secondary);
void shaders_free_input(const ShaderProgram *program,
const VideoCapture *input);
void shaders_free_input(ShaderProgram *program, unsigned int input_index);
#endif /* SHADERS_H */
#endif /* SHADERS_H */
+1 -1
View File
@@ -32,4 +32,4 @@ SharedContext *shared_init_context(const char *key) {
void shared_close_context(SharedContext *shared) {
close_shared(shared, sizeof(SharedContext), shared->fd);
}
}
+1 -1
View File
@@ -6,4 +6,4 @@
SharedContext *shared_init_context(const char *key);
void shared_close_context(SharedContext *shared);
#endif /* SHARED_H */
#endif /* SHARED_H */
+2 -1
View File
@@ -1,5 +1,6 @@
#include <log.h>
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
@@ -619,7 +620,7 @@ bool state_background_write(SharedContext *context,
}
log_info("(state) background writing stopped by main thread (pid: %d)", pid);
return false;
exit(EXIT_SUCCESS);
}
void state_init(SharedContext *context, const StateConfig *state_config,
+1 -1
View File
@@ -22,4 +22,4 @@ void state_init(SharedContext *context, const StateConfig *state_config,
void state_save(const SharedContext *context, const StateConfig *state_config);
#endif /* STATE_H */
#endif /* STATE_H */
+1 -2
View File
@@ -1,4 +1,3 @@
#include <bsd/string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -73,4 +72,4 @@ char *string_replace_at(const char *src, unsigned int from, unsigned int to,
strlcpy(dst + from + rpl_len, src + to, src_len - to + 1);
return dst;
}
}
+1 -1
View File
@@ -10,4 +10,4 @@ bool string_is_number(const char *value);
char *string_replace_at(const char *src, unsigned int from, unsigned int to,
const char *rpl);
#endif /* STRINGS_H */
#endif /* STRINGS_H */
+1 -1
View File
@@ -137,4 +137,4 @@ double tempo_total(const Tempo *tempo) {
double tempo_progress(const Tempo *tempo, double modulo) {
return fmod(tempo_total(tempo), modulo);
}
}
+1 -1
View File
@@ -13,4 +13,4 @@ double tempo_total(const Tempo *tempo);
double tempo_progress(const Tempo *tempo, double modulo);
#endif /* TEMPO_H */
#endif /* TEMPO_H */
+1 -1
View File
@@ -31,4 +31,4 @@ double timer_reset(Timer *timer) {
timer->counter = 0;
return per_secs;
}
}
+1 -1
View File
@@ -9,4 +9,4 @@ bool timer_inc(Timer *timer);
double timer_reset(Timer *timer);
#endif /* TIMER_H */
#endif /* TIMER_H */
+13 -8
View File
@@ -8,7 +8,6 @@
#include <hashmap.h>
#include <linmath.h>
#include <stdbool.h>
#include <sys/time.h>
#include <time.h>
#include "config.h"
@@ -49,6 +48,7 @@ typedef struct Parameters {
StringArray video_in;
unsigned int video_size;
unsigned int internal_size;
bool video_reconnect;
bool load_state;
bool save_state;
bool trace_midi;
@@ -130,6 +130,8 @@ typedef struct ShaderProgram {
unsigned int in_count;
#ifdef VIDEO_IN
EGLDisplay egl_display;
EGLImageKHR dma_images[MAX_VIDEO];
EGLImageKHR dma_images_swap[MAX_VIDEO];
#endif /* VIDEO_IN */
} ShaderProgram;
@@ -138,7 +140,11 @@ typedef struct ShaderProgram {
typedef struct VideoCapture {
char name[STR_LEN];
bool error;
bool disconnected;
bool needs_reload;
bool with_swap;
bool swap;
unsigned int fps;
int fd;
int exp_fd;
int exp_fd_swap;
@@ -149,12 +155,13 @@ typedef struct VideoCapture {
#ifdef VIDEO_IN
struct v4l2_buffer buf;
struct v4l2_buffer buf_swap;
EGLImageKHR dma_image;
EGLImageKHR dma_image_swap;
#endif /* VIDEO_IN */
} VideoCapture;
typedef ARRAY(VideoCaptureArray, VideoCapture);
typedef struct VideoCaptureArray {
VideoCapture values[MAX_VIDEO];
unsigned int length;
} VideoCaptureArray;
// window.c
@@ -193,9 +200,7 @@ typedef struct SharedContext {
unsigned int auto_random_cycle;
unsigned int seeds[MAX_FRAG];
unsigned int fps;
unsigned int input_formats[MAX_VIDEO];
unsigned int input_fps[MAX_VIDEO];
bool input_swap[MAX_VIDEO];
VideoCaptureArray inputs;
bool stop;
} SharedContext;
@@ -277,4 +282,4 @@ typedef struct Project {
File fragment_shaders[MAX_FRAG][MAX_SUB_FILE + 1];
} Project;
#endif /* TYPES_H */
#endif /* TYPES_H */
+16 -14
View File
@@ -1,6 +1,5 @@
#ifdef VIDEO_IN
#include <bsd/string.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/videodev2.h>
@@ -286,22 +285,25 @@ static void close_stream(const VideoCapture *video_capture) {
ioctl(video_capture->fd, VIDIOC_STREAMOFF, &buf_type);
}
static unsigned int read_video(const VideoCapture *video_capture, bool swap) {
int result;
static unsigned int read_video(VideoCapture *video_capture) {
unsigned int result;
struct v4l2_capability cap;
result = 0;
if ((swap || !video_capture->with_swap) &&
if ((video_capture->swap || !video_capture->with_swap) &&
ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf) != -1) {
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf);
result = 1;
} else if (!swap && video_capture->with_swap &&
} else if (!video_capture->swap && video_capture->with_swap &&
ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf_swap) !=
-1) {
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf_swap);
result = 2;
} else if (ioctl(video_capture->fd, VIDIOC_QUERYCAP, &cap) == -1) {
video_capture->error = true;
}
return result;
@@ -361,18 +363,18 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
pid);
timer_init(&timer, 30);
while (!context->stop) {
video_result = read_video(video_capture, context->input_swap[input_index]);
while (!context->stop && !video_capture->error) {
video_result = read_video(video_capture);
if (video_result > 0 && timer_inc(&timer)) {
fps = timer_reset(&timer);
context->input_fps[input_index] = (unsigned int)round(fps);
context->inputs.values[input_index].fps = (unsigned int)round(fps);
if (trace_fps) {
log_trace("(%s) %.2ffps", video_capture->name, fps);
}
}
if (video_result > 0) {
context->input_swap[input_index] = video_result == 2;
video_capture->swap = video_result == 2;
}
}
if (context->stop) {
@@ -381,15 +383,15 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
} else {
log_info("(%s) background acquisition stopped after error (pid: %d)",
video_capture->name, pid);
video_capture->disconnected = true;
video_capture->pixelformat = 0;
video_free(video_capture);
}
exit(context->stop ? EXIT_SUCCESS : EXIT_FAILURE);
return false;
}
void video_free(const VideoCapture *video_capture) {
if (!video_capture->error) {
close_stream(video_capture);
}
close_stream(video_capture);
if (video_capture->exp_fd != -1) {
close(video_capture->exp_fd);
}
@@ -401,4 +403,4 @@ void video_free(const VideoCapture *video_capture) {
}
}
#endif /* VIDEO_IN */
#endif /* VIDEO_IN */
+1 -1
View File
@@ -11,4 +11,4 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
void video_free(const VideoCapture *video_capture);
#endif /* VIDEO_H */
#endif /* VIDEO_H */
+1 -1
View File
@@ -165,4 +165,4 @@ unsigned int window_read_key(int key, int action, int mods) {
result += 100000;
}
return result;
}
}
+1 -1
View File
@@ -29,4 +29,4 @@ bool window_escape_key(int key, int action);
unsigned int window_read_key(int key, int action, int mods);
#endif /* WINDOW_H */
#endif /* WINDOW_H */