@supersoniks/concorde 4.9.97-visual-stack.0 → 4.9.98-visual-stack.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/build-infos.json +1 -1
  2. package/concorde-3d.bundle.js +201 -152
  3. package/concorde-3d.es.js +14621 -13823
  4. package/concorde-core.bundle.js +1 -1
  5. package/concorde-core.es.js +1 -1
  6. package/concorde-hugging-face-infer.bundle.js +1 -1
  7. package/concorde-hugging-face-infer.es.js +1 -1
  8. package/concorde-shader.bundle.js +31 -20
  9. package/concorde-shader.es.js +3236 -2865
  10. package/concorde-webgpu.bundle.js +43 -32
  11. package/concorde-webgpu.es.js +2977 -2618
  12. package/dist/concorde-3d.bundle.js +201 -152
  13. package/dist/concorde-3d.es.js +14621 -13823
  14. package/dist/concorde-core.bundle.js +1 -1
  15. package/dist/concorde-core.es.js +1 -1
  16. package/dist/concorde-hugging-face-infer.bundle.js +1 -1
  17. package/dist/concorde-hugging-face-infer.es.js +1 -1
  18. package/dist/concorde-shader.bundle.js +31 -20
  19. package/dist/concorde-shader.es.js +3236 -2865
  20. package/dist/concorde-webgpu.bundle.js +43 -32
  21. package/dist/concorde-webgpu.es.js +2977 -2618
  22. package/dist/js/core/components/functional/3d/3d.js +100 -2
  23. package/dist/js/core/components/functional/3d/3d.js.map +1 -1
  24. package/dist/js/core/components/functional/3d/constants.js +3 -0
  25. package/dist/js/core/components/functional/3d/constants.js.map +1 -1
  26. package/dist/js/core/components/functional/3d/ensure.js +6 -3
  27. package/dist/js/core/components/functional/3d/ensure.js.map +1 -1
  28. package/dist/js/core/components/functional/3d/form-params.js +2 -0
  29. package/dist/js/core/components/functional/3d/form-params.js.map +1 -1
  30. package/dist/js/core/components/functional/3d/frame-source.js +22 -0
  31. package/dist/js/core/components/functional/3d/frame-source.js.map +1 -0
  32. package/dist/js/core/components/functional/3d/life.js +2 -1
  33. package/dist/js/core/components/functional/3d/life.js.map +1 -1
  34. package/dist/js/core/components/functional/3d/out-publish.js +3 -2
  35. package/dist/js/core/components/functional/3d/out-publish.js.map +1 -1
  36. package/dist/js/core/components/functional/3d/playback.js +6 -1
  37. package/dist/js/core/components/functional/3d/playback.js.map +1 -1
  38. package/dist/js/core/components/functional/3d/post-bind.js +207 -0
  39. package/dist/js/core/components/functional/3d/post-bind.js.map +1 -0
  40. package/dist/js/core/components/functional/3d/post-channels.js +39 -0
  41. package/dist/js/core/components/functional/3d/post-channels.js.map +1 -0
  42. package/dist/js/core/components/functional/3d/post-fx.js +495 -0
  43. package/dist/js/core/components/functional/3d/post-fx.js.map +1 -0
  44. package/dist/js/core/components/functional/3d/props.js +142 -0
  45. package/dist/js/core/components/functional/3d/props.js.map +1 -1
  46. package/dist/js/core/components/functional/3d/reload.js +5 -0
  47. package/dist/js/core/components/functional/3d/reload.js.map +1 -1
  48. package/dist/js/core/components/functional/3d/runtime.js +80 -3
  49. package/dist/js/core/components/functional/3d/runtime.js.map +1 -1
  50. package/dist/js/core/components/functional/3d/scene-runtime.js +1 -1
  51. package/dist/js/core/components/functional/3d/scene-runtime.js.map +1 -1
  52. package/dist/js/core/components/functional/3d/tune.js +21 -0
  53. package/dist/js/core/components/functional/3d/tune.js.map +1 -1
  54. package/dist/js/core/components/functional/3d/updated.js +13 -1
  55. package/dist/js/core/components/functional/3d/updated.js.map +1 -1
  56. package/dist/js/core/components/functional/shader/gl-runtime.js +280 -31
  57. package/dist/js/core/components/functional/shader/gl-runtime.js.map +1 -1
  58. package/dist/js/core/components/functional/shader/shader.js +207 -61
  59. package/dist/js/core/components/functional/shader/shader.js.map +1 -1
  60. package/dist/js/core/components/functional/shader/shadertoy-prelude.js +39 -5
  61. package/dist/js/core/components/functional/shader/shadertoy-prelude.js.map +1 -1
  62. package/dist/js/core/components/functional/shader/types.js +58 -7
  63. package/dist/js/core/components/functional/shader/types.js.map +1 -1
  64. package/dist/js/core/components/functional/webgpu/gl-fallback.js +27 -6
  65. package/dist/js/core/components/functional/webgpu/gl-fallback.js.map +1 -1
  66. package/dist/js/core/components/functional/webgpu/gpu-runtime.js +115 -13
  67. package/dist/js/core/components/functional/webgpu/gpu-runtime.js.map +1 -1
  68. package/dist/js/core/components/functional/webgpu/helpers.js +8 -2
  69. package/dist/js/core/components/functional/webgpu/helpers.js.map +1 -1
  70. package/dist/js/core/components/functional/webgpu/webgpu.js +43 -1
  71. package/dist/js/core/components/functional/webgpu/webgpu.js.map +1 -1
  72. package/dist/js/core/utils/mediaRef.js +86 -0
  73. package/dist/js/core/utils/mediaRef.js.map +1 -1
  74. package/dist/types/core/components/functional/3d/3d.d.ts +31 -3
  75. package/dist/types/core/components/functional/3d/3d.d.ts.map +1 -1
  76. package/dist/types/core/components/functional/3d/constants.d.ts +1 -1
  77. package/dist/types/core/components/functional/3d/constants.d.ts.map +1 -1
  78. package/dist/types/core/components/functional/3d/ensure.d.ts +3 -0
  79. package/dist/types/core/components/functional/3d/ensure.d.ts.map +1 -1
  80. package/dist/types/core/components/functional/3d/form-params.d.ts.map +1 -1
  81. package/dist/types/core/components/functional/3d/frame-source.d.ts +23 -0
  82. package/dist/types/core/components/functional/3d/frame-source.d.ts.map +1 -0
  83. package/dist/types/core/components/functional/3d/life.d.ts +2 -0
  84. package/dist/types/core/components/functional/3d/life.d.ts.map +1 -1
  85. package/dist/types/core/components/functional/3d/out-publish.d.ts +1 -0
  86. package/dist/types/core/components/functional/3d/out-publish.d.ts.map +1 -1
  87. package/dist/types/core/components/functional/3d/playback.d.ts +9 -0
  88. package/dist/types/core/components/functional/3d/playback.d.ts.map +1 -1
  89. package/dist/types/core/components/functional/3d/post-bind.d.ts +52 -0
  90. package/dist/types/core/components/functional/3d/post-bind.d.ts.map +1 -0
  91. package/dist/types/core/components/functional/3d/post-channels.d.ts +40 -0
  92. package/dist/types/core/components/functional/3d/post-channels.d.ts.map +1 -0
  93. package/dist/types/core/components/functional/3d/post-fx.d.ts +73 -0
  94. package/dist/types/core/components/functional/3d/post-fx.d.ts.map +1 -0
  95. package/dist/types/core/components/functional/3d/props.d.ts +42 -0
  96. package/dist/types/core/components/functional/3d/props.d.ts.map +1 -1
  97. package/dist/types/core/components/functional/3d/reload.d.ts +3 -0
  98. package/dist/types/core/components/functional/3d/reload.d.ts.map +1 -1
  99. package/dist/types/core/components/functional/3d/runtime.d.ts +16 -1
  100. package/dist/types/core/components/functional/3d/runtime.d.ts.map +1 -1
  101. package/dist/types/core/components/functional/3d/scene-runtime.d.ts +1 -1
  102. package/dist/types/core/components/functional/3d/scene-runtime.d.ts.map +1 -1
  103. package/dist/types/core/components/functional/3d/tune.d.ts +5 -0
  104. package/dist/types/core/components/functional/3d/tune.d.ts.map +1 -1
  105. package/dist/types/core/components/functional/3d/types.d.ts +16 -0
  106. package/dist/types/core/components/functional/3d/types.d.ts.map +1 -1
  107. package/dist/types/core/components/functional/3d/updated.d.ts.map +1 -1
  108. package/dist/types/core/components/functional/shader/gl-runtime.d.ts +53 -2
  109. package/dist/types/core/components/functional/shader/gl-runtime.d.ts.map +1 -1
  110. package/dist/types/core/components/functional/shader/shader.d.ts +41 -9
  111. package/dist/types/core/components/functional/shader/shader.d.ts.map +1 -1
  112. package/dist/types/core/components/functional/shader/shadertoy-prelude.d.ts +16 -2
  113. package/dist/types/core/components/functional/shader/shadertoy-prelude.d.ts.map +1 -1
  114. package/dist/types/core/components/functional/shader/types.d.ts +35 -5
  115. package/dist/types/core/components/functional/shader/types.d.ts.map +1 -1
  116. package/dist/types/core/components/functional/webgpu/gl-fallback.d.ts +4 -0
  117. package/dist/types/core/components/functional/webgpu/gl-fallback.d.ts.map +1 -1
  118. package/dist/types/core/components/functional/webgpu/gpu-runtime.d.ts +14 -0
  119. package/dist/types/core/components/functional/webgpu/gpu-runtime.d.ts.map +1 -1
  120. package/dist/types/core/components/functional/webgpu/helpers.d.ts +1 -0
  121. package/dist/types/core/components/functional/webgpu/helpers.d.ts.map +1 -1
  122. package/dist/types/core/components/functional/webgpu/webgpu.d.ts +11 -1
  123. package/dist/types/core/components/functional/webgpu/webgpu.d.ts.map +1 -1
  124. package/dist/types/core/utils/mediaRef.d.ts +34 -0
  125. package/dist/types/core/utils/mediaRef.d.ts.map +1 -1
  126. package/package.json +49 -1
  127. package/src/core/components/functional/3d/3d.md +46 -3
  128. package/src/core/components/functional/3d/3d.spec.ts +42 -0
  129. package/src/core/components/functional/3d/3d.ts +112 -3
  130. package/src/core/components/functional/3d/constants.ts +3 -0
  131. package/src/core/components/functional/3d/ensure.ts +11 -3
  132. package/src/core/components/functional/3d/form-params.ts +2 -0
  133. package/src/core/components/functional/3d/frame-source.ts +30 -0
  134. package/src/core/components/functional/3d/life.ts +4 -1
  135. package/src/core/components/functional/3d/out-publish.ts +4 -3
  136. package/src/core/components/functional/3d/playback.ts +13 -1
  137. package/src/core/components/functional/3d/post-bind.ts +262 -0
  138. package/src/core/components/functional/3d/post-channels.spec.ts +36 -0
  139. package/src/core/components/functional/3d/post-channels.ts +69 -0
  140. package/src/core/components/functional/3d/post-fx.ts +594 -0
  141. package/src/core/components/functional/3d/props.ts +45 -0
  142. package/src/core/components/functional/3d/reload.ts +8 -0
  143. package/src/core/components/functional/3d/runtime.ts +93 -4
  144. package/src/core/components/functional/3d/scene-runtime.ts +3 -0
  145. package/src/core/components/functional/3d/tune.ts +21 -0
  146. package/src/core/components/functional/3d/types.ts +14 -0
  147. package/src/core/components/functional/3d/updated.ts +17 -1
  148. package/src/core/components/functional/shader/gl-runtime.ts +374 -36
  149. package/src/core/components/functional/shader/shader.md +36 -11
  150. package/src/core/components/functional/shader/shader.spec.ts +350 -3
  151. package/src/core/components/functional/shader/shader.ts +236 -59
  152. package/src/core/components/functional/shader/shadertoy-prelude.ts +49 -5
  153. package/src/core/components/functional/shader/types.ts +92 -13
  154. package/src/core/components/functional/webgpu/gl-fallback.ts +28 -4
  155. package/src/core/components/functional/webgpu/gpu-runtime.ts +154 -18
  156. package/src/core/components/functional/webgpu/helpers.ts +8 -2
  157. package/src/core/components/functional/webgpu/webgpu.md +13 -2
  158. package/src/core/components/functional/webgpu/webgpu.spec.ts +37 -0
  159. package/src/core/components/functional/webgpu/webgpu.ts +48 -1
  160. package/src/core/utils/mediaRef.spec.ts +61 -0
  161. package/src/core/utils/mediaRef.ts +108 -0
  162. package/src/docs/_misc/visual-stack.md +37 -3
  163. package/src/docs/components/docs-demo-sources.ts +42 -0
  164. package/src/docs/docs.ts +3 -0
  165. package/src/docs/example/docs-3d-post-depth-demo.ts +68 -0
  166. package/src/docs/example/docs-3d-shader-live-demo.ts +98 -0
  167. package/src/docs/example/docs-3d-webgpu-live-demo.ts +83 -0
  168. package/src/docs/example/docs-harris-webgpu-demo.ts +53 -55
  169. package/src/docs/search/docs-search.json +2583 -863
  170. package/src/tsconfig.json +36 -0
  171. package/src/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,7 @@
1
1
  import { css, html, LitElement } from "lit";
2
2
  import { customElement, property, state } from "lit/decorators.js";
3
3
  import HTML from "@supersoniks/concorde/core/utils/HTML";
4
- import { revokeMediaUrls, toMediaUrl } from "@supersoniks/concorde/core/utils/mediaRef";
4
+ import { revokeMediaUrls, toMediaUrl, mediaElementKey, lookupLiveElement, FrameConsumerRegistry } from "@supersoniks/concorde/core/utils/mediaRef";
5
5
  import { PublisherManager } from "@supersoniks/concorde/utils";
6
6
  import {
7
7
  parseChannelSource,
@@ -9,6 +9,8 @@ import {
9
9
  } from "./gl-runtime";
10
10
  import {
11
11
  PassChannels,
12
+ ReleaseOffscreen,
13
+ ShaderBufferFormat,
12
14
  ShaderCompileError,
13
15
  ShaderExtractItem,
14
16
  ShaderExtractOutput,
@@ -18,17 +20,61 @@ import {
18
20
  ShaderRoi,
19
21
  ShaderSources,
20
22
  SonicShaderOutput,
23
+ channelsUseSelf,
21
24
  findPeaks,
22
25
  meanRgba,
26
+ parseBufferFormat,
23
27
  parseExtractPass,
24
28
  parseHoverParam,
29
+ parseReleaseOffscreen,
25
30
  roiToPixels,
31
+ rgbaToUint8,
32
+ shouldReleaseWhenOffscreen,
26
33
  } from "./types";
27
34
 
28
35
  const tagName = "sonic-shader";
29
36
 
30
37
  const MAX_PATCH_PX = 128;
31
38
 
39
+ /** Clés qui forcent une recompilation / recreate FBO. */
40
+ const PROGRAM_KEYS = [
41
+ "image",
42
+ "bufferA",
43
+ "bufferB",
44
+ "bufferC",
45
+ "bufferD",
46
+ "common",
47
+ "bufferFormat",
48
+ ] as const;
49
+
50
+ /** Clés qui ne font que re-lier les textures (pas de rebuild). */
51
+ const CHANNEL_KEYS = [
52
+ "channel0",
53
+ "channel1",
54
+ "channel2",
55
+ "channel3",
56
+ "imageCh0",
57
+ "imageCh1",
58
+ "imageCh2",
59
+ "imageCh3",
60
+ "bufferACh0",
61
+ "bufferACh1",
62
+ "bufferACh2",
63
+ "bufferACh3",
64
+ "bufferBCh0",
65
+ "bufferBCh1",
66
+ "bufferBCh2",
67
+ "bufferBCh3",
68
+ "bufferCCh0",
69
+ "bufferCCh1",
70
+ "bufferCCh2",
71
+ "bufferCCh3",
72
+ "bufferDCh0",
73
+ "bufferDCh1",
74
+ "bufferDCh2",
75
+ "bufferDCh3",
76
+ ] as const;
77
+
32
78
  /**
33
79
  * Champs méta publiés vers le out-dataProvider.
34
80
  * `param0…3` sont exclus du gate « consommateur » : un `sonic-input name="param0"`
@@ -104,16 +150,24 @@ function toNum(v: unknown, fallback: number): number {
104
150
  return fallback;
105
151
  }
106
152
 
107
- /** Valeur canal depuis DP : string (url ou buffer-a) | MediaRef → url. */
153
+ /** Valeur canal depuis DP : string (url, buffer-a, #id) | MediaRef → url | { element }. */
108
154
  function channelFormValue(value: unknown): string | undefined {
109
155
  if (value === null || value === "") return "";
110
156
  if (typeof value === "string") return value.trim();
111
157
  const url = toMediaUrl(value);
112
158
  if (url) return url;
113
- if (value && typeof value === "object") return undefined;
159
+ if (value && typeof value === "object") {
160
+ const el = (value as { element?: unknown }).element;
161
+ const key = mediaElementKey(el);
162
+ if (key) return key;
163
+ return undefined;
164
+ }
114
165
  return "";
115
166
  }
116
167
 
168
+ /** Exposé pour tests. */
169
+ export { channelFormValue };
170
+
117
171
  function parseRois(value: unknown): ShaderRoi[] {
118
172
  if (!Array.isArray(value)) return [];
119
173
  const out: ShaderRoi[] = [];
@@ -286,12 +340,32 @@ export class SonicShader extends LitElement {
286
340
  /** Vitesse d’easing hover on/off (1/s, défaut 10). */
287
341
  @property({ type: Number, attribute: "hover-speed" }) hoverSpeed = 10;
288
342
  /**
289
- * Libère le contexte WebGL hors viewport (défaut true).
290
- * Les navigateurs limitent ~8–16 contextes.
343
+ * Hors viewport : libère textures/FBO (garde le contexte).
344
+ * `auto` (défaut) : ne libère pas si une passe lit `self`.
345
+ * `true` / `false` forcent le comportement.
291
346
  */
292
- @property({ type: Boolean, attribute: "release-offscreen" })
293
- releaseOffscreen = true;
347
+ @property({
348
+ attribute: "release-offscreen",
349
+ converter: {
350
+ fromAttribute: (value: string | null) => parseReleaseOffscreen(value),
351
+ toAttribute: (value: ReleaseOffscreen) => {
352
+ if (value === "auto") return "auto";
353
+ if (value === false) return "false";
354
+ return "";
355
+ },
356
+ },
357
+ })
358
+ releaseOffscreen: ReleaseOffscreen = "auto";
294
359
  @property({ type: Number, attribute: "dpr-max" }) dprMax = 2;
360
+ /**
361
+ * Format des buffers A–D : `rgba8` (défaut) | `rgba16f` | `rgba32f`.
362
+ * Attribut reflété `active-buffer-format` = format réellement utilisé.
363
+ */
364
+ @property({ attribute: "buffer-format" })
365
+ bufferFormat: ShaderBufferFormat | string = "rgba8";
366
+ /** Format actif après repli éventuel (lecture seule, reflect). */
367
+ @property({ attribute: "active-buffer-format", reflect: true })
368
+ activeBufferFormat: ShaderBufferFormat = "rgba8";
295
369
 
296
370
  /** Generic float uniforms `uParam0`…`uParam3` (no shader rebuild). */
297
371
  @property({ type: Number, attribute: "param0" }) param0 = 0;
@@ -364,6 +438,8 @@ export class SonicShader extends LitElement {
364
438
  private startTime = 0;
365
439
  private lastTime = 0;
366
440
  private frame = 0;
441
+ /** Incrémenté après chaque render (contrat SonicFrameSource). */
442
+ frameSeq = 0;
367
443
  private mouseVec: [number, number, number, number] = [0, 0, 0, 0];
368
444
  /** Cible pointeur (px drawing buffer, origine bas-gauche). */
369
445
  private mouseTarget: [number, number] = [0, 0];
@@ -389,6 +465,9 @@ export class SonicShader extends LitElement {
389
465
  private resizeObserver: ResizeObserver | null = null;
390
466
  private intersectionObserver: IntersectionObserver | null = null;
391
467
  private rebuildToken = 0;
468
+ /** Annule un `rebindChannels` en vol si rebuild / release intervient. */
469
+ private rebindToken = 0;
470
+ private frameConsumers = new FrameConsumerRegistry();
392
471
  private ready = false;
393
472
  private formPublisher: ReturnType<typeof PublisherManager.get> | null = null;
394
473
  private outPublisher: ReturnType<typeof PublisherManager.get> | null = null;
@@ -469,9 +548,11 @@ export class SonicShader extends LitElement {
469
548
  }
470
549
  this.pendingRevokeUrls = [];
471
550
  this.stopLoop();
551
+ this.frameConsumers.clear();
472
552
  this.resizeObserver?.disconnect();
473
553
  this.intersectionObserver?.disconnect();
474
554
  this.rebuildToken++;
555
+ this.rebindToken++;
475
556
  this.ready = false;
476
557
  this.releasing = true;
477
558
  this.runtime?.dispose(true); // libère le slot navigateur
@@ -495,7 +576,7 @@ export class SonicShader extends LitElement {
495
576
  this.bindOutPublisher();
496
577
  this.bindExtractPublisher();
497
578
  requestAnimationFrame(() => {
498
- if (this.inView || !this.releaseOffscreen) {
579
+ if (this.inView || !this.wantsReleaseOffscreen()) {
499
580
  void this.ensureRuntime();
500
581
  }
501
582
  });
@@ -503,44 +584,19 @@ export class SonicShader extends LitElement {
503
584
 
504
585
  protected updated(changed: Map<string, unknown>) {
505
586
  if (!this.runtime) return;
506
- const shaderKeys = [
507
- "image",
508
- "bufferA",
509
- "bufferB",
510
- "bufferC",
511
- "bufferD",
512
- "common",
513
- "channel0",
514
- "channel1",
515
- "channel2",
516
- "channel3",
517
- "imageCh0",
518
- "imageCh1",
519
- "imageCh2",
520
- "imageCh3",
521
- "bufferACh0",
522
- "bufferACh1",
523
- "bufferACh2",
524
- "bufferACh3",
525
- "bufferBCh0",
526
- "bufferBCh1",
527
- "bufferBCh2",
528
- "bufferBCh3",
529
- "bufferCCh0",
530
- "bufferCCh1",
531
- "bufferCCh2",
532
- "bufferCCh3",
533
- "bufferDCh0",
534
- "bufferDCh1",
535
- "bufferDCh2",
536
- "bufferDCh3",
537
- ];
538
- if (shaderKeys.some((k) => changed.has(k))) {
587
+ const programChanged = PROGRAM_KEYS.some((k) => changed.has(k));
588
+ const channelChanged = CHANNEL_KEYS.some((k) => changed.has(k));
589
+ if (programChanged) {
539
590
  void this.rebuild();
591
+ } else if (channelChanged) {
592
+ void this.rebindChannels();
540
593
  }
541
594
  if (changed.has("play")) {
542
595
  this.syncPlayback();
543
596
  }
597
+ if (changed.has("releaseOffscreen") || channelChanged) {
598
+ this.syncVisibility();
599
+ }
544
600
  if (
545
601
  changed.has("dataProvider") ||
546
602
  changed.has("outDataProvider") ||
@@ -686,6 +742,7 @@ export class SonicShader extends LitElement {
686
742
  */
687
743
  private releaseRuntime() {
688
744
  this.rebuildToken++;
745
+ this.rebindToken++;
689
746
  this.stopLoop();
690
747
  this.ready = false;
691
748
  this.releasing = true;
@@ -702,6 +759,27 @@ export class SonicShader extends LitElement {
702
759
  );
703
760
  }
704
761
 
762
+ /** Contrat SonicFrameSource — canvas lisible pour un autre sonic-shader. */
763
+ getFrameCanvas(): HTMLCanvasElement | null {
764
+ if (!this.ready || !this.runtime || this.runtime.isLost) return null;
765
+ return this.getCanvas();
766
+ }
767
+
768
+ /** Résout `#id` dans getRootNode() puis document ; clés live `__sonic_frame_`. */
769
+ private lookupElement(key: string): Element | null {
770
+ if (!key.startsWith("#")) return null;
771
+ const live = lookupLiveElement(key);
772
+ if (live) return live;
773
+ const id = key.slice(1);
774
+ if (!id || id.startsWith("__sonic_frame_")) return null;
775
+ const root = this.getRootNode();
776
+ if (root instanceof Document || root instanceof ShadowRoot) {
777
+ const el = root.getElementById(id);
778
+ if (el) return el;
779
+ }
780
+ return typeof document !== "undefined" ? document.getElementById(id) : null;
781
+ }
782
+
705
783
  private bindContextEvents() {
706
784
  const canvas = this.getCanvas();
707
785
  canvas?.addEventListener("webglcontextlost", this.onContextLost);
@@ -721,6 +799,7 @@ export class SonicShader extends LitElement {
721
799
  e.preventDefault();
722
800
  if (this.releasing) return;
723
801
  this.rebuildToken++;
802
+ this.rebindToken++;
724
803
  this.stopLoop();
725
804
  this.ready = false;
726
805
  this.runtime = null;
@@ -735,7 +814,8 @@ export class SonicShader extends LitElement {
735
814
  if (this.runtime && !this.runtime.isLost) return;
736
815
  this.runtime = null;
737
816
  await this.updateComplete;
738
- if (!this.isConnected || (!this.inView && this.releaseOffscreen)) return;
817
+ if (!this.isConnected || (!this.inView && this.wantsReleaseOffscreen()))
818
+ return;
739
819
 
740
820
  const canvas = this.getCanvas();
741
821
  if (!canvas) return;
@@ -743,6 +823,10 @@ export class SonicShader extends LitElement {
743
823
  try {
744
824
  this.errorMessage = "";
745
825
  this.runtime = new ShaderToyRuntime(canvas);
826
+ this.runtime.setElementResolver((key) => this.lookupElement(key));
827
+ this.activeBufferFormat = this.runtime.setBufferFormat(
828
+ parseBufferFormat(this.bufferFormat),
829
+ );
746
830
  this.bindContextEvents();
747
831
  this.syncSize();
748
832
  await this.rebuild();
@@ -752,7 +836,7 @@ export class SonicShader extends LitElement {
752
836
  }
753
837
 
754
838
  private syncVisibility() {
755
- if (!this.inView && this.releaseOffscreen) {
839
+ if (!this.inView && this.wantsReleaseOffscreen()) {
756
840
  this.releaseRuntime();
757
841
  return;
758
842
  }
@@ -827,8 +911,84 @@ export class SonicShader extends LitElement {
827
911
  return urls;
828
912
  }
829
913
 
914
+ private collectElementKeys(
915
+ channels: Map<ShaderPassId, PassChannels>,
916
+ ): string[] {
917
+ const keys: string[] = [];
918
+ for (const chs of channels.values()) {
919
+ for (const c of chs) {
920
+ if (c.kind === "element") keys.push(c.key);
921
+ }
922
+ }
923
+ return [...new Set(keys)];
924
+ }
925
+
926
+ /** Enregistre les producteurs live (`sonic-3d`…) pour play-offscreen auto. */
927
+ private syncFrameConsumers(channels: Map<ShaderPassId, PassChannels>) {
928
+ const els = this.collectElementKeys(channels).map((k) =>
929
+ this.lookupElement(k),
930
+ );
931
+ this.frameConsumers.sync(els);
932
+ }
933
+
934
+ /** Map canaux courante (tous les passes). */
935
+ private collectPassChannels(): Map<ShaderPassId, PassChannels> {
936
+ const channels = new Map<ShaderPassId, PassChannels>();
937
+ for (const pass of [
938
+ "bufferA",
939
+ "bufferB",
940
+ "bufferC",
941
+ "bufferD",
942
+ "image",
943
+ ] as ShaderPassId[]) {
944
+ channels.set(pass, this.buildChannels(pass));
945
+ }
946
+ return channels;
947
+ }
948
+
949
+ /** `true` si hors viewport on doit libérer textures/FBO. */
950
+ private wantsReleaseOffscreen(): boolean {
951
+ return shouldReleaseWhenOffscreen(
952
+ this.releaseOffscreen,
953
+ channelsUseSelf(this.collectPassChannels().values()),
954
+ );
955
+ }
956
+
957
+ /**
958
+ * Re-lie les canaux sans recompiler ni vider les FBO (Vague A Lot 2).
959
+ */
960
+ private async rebindChannels() {
961
+ if (!this.runtime || this.runtime.isLost || !this.image.trim()) return;
962
+ if (!this.ready) {
963
+ void this.rebuild();
964
+ return;
965
+ }
966
+ const token = ++this.rebindToken;
967
+ const channels = this.collectPassChannels();
968
+ try {
969
+ const urls = this.collectUrls(channels);
970
+ await this.runtime.ensureTextures(urls);
971
+ for (const key of this.collectElementKeys(channels)) {
972
+ this.runtime.ensureElementTexture(key);
973
+ }
974
+ if (token !== this.rebindToken || !this.runtime || this.runtime.isLost) {
975
+ return;
976
+ }
977
+ this.syncFrameConsumers(channels);
978
+ const aspectHint =
979
+ this.channel0.startsWith("#") ? "" : this.channel0 || urls[0];
980
+ this.applyAspectRatio(aspectHint);
981
+ this.runtime.setPassChannels(channels);
982
+ this.syncSize();
983
+ } catch (err) {
984
+ if (token !== this.rebindToken) return;
985
+ this.emitError(err);
986
+ }
987
+ }
988
+
830
989
  private async rebuild() {
831
990
  if (!this.runtime || this.runtime.isLost || !this.image.trim()) return;
991
+ this.rebindToken++;
832
992
  const token = ++this.rebuildToken;
833
993
  this.errorMessage = "";
834
994
  const sources: ShaderSources = {
@@ -839,24 +999,24 @@ export class SonicShader extends LitElement {
839
999
  bufferC: this.bufferC,
840
1000
  bufferD: this.bufferD,
841
1001
  };
842
- const channels = new Map<ShaderPassId, PassChannels>();
843
- for (const pass of [
844
- "bufferA",
845
- "bufferB",
846
- "bufferC",
847
- "bufferD",
848
- "image",
849
- ] as ShaderPassId[]) {
850
- channels.set(pass, this.buildChannels(pass));
851
- }
1002
+ const channels = this.collectPassChannels();
852
1003
  try {
853
1004
  this.syncSize();
1005
+ this.activeBufferFormat = this.runtime.setBufferFormat(
1006
+ parseBufferFormat(this.bufferFormat),
1007
+ );
854
1008
  const urls = this.collectUrls(channels);
855
1009
  await this.runtime.ensureTextures(urls);
1010
+ for (const key of this.collectElementKeys(channels)) {
1011
+ this.runtime.ensureElementTexture(key);
1012
+ }
856
1013
  if (token !== this.rebuildToken || !this.runtime || this.runtime.isLost) {
857
1014
  return;
858
1015
  }
859
- this.applyAspectRatio(this.channel0 || urls[0]);
1016
+ this.syncFrameConsumers(channels);
1017
+ const aspectHint =
1018
+ this.channel0.startsWith("#") ? "" : this.channel0 || urls[0];
1019
+ this.applyAspectRatio(aspectHint);
860
1020
  this.syncSize();
861
1021
  this.runtime.setSources(sources, channels);
862
1022
  if (token !== this.rebuildToken || !this.runtime || this.runtime.isLost) {
@@ -964,6 +1124,7 @@ export class SonicShader extends LitElement {
964
1124
  mouse: this.mouseVec,
965
1125
  params,
966
1126
  });
1127
+ this.frameSeq++;
967
1128
  this.maybePublishOut(now, time, params);
968
1129
  void this.maybeExtract(now, time);
969
1130
  }
@@ -1262,8 +1423,8 @@ export class SonicShader extends LitElement {
1262
1423
  : this.runtime.readBufferRect(pass, px.x, px.y, px.w, px.h);
1263
1424
  if (!rect) continue;
1264
1425
  // Copie : le buffer WebGL peut être réutilisé / invalidé ensuite
1265
- const rgba = new Uint8Array(rect.data);
1266
- const mean = meanRgba(rgba);
1426
+ const rgba = rgbaToUint8(rect.data);
1427
+ const mean = meanRgba(rect.data);
1267
1428
  const item: ShaderExtractItem = {
1268
1429
  id: roi.id || `${source}-${px.x}-${px.y}`,
1269
1430
  x: this.round3(rect.x / bufW),
@@ -1360,7 +1521,11 @@ export class SonicShader extends LitElement {
1360
1521
  if (typeof score === "number") item.score = this.round3(score);
1361
1522
  if (this.extractBitmaps) {
1362
1523
  const token = this.rebuildToken;
1363
- const url = await rgbaToBlobUrl(rect.data, rect.width, rect.height);
1524
+ const url = await rgbaToBlobUrl(
1525
+ rgbaToUint8(rect.data),
1526
+ rect.width,
1527
+ rect.height,
1528
+ );
1364
1529
  if (token !== this.rebuildToken || !this.isConnected) {
1365
1530
  if (url) revokeMediaUrls([url]);
1366
1531
  return null;
@@ -1531,10 +1696,12 @@ export class SonicShader extends LitElement {
1531
1696
  }
1532
1697
  }
1533
1698
 
1534
- export { parseChannelSource, ShaderToyRuntime } from "./gl-runtime";
1699
+ export { parseChannelSource, ShaderToyRuntime, shouldUploadElementFrame, resolveBufferGlFormat, resetBufferFormatWarnFlag } from "./gl-runtime";
1535
1700
  export { buildFragmentSource } from "./shadertoy-prelude";
1536
1701
  export type {
1537
1702
  ChannelSource,
1703
+ ReleaseOffscreen,
1704
+ ShaderBufferFormat,
1538
1705
  ShaderExtractItem,
1539
1706
  ShaderExtractOutput,
1540
1707
  ShaderExtractPass,
@@ -1548,17 +1715,27 @@ export type {
1548
1715
  SonicShaderConfig,
1549
1716
  SonicShaderOutput,
1550
1717
  } from "./types";
1551
- export type { SonicMediaRef } from "@supersoniks/concorde/core/utils/mediaRef";
1718
+ export type {
1719
+ SonicMediaRef,
1720
+ SonicFrameSource,
1721
+ } from "@supersoniks/concorde/core/utils/mediaRef";
1552
1722
  export {
1553
1723
  toMediaUrl,
1554
1724
  cloneMediaRef,
1725
+ isFrameSource,
1726
+ mediaElementKey,
1555
1727
  } from "@supersoniks/concorde/core/utils/mediaRef";
1556
1728
  export {
1729
+ channelsUseSelf,
1557
1730
  findPeaks,
1558
1731
  meanRgba,
1732
+ parseBufferFormat,
1559
1733
  parseExtractPass,
1560
1734
  parseHoverParam,
1735
+ parseReleaseOffscreen,
1561
1736
  roiToPixels,
1737
+ rgbaToUint8,
1738
+ shouldReleaseWhenOffscreen,
1562
1739
  ShaderCompileError,
1563
1740
  } from "./types";
1564
1741
 
@@ -1,6 +1,14 @@
1
- /** ShaderToy-compatible GLSL ES 3.0 prelude (fragment). User code is inserted before main(). */
2
- export const SHADERTOY_FRAGMENT_PRELUDE = `#version 300 es
3
- precision highp float;
1
+ /**
2
+ * ShaderToy-compatible GLSL ES 3.0 prelude (fragment body, sans `#version`).
3
+ * - `sonic-shader` préfixe `#version 300 es` via `buildFragmentSource`.
4
+ * - `sonic-3d` post passe le corps à Three `RawShaderMaterial` (GLSL3).
5
+ */
6
+
7
+ export const GLSL_VERSION_300 = `#version 300 es
8
+ `;
9
+
10
+ /** Corps des uniforms / out (sans version). */
11
+ export const SHADERTOY_FRAGMENT_BODY = `precision highp float;
4
12
  precision highp int;
5
13
  precision mediump sampler2D;
6
14
 
@@ -22,11 +30,26 @@ uniform float uParam0;
22
30
  uniform float uParam1;
23
31
  uniform float uParam2;
24
32
  uniform float uParam3;
33
+ /** Near / far caméra (0 dans sonic-shader standalone). */
34
+ uniform float uNear;
35
+ uniform float uFar;
25
36
 
26
37
  out vec4 sonicFragColor;
27
38
 
39
+ /** Profondeur linéaire monde depuis une DepthTexture (0–1). */
40
+ float sonicLinearDepth(float d) {
41
+ float z = d * 2.0 - 1.0;
42
+ float n = max(uNear, 1e-4);
43
+ float f = max(uFar, n + 1e-4);
44
+ return (2.0 * n * f) / (f + n - z * (f - n));
45
+ }
46
+
28
47
  `;
29
48
 
49
+ /** @deprecated Préférer SHADERTOY_FRAGMENT_BODY + buildFragmentSource. */
50
+ export const SHADERTOY_FRAGMENT_PRELUDE =
51
+ GLSL_VERSION_300 + SHADERTOY_FRAGMENT_BODY;
52
+
30
53
  export const SHADERTOY_FRAGMENT_EPILOGUE = `
31
54
  void main() {
32
55
  mainImage(sonicFragColor, gl_FragCoord.xy);
@@ -41,12 +64,33 @@ void main() {
41
64
  }
42
65
  `;
43
66
 
44
- export function buildFragmentSource(common: string, userCode: string): string {
67
+ /** Vertex Three RawShaderMaterial GLSL3 (fullscreen triangle via PlaneGeometry 2×2). */
68
+ export const THREE_FULLSCREEN_VERTEX = `precision highp float;
69
+ in vec3 position;
70
+ void main() {
71
+ gl_Position = vec4(position.xy, 0.0, 1.0);
72
+ }
73
+ `;
74
+
75
+ export function buildFragmentBody(common: string, userCode: string): string {
45
76
  return (
46
- SHADERTOY_FRAGMENT_PRELUDE +
77
+ SHADERTOY_FRAGMENT_BODY +
47
78
  (common ? common + "\n" : "") +
48
79
  userCode +
49
80
  "\n" +
50
81
  SHADERTOY_FRAGMENT_EPILOGUE
51
82
  );
52
83
  }
84
+
85
+ /** Source fragment WebGL2 native (`sonic-shader`). */
86
+ export function buildFragmentSource(common: string, userCode: string): string {
87
+ return GLSL_VERSION_300 + buildFragmentBody(common, userCode);
88
+ }
89
+
90
+ /** Source fragment pour Three RawShaderMaterial (GLSL3, sans #version). */
91
+ export function buildThreeFragmentSource(
92
+ common: string,
93
+ userCode: string,
94
+ ): string {
95
+ return buildFragmentBody(common, userCode);
96
+ }