@flighthq/effects-gl 0.3.0-next.906.07cea63 → 0.3.0

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 (278) hide show
  1. package/dist/contract.d.ts +3 -2
  2. package/dist/contract.d.ts.map +1 -1
  3. package/dist/contract.js +3 -2
  4. package/dist/contract.js.map +1 -1
  5. package/dist/enableGlRenderEffectGuards.d.ts +5 -0
  6. package/dist/enableGlRenderEffectGuards.d.ts.map +1 -0
  7. package/dist/enableGlRenderEffectGuards.js +43 -0
  8. package/dist/enableGlRenderEffectGuards.js.map +1 -0
  9. package/dist/glBevelEffect.d.ts +1 -0
  10. package/dist/glBevelEffect.d.ts.map +1 -1
  11. package/dist/glBevelEffect.js +7 -3
  12. package/dist/glBevelEffect.js.map +1 -1
  13. package/dist/glBlendEffect.d.ts +2 -0
  14. package/dist/glBlendEffect.d.ts.map +1 -1
  15. package/dist/glBlendEffect.js +13 -2
  16. package/dist/glBlendEffect.js.map +1 -1
  17. package/dist/glBloomEffect.d.ts +1 -0
  18. package/dist/glBloomEffect.d.ts.map +1 -1
  19. package/dist/glBloomEffect.js +4 -0
  20. package/dist/glBloomEffect.js.map +1 -1
  21. package/dist/glBlurEffect.d.ts +7 -1
  22. package/dist/glBlurEffect.d.ts.map +1 -1
  23. package/dist/glBlurEffect.js +34 -6
  24. package/dist/glBlurEffect.js.map +1 -1
  25. package/dist/glBokehDepthOfFieldEffect.d.ts +1 -0
  26. package/dist/glBokehDepthOfFieldEffect.d.ts.map +1 -1
  27. package/dist/glBokehDepthOfFieldEffect.js +4 -0
  28. package/dist/glBokehDepthOfFieldEffect.js.map +1 -1
  29. package/dist/glCameraMotionBlurEffect.d.ts +1 -0
  30. package/dist/glCameraMotionBlurEffect.d.ts.map +1 -1
  31. package/dist/glCameraMotionBlurEffect.js +4 -0
  32. package/dist/glCameraMotionBlurEffect.js.map +1 -1
  33. package/dist/glChromaticAberrationEffect.d.ts +1 -0
  34. package/dist/glChromaticAberrationEffect.d.ts.map +1 -1
  35. package/dist/glChromaticAberrationEffect.js +4 -0
  36. package/dist/glChromaticAberrationEffect.js.map +1 -1
  37. package/dist/glCompositeEffect.d.ts +1 -0
  38. package/dist/glCompositeEffect.d.ts.map +1 -1
  39. package/dist/glCompositeEffect.js +4 -0
  40. package/dist/glCompositeEffect.js.map +1 -1
  41. package/dist/glContactShadowsEffect.d.ts +5 -0
  42. package/dist/glContactShadowsEffect.d.ts.map +1 -0
  43. package/dist/glContactShadowsEffect.js +20 -0
  44. package/dist/glContactShadowsEffect.js.map +1 -0
  45. package/dist/glConvolutionEffect.d.ts +1 -0
  46. package/dist/glConvolutionEffect.d.ts.map +1 -1
  47. package/dist/glConvolutionEffect.js +4 -0
  48. package/dist/glConvolutionEffect.js.map +1 -1
  49. package/dist/glCrtEffect.d.ts +1 -0
  50. package/dist/glCrtEffect.d.ts.map +1 -1
  51. package/dist/glCrtEffect.js +4 -0
  52. package/dist/glCrtEffect.js.map +1 -1
  53. package/dist/glCustomShaderEffect.d.ts +1 -0
  54. package/dist/glCustomShaderEffect.d.ts.map +1 -1
  55. package/dist/glCustomShaderEffect.js +4 -0
  56. package/dist/glCustomShaderEffect.js.map +1 -1
  57. package/dist/glDirectionalBlurEffect.d.ts +1 -0
  58. package/dist/glDirectionalBlurEffect.d.ts.map +1 -1
  59. package/dist/glDirectionalBlurEffect.js +4 -0
  60. package/dist/glDirectionalBlurEffect.js.map +1 -1
  61. package/dist/glDisplacementEffect.d.ts +1 -0
  62. package/dist/glDisplacementEffect.d.ts.map +1 -1
  63. package/dist/glDisplacementEffect.js +4 -0
  64. package/dist/glDisplacementEffect.js.map +1 -1
  65. package/dist/glDitherEffect.d.ts +1 -0
  66. package/dist/glDitherEffect.d.ts.map +1 -1
  67. package/dist/glDitherEffect.js +4 -0
  68. package/dist/glDitherEffect.js.map +1 -1
  69. package/dist/glDropShadowEffect.d.ts +1 -0
  70. package/dist/glDropShadowEffect.d.ts.map +1 -1
  71. package/dist/glDropShadowEffect.js +4 -0
  72. package/dist/glDropShadowEffect.js.map +1 -1
  73. package/dist/glEffectBlitShader.js +6 -6
  74. package/dist/glEffectBlitShader.js.map +1 -1
  75. package/dist/glEffectBoxBlur.js +2 -2
  76. package/dist/glEffectBoxBlur.js.map +1 -1
  77. package/dist/glEffectProgramCache.js +5 -5
  78. package/dist/glEffectProgramCache.js.map +1 -1
  79. package/dist/glEffectTintShader.js +4 -4
  80. package/dist/glEffectTintShader.js.map +1 -1
  81. package/dist/glFilmGrainEffect.d.ts +1 -0
  82. package/dist/glFilmGrainEffect.d.ts.map +1 -1
  83. package/dist/glFilmGrainEffect.js +4 -0
  84. package/dist/glFilmGrainEffect.js.map +1 -1
  85. package/dist/glFxaaEffect.d.ts +1 -0
  86. package/dist/glFxaaEffect.d.ts.map +1 -1
  87. package/dist/glFxaaEffect.js +4 -0
  88. package/dist/glFxaaEffect.js.map +1 -1
  89. package/dist/glGlitchEffect.d.ts +1 -0
  90. package/dist/glGlitchEffect.d.ts.map +1 -1
  91. package/dist/glGlitchEffect.js +4 -0
  92. package/dist/glGlitchEffect.js.map +1 -1
  93. package/dist/glGodRaysEffect.d.ts +1 -0
  94. package/dist/glGodRaysEffect.d.ts.map +1 -1
  95. package/dist/glGodRaysEffect.js +4 -0
  96. package/dist/glGodRaysEffect.js.map +1 -1
  97. package/dist/glGradientBevelEffect.d.ts +1 -0
  98. package/dist/glGradientBevelEffect.d.ts.map +1 -1
  99. package/dist/glGradientBevelEffect.js +8 -4
  100. package/dist/glGradientBevelEffect.js.map +1 -1
  101. package/dist/glGradientGlowEffect.d.ts +1 -0
  102. package/dist/glGradientGlowEffect.d.ts.map +1 -1
  103. package/dist/glGradientGlowEffect.js +6 -2
  104. package/dist/glGradientGlowEffect.js.map +1 -1
  105. package/dist/glHalftoneEffect.d.ts +1 -0
  106. package/dist/glHalftoneEffect.d.ts.map +1 -1
  107. package/dist/glHalftoneEffect.js +4 -0
  108. package/dist/glHalftoneEffect.js.map +1 -1
  109. package/dist/glInnerGlowEffect.d.ts +1 -0
  110. package/dist/glInnerGlowEffect.d.ts.map +1 -1
  111. package/dist/glInnerGlowEffect.js +6 -2
  112. package/dist/glInnerGlowEffect.js.map +1 -1
  113. package/dist/glInnerShadowEffect.d.ts +1 -0
  114. package/dist/glInnerShadowEffect.d.ts.map +1 -1
  115. package/dist/glInnerShadowEffect.js +6 -2
  116. package/dist/glInnerShadowEffect.js.map +1 -1
  117. package/dist/glKuwaharaEffect.d.ts +1 -0
  118. package/dist/glKuwaharaEffect.d.ts.map +1 -1
  119. package/dist/glKuwaharaEffect.js +4 -0
  120. package/dist/glKuwaharaEffect.js.map +1 -1
  121. package/dist/glLensDirtEffect.d.ts +1 -0
  122. package/dist/glLensDirtEffect.d.ts.map +1 -1
  123. package/dist/glLensDirtEffect.js +4 -0
  124. package/dist/glLensDirtEffect.js.map +1 -1
  125. package/dist/glLensDistortionEffect.d.ts +1 -0
  126. package/dist/glLensDistortionEffect.d.ts.map +1 -1
  127. package/dist/glLensDistortionEffect.js +4 -0
  128. package/dist/glLensDistortionEffect.js.map +1 -1
  129. package/dist/glLensFlareEffect.d.ts +1 -0
  130. package/dist/glLensFlareEffect.d.ts.map +1 -1
  131. package/dist/glLensFlareEffect.js +4 -0
  132. package/dist/glLensFlareEffect.js.map +1 -1
  133. package/dist/glMedianEffect.d.ts +1 -0
  134. package/dist/glMedianEffect.d.ts.map +1 -1
  135. package/dist/glMedianEffect.js +4 -0
  136. package/dist/glMedianEffect.js.map +1 -1
  137. package/dist/glMotionBlurEffect.d.ts +1 -0
  138. package/dist/glMotionBlurEffect.d.ts.map +1 -1
  139. package/dist/glMotionBlurEffect.js +4 -0
  140. package/dist/glMotionBlurEffect.js.map +1 -1
  141. package/dist/glOuterGlowEffect.d.ts +1 -0
  142. package/dist/glOuterGlowEffect.d.ts.map +1 -1
  143. package/dist/glOuterGlowEffect.js +4 -0
  144. package/dist/glOuterGlowEffect.js.map +1 -1
  145. package/dist/glOutlineEffect.d.ts +1 -0
  146. package/dist/glOutlineEffect.d.ts.map +1 -1
  147. package/dist/glOutlineEffect.js +4 -0
  148. package/dist/glOutlineEffect.js.map +1 -1
  149. package/dist/glPixelateEffect.d.ts +1 -0
  150. package/dist/glPixelateEffect.d.ts.map +1 -1
  151. package/dist/glPixelateEffect.js +4 -0
  152. package/dist/glPixelateEffect.js.map +1 -1
  153. package/dist/glPosterizeEffect.d.ts +1 -0
  154. package/dist/glPosterizeEffect.d.ts.map +1 -1
  155. package/dist/glPosterizeEffect.js +4 -0
  156. package/dist/glPosterizeEffect.js.map +1 -1
  157. package/dist/glRadialBlurEffect.d.ts +1 -0
  158. package/dist/glRadialBlurEffect.d.ts.map +1 -1
  159. package/dist/glRadialBlurEffect.js +4 -0
  160. package/dist/glRadialBlurEffect.js.map +1 -1
  161. package/dist/glRenderEffectPipeline.d.ts +2 -2
  162. package/dist/glRenderEffectPipeline.d.ts.map +1 -1
  163. package/dist/glRenderEffectPipeline.js +14 -7
  164. package/dist/glRenderEffectPipeline.js.map +1 -1
  165. package/dist/glRenderEffectRegistry.d.ts.map +1 -1
  166. package/dist/glRenderEffectRegistry.js +8 -10
  167. package/dist/glRenderEffectRegistry.js.map +1 -1
  168. package/dist/glRenderTextureEffect.d.ts +5 -0
  169. package/dist/glRenderTextureEffect.d.ts.map +1 -0
  170. package/dist/glRenderTextureEffect.js +83 -0
  171. package/dist/glRenderTextureEffect.js.map +1 -0
  172. package/dist/glScanlinesEffect.d.ts +1 -0
  173. package/dist/glScanlinesEffect.d.ts.map +1 -1
  174. package/dist/glScanlinesEffect.js +4 -0
  175. package/dist/glScanlinesEffect.js.map +1 -1
  176. package/dist/glScreenSpaceFogEffect.d.ts +1 -0
  177. package/dist/glScreenSpaceFogEffect.d.ts.map +1 -1
  178. package/dist/glScreenSpaceFogEffect.js +4 -0
  179. package/dist/glScreenSpaceFogEffect.js.map +1 -1
  180. package/dist/glSharpenEffect.d.ts +1 -0
  181. package/dist/glSharpenEffect.d.ts.map +1 -1
  182. package/dist/glSharpenEffect.js +4 -0
  183. package/dist/glSharpenEffect.js.map +1 -1
  184. package/dist/glSketchEffect.d.ts +1 -0
  185. package/dist/glSketchEffect.d.ts.map +1 -1
  186. package/dist/glSketchEffect.js +4 -0
  187. package/dist/glSketchEffect.js.map +1 -1
  188. package/dist/glSmaaEffect.d.ts +1 -0
  189. package/dist/glSmaaEffect.d.ts.map +1 -1
  190. package/dist/glSmaaEffect.js +4 -0
  191. package/dist/glSmaaEffect.js.map +1 -1
  192. package/dist/glSsaoEffect.d.ts +1 -0
  193. package/dist/glSsaoEffect.d.ts.map +1 -1
  194. package/dist/glSsaoEffect.js +4 -0
  195. package/dist/glSsaoEffect.js.map +1 -1
  196. package/dist/glTiltShiftEffect.d.ts +1 -0
  197. package/dist/glTiltShiftEffect.d.ts.map +1 -1
  198. package/dist/glTiltShiftEffect.js +4 -0
  199. package/dist/glTiltShiftEffect.js.map +1 -1
  200. package/dist/glToneMapEffect.d.ts +1 -0
  201. package/dist/glToneMapEffect.d.ts.map +1 -1
  202. package/dist/glToneMapEffect.js +4 -0
  203. package/dist/glToneMapEffect.js.map +1 -1
  204. package/dist/glVignetteEffect.d.ts +1 -0
  205. package/dist/glVignetteEffect.d.ts.map +1 -1
  206. package/dist/glVignetteEffect.js +4 -0
  207. package/dist/glVignetteEffect.js.map +1 -1
  208. package/dist/glWhiteBalanceEffect.d.ts +1 -0
  209. package/dist/glWhiteBalanceEffect.d.ts.map +1 -1
  210. package/dist/glWhiteBalanceEffect.js +4 -0
  211. package/dist/glWhiteBalanceEffect.js.map +1 -1
  212. package/dist/index.d.ts +1 -1
  213. package/dist/index.d.ts.map +1 -1
  214. package/dist/index.js +1 -1
  215. package/dist/index.js.map +1 -1
  216. package/package.json +9 -6
  217. package/src/enableGlRenderEffectGuards.test.ts +156 -0
  218. package/src/glBevelEffect.test.ts +7 -1
  219. package/src/glBlendEffect.test.ts +26 -0
  220. package/src/glBloomEffect.test.ts +7 -1
  221. package/src/glBlurEffect.test.ts +69 -1
  222. package/src/glBokehDepthOfFieldEffect.test.ts +11 -1
  223. package/src/glCameraMotionBlurEffect.test.ts +11 -1
  224. package/src/glChromaticAberrationEffect.test.ts +7 -0
  225. package/src/glCompositeEffect.test.ts +7 -0
  226. package/src/glContactShadowsEffect.test.ts +43 -0
  227. package/src/glConvolutionEffect.test.ts +11 -1
  228. package/src/glCrtEffect.test.ts +7 -1
  229. package/src/glCustomShaderEffect.test.ts +7 -0
  230. package/src/glDirectionalBlurEffect.test.ts +11 -1
  231. package/src/glDisplacementEffect.test.ts +11 -1
  232. package/src/glDitherEffect.test.ts +7 -1
  233. package/src/glDropShadowEffect.test.ts +11 -9
  234. package/src/glEffectBoxBlur.test.ts +0 -5
  235. package/src/glFilmGrainEffect.test.ts +11 -1
  236. package/src/glFxaaEffect.test.ts +7 -1
  237. package/src/glGlitchEffect.test.ts +7 -1
  238. package/src/glGodRaysEffect.test.ts +7 -1
  239. package/src/glGradientBevelEffect.test.ts +11 -1
  240. package/src/glGradientGlowEffect.test.ts +11 -1
  241. package/src/glHalftoneEffect.test.ts +7 -1
  242. package/src/glInnerGlowEffect.test.ts +11 -9
  243. package/src/glInnerShadowEffect.test.ts +11 -9
  244. package/src/glKuwaharaEffect.test.ts +7 -1
  245. package/src/glLensDirtEffect.test.ts +7 -1
  246. package/src/glLensDistortionEffect.test.ts +11 -1
  247. package/src/glLensFlareEffect.test.ts +11 -1
  248. package/src/glMedianEffect.test.ts +7 -1
  249. package/src/glMotionBlurEffect.test.ts +11 -1
  250. package/src/glOuterGlowEffect.test.ts +11 -9
  251. package/src/glOutlineEffect.test.ts +7 -1
  252. package/src/glPixelateEffect.test.ts +7 -1
  253. package/src/glPosterizeEffect.test.ts +11 -1
  254. package/src/glRadialBlurEffect.test.ts +11 -1
  255. package/src/glRenderEffectPipeline.test.ts +15 -0
  256. package/src/glRenderEffectRegistry.test.ts +24 -14
  257. package/src/glRenderTextureEffect.test.ts +246 -0
  258. package/src/glScanlinesEffect.test.ts +11 -1
  259. package/src/glScreenSpaceFogEffect.test.ts +11 -1
  260. package/src/glSharpenEffect.test.ts +7 -1
  261. package/src/glSketchEffect.test.ts +7 -1
  262. package/src/glSmaaEffect.test.ts +7 -1
  263. package/src/glSpatialEffectRegistration.test.ts +64 -0
  264. package/src/glSsaoEffect.test.ts +7 -1
  265. package/src/glTiltShiftEffect.test.ts +11 -1
  266. package/src/glToneMapEffect.test.ts +7 -1
  267. package/src/glVignetteEffect.test.ts +7 -1
  268. package/src/glWhiteBalanceEffect.test.ts +11 -1
  269. package/dist/glSsrEffect.d.ts +0 -4
  270. package/dist/glSsrEffect.d.ts.map +0 -1
  271. package/dist/glSsrEffect.js +0 -24
  272. package/dist/glSsrEffect.js.map +0 -1
  273. package/dist/glTaaEffect.d.ts +0 -4
  274. package/dist/glTaaEffect.d.ts.map +0 -1
  275. package/dist/glTaaEffect.js +0 -22
  276. package/dist/glTaaEffect.js.map +0 -1
  277. package/src/glSsrEffect.test.ts +0 -13
  278. package/src/glTaaEffect.test.ts +0 -13
@@ -0,0 +1,156 @@
1
+ import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log/contract';
2
+ import {
3
+ acquireGlRenderTexture,
4
+ createGlRenderState,
5
+ createGlRenderTexturePool,
6
+ writeGlRenderTextureTarget,
7
+ } from '@flighthq/render-gl/contract';
8
+ import type { GlRenderEffectRunner, GlRenderState, LogEntry, RenderEffect } from '@flighthq/types/contract';
9
+
10
+ import {
11
+ areGlRenderEffectGuardsEnabled,
12
+ disableGlRenderEffectGuards,
13
+ enableGlRenderEffectGuards,
14
+ } from './enableGlRenderEffectGuards';
15
+ import { registerGlRenderEffect } from './glRenderEffectRegistry';
16
+ import { applyGlRenderEffectsToRenderTexture } from './glRenderTextureEffect';
17
+
18
+ describe('areGlRenderEffectGuardsEnabled', () => {
19
+ it('reports whether diagnostics were installed for the state', () => {
20
+ const state = createState();
21
+ expect(areGlRenderEffectGuardsEnabled(state)).toBe(false);
22
+
23
+ enableGlRenderEffectGuards(state);
24
+ expect(areGlRenderEffectGuardsEnabled(state)).toBe(true);
25
+
26
+ disableGlRenderEffectGuards(state);
27
+ expect(areGlRenderEffectGuardsEnabled(state)).toBe(false);
28
+ });
29
+ });
30
+
31
+ describe('disableGlRenderEffectGuards', () => {
32
+ it('stops reporting, leaving the silent sentinel silent again', () => {
33
+ const state = createState();
34
+ enableGlRenderEffectGuards(state);
35
+ disableGlRenderEffectGuards(state);
36
+
37
+ const entries = captureLog(() => {
38
+ expect(applyChain(state, ['test.disabled-kind'])).toBe(false);
39
+ });
40
+
41
+ expect(entries.length).toBe(0);
42
+ });
43
+ });
44
+
45
+ describe('enableGlRenderEffectGuards', () => {
46
+ // logOnce suppresses a key for the whole PROCESS, so a fired key can never fire again in a later
47
+ // test. Both the fire and the silence assertions live in this one test, in order, and every test in
48
+ // this file uses distinct effect kinds so no two share a key.
49
+ it('WARNS that an unregistered chain returned false without writing dest, then stays quiet', () => {
50
+ const state = createState();
51
+ enableGlRenderEffectGuards(state);
52
+
53
+ const entries = captureLog(() => {
54
+ expect(applyChain(state, ['test.unregistered-a'])).toBe(false);
55
+ });
56
+ expect(entries.length).toBe(1);
57
+ // The consequence is the point: dest is never written, so the caller samples a stale texture.
58
+ expect(messageOf(entries[0])).toContain('NEVER WRITTEN');
59
+ expect(messageOf(entries[0])).toContain('registerGlRenderEffect');
60
+
61
+ // Same observation again is suppressed — the miss recurs every frame and must not flood.
62
+ const repeat = captureLog(() => {
63
+ expect(applyChain(state, ['test.unregistered-a'])).toBe(false);
64
+ });
65
+ expect(repeat.length).toBe(0);
66
+ });
67
+
68
+ it('WARNS that a partially registered chain silently DROPPED the effects it could not run', () => {
69
+ const state = createState();
70
+ registerGlRenderEffect(state, 'test.registered-b', noopRunner);
71
+ enableGlRenderEffectGuards(state);
72
+
73
+ const entries = captureLog(() => {
74
+ // It succeeds — which is exactly why this one is invisible without a crumb.
75
+ expect(applyChain(state, ['test.registered-b', 'test.unregistered-b'])).toBe(true);
76
+ });
77
+
78
+ expect(entries.length).toBe(1);
79
+ expect(messageOf(entries[0])).toContain('SKIPPED');
80
+ expect(messageOf(entries[0])).toContain('test.unregistered-b');
81
+ });
82
+
83
+ it('WARNS when a failed chain leaves a previously published destination stale', () => {
84
+ const state = createState();
85
+ enableGlRenderEffectGuards(state);
86
+
87
+ const entries = captureLog(() => {
88
+ expect(applyChain(state, ['test.stale-destination'], true)).toBe(false);
89
+ });
90
+
91
+ expect(entries.length).toBe(1);
92
+ expect(messageOf(entries[0])).toContain('STALE DESTINATION');
93
+ expect(messageOf(entries[0])).toContain('handle the false return');
94
+ });
95
+
96
+ it('stays SILENT for an empty chain, which is a no-op the caller asked for rather than a miss', () => {
97
+ const state = createState();
98
+ enableGlRenderEffectGuards(state);
99
+
100
+ const entries = captureLog(() => {
101
+ expect(applyChain(state, [])).toBe(false);
102
+ });
103
+
104
+ // Reporting this would train readers to ignore the crumb.
105
+ expect(entries.length).toBe(0);
106
+ });
107
+
108
+ it('stays SILENT when every requested effect has a runner', () => {
109
+ const state = createState();
110
+ registerGlRenderEffect(state, 'test.registered-c', noopRunner);
111
+ enableGlRenderEffectGuards(state);
112
+
113
+ const entries = captureLog(() => {
114
+ expect(applyChain(state, ['test.registered-c'])).toBe(true);
115
+ });
116
+
117
+ expect(entries.length).toBe(0);
118
+ });
119
+ });
120
+
121
+ function applyChain(state: GlRenderState, kinds: readonly string[], publishDestination = false): boolean {
122
+ const pool = createGlRenderTexturePool();
123
+ const source = acquireGlRenderTexture(state, pool, { width: 8, height: 8 });
124
+ const dest = acquireGlRenderTexture(state, pool, { width: 8, height: 8 });
125
+ const scratch = acquireGlRenderTexture(state, pool, { width: 8, height: 8 });
126
+ // Realize the source so `source-unavailable` is not what is being measured here.
127
+ writeGlRenderTextureTarget(state, source, () => {});
128
+ if (publishDestination) writeGlRenderTextureTarget(state, dest, () => {});
129
+ const effects = kinds.map((kind) => ({ kind }) as unknown as Readonly<RenderEffect>);
130
+ return applyGlRenderEffectsToRenderTexture(state, pool, source, dest, scratch, effects);
131
+ }
132
+
133
+ function captureLog(run: () => void): readonly LogEntry[] {
134
+ const sink = createMemoryLogSink(8);
135
+ addLogSink(sink.sink);
136
+ try {
137
+ run();
138
+ return getMemoryLogSinkEntries(sink);
139
+ } finally {
140
+ removeLogSink(sink.sink);
141
+ }
142
+ }
143
+
144
+ function createState(): GlRenderState {
145
+ const canvas = document.createElement('canvas');
146
+ canvas.width = 32;
147
+ canvas.height = 24;
148
+ return createGlRenderState(canvas);
149
+ }
150
+
151
+ function messageOf(entry: Readonly<LogEntry>): string {
152
+ const data = entry.data;
153
+ return typeof data === 'string' ? data : String(data.message);
154
+ }
155
+
156
+ const noopRunner: GlRenderEffectRunner = () => {};
@@ -1,4 +1,4 @@
1
- import { applyBevelEffectToGl, defaultGlBevelEffectRunner } from './glBevelEffect';
1
+ import { applyBevelEffectToGl, defaultGlBevelEffectRunner, registerGlBevelEffect } from './glBevelEffect';
2
2
 
3
3
  describe('applyBevelEffectToGl', () => {
4
4
  it('is a function', () => {
@@ -11,3 +11,9 @@ describe('defaultGlBevelEffectRunner', () => {
11
11
  expect(typeof defaultGlBevelEffectRunner).toBe('function');
12
12
  });
13
13
  });
14
+
15
+ describe('registerGlBevelEffect', () => {
16
+ it('is a separately importable registration primitive', () => {
17
+ expect(registerGlBevelEffect).toBeTypeOf('function');
18
+ });
19
+ });
@@ -4,8 +4,10 @@ import { AdvancedBlendMode } from '@flighthq/types/contract';
4
4
  import {
5
5
  applyBlendEffectToGl,
6
6
  defaultGlBlendEffectRunner,
7
+ BLEND_FRAGMENT_SRC,
7
8
  getBlendEffectModeIndex,
8
9
  getGlBlendEffectBackdrop,
10
+ registerGlBlendEffect,
9
11
  registerGlBlendEffectBackdrop,
10
12
  unregisterGlBlendEffectBackdrop,
11
13
  } from './glBlendEffect';
@@ -62,6 +64,24 @@ describe('getBlendEffectModeIndex', () => {
62
64
  });
63
65
  });
64
66
 
67
+ // The index table and the shader's branch switch are two hand-maintained lists that must agree; a mode
68
+ // assigned an index with no matching branch silently renders Normal passthrough instead of the blend the
69
+ // caller asked for. Reading the module source is crude, but it is the only thing that actually pins the
70
+ // two together — and it covers every mode rather than whichever one was added last.
71
+ describe('getBlendEffectModeIndex shader lockstep', () => {
72
+ const source = BLEND_FRAGMENT_SRC;
73
+
74
+ it('has a shader branch for every index the table assigns', () => {
75
+ for (const mode of Object.values(AdvancedBlendMode)) {
76
+ const index = getBlendEffectModeIndex(mode);
77
+ expect(index).toBeGreaterThanOrEqual(0);
78
+ expect(source, `{mode} maps to index ${index} with no shader branch`.replace('{mode}', mode)).toContain(
79
+ `mode == ${index}`,
80
+ );
81
+ }
82
+ });
83
+ });
84
+
65
85
  describe('getGlBlendEffectBackdrop', () => {
66
86
  it('returns null for a null key', () => {
67
87
  expect(getGlBlendEffectBackdrop(makeState(), null)).toBeNull();
@@ -86,6 +106,12 @@ describe('getGlBlendEffectBackdrop', () => {
86
106
  });
87
107
  });
88
108
 
109
+ describe('registerGlBlendEffect', () => {
110
+ it('is a separately importable registration primitive', () => {
111
+ expect(registerGlBlendEffect).toBeTypeOf('function');
112
+ });
113
+ });
114
+
89
115
  describe('registerGlBlendEffectBackdrop', () => {
90
116
  it('is last-write-wins for a given key', () => {
91
117
  const state = makeState();
@@ -1,4 +1,4 @@
1
- import { applyBloomEffectToGl, defaultGlBloomEffectRunner } from './glBloomEffect';
1
+ import { applyBloomEffectToGl, defaultGlBloomEffectRunner, registerGlBloomEffect } from './glBloomEffect';
2
2
 
3
3
  describe('applyBloomEffectToGl', () => {
4
4
  it('is a function', () => {
@@ -11,3 +11,9 @@ describe('defaultGlBloomEffectRunner', () => {
11
11
  expect(typeof defaultGlBloomEffectRunner).toBe('function');
12
12
  });
13
13
  });
14
+
15
+ describe('registerGlBloomEffect', () => {
16
+ it('is a separately importable registration primitive', () => {
17
+ expect(registerGlBloomEffect).toBeTypeOf('function');
18
+ });
19
+ });
@@ -1,4 +1,24 @@
1
- import { applyBlurEffectToGl, applyGaussianBlurToGl, defaultGlBlurEffectRunner } from './glBlurEffect';
1
+ import {
2
+ acquireGlRenderTexture,
3
+ createGlRenderState,
4
+ createGlRenderTexturePool,
5
+ isGlRenderTextureReady,
6
+ writeGlRenderTextureTarget,
7
+ } from '@flighthq/render-gl/contract';
8
+
9
+ import {
10
+ applyBlurEffectToGl,
11
+ applyBlurEffectToGlRenderTextures,
12
+ applyGaussianBlurToGl,
13
+ applyGaussianBlurToGlRenderTextures,
14
+ defaultGlBlurEffectRunner,
15
+ registerGlBlurEffect,
16
+ } from './glBlurEffect';
17
+ import { getGlRenderEffectRunner } from './glRenderEffectRegistry';
18
+
19
+ function createGlState() {
20
+ return { state: createGlRenderState(document.createElement('canvas')) };
21
+ }
2
22
 
3
23
  describe('applyBlurEffectToGl', () => {
4
24
  it('is a function', () => {
@@ -6,14 +26,62 @@ describe('applyBlurEffectToGl', () => {
6
26
  });
7
27
  });
8
28
 
29
+ describe('applyBlurEffectToGlRenderTextures', () => {
30
+ it('publishes destination and scratch handles after applying the blur descriptor', () => {
31
+ const { state } = createGlState();
32
+ const pool = createGlRenderTexturePool();
33
+ const source = acquireGlRenderTexture(state, pool, { width: 16, height: 12 });
34
+ const dest = acquireGlRenderTexture(state, pool, { width: 16, height: 12 });
35
+ const scratch = acquireGlRenderTexture(state, pool, { width: 16, height: 12 });
36
+ writeGlRenderTextureTarget(state, source, () => {});
37
+
38
+ expect(
39
+ applyBlurEffectToGlRenderTextures(state, source, dest, scratch, {
40
+ blurX: 2,
41
+ blurY: 3,
42
+ kind: 'BlurEffect',
43
+ }),
44
+ ).toBe(true);
45
+ expect(isGlRenderTextureReady(state, dest)).toBe(true);
46
+ expect(isGlRenderTextureReady(state, scratch)).toBe(true);
47
+ });
48
+ });
49
+
9
50
  describe('applyGaussianBlurToGl', () => {
10
51
  it('is a function', () => {
11
52
  expect(typeof applyGaussianBlurToGl).toBe('function');
12
53
  });
13
54
  });
14
55
 
56
+ describe('applyGaussianBlurToGlRenderTextures', () => {
57
+ it('publishes destination and scratch handles after the two Gaussian target passes', () => {
58
+ const { state } = createGlState();
59
+ const pool = createGlRenderTexturePool();
60
+ const source = acquireGlRenderTexture(state, pool, { width: 16, height: 12 });
61
+ const dest = acquireGlRenderTexture(state, pool, { width: 16, height: 12 });
62
+ const scratch = acquireGlRenderTexture(state, pool, { width: 16, height: 12 });
63
+ writeGlRenderTextureTarget(state, source, () => {});
64
+
65
+ expect(applyGaussianBlurToGlRenderTextures(state, source, dest, scratch, { blurX: 2, blurY: 3 })).toBe(true);
66
+ expect(isGlRenderTextureReady(state, dest)).toBe(true);
67
+ expect(isGlRenderTextureReady(state, scratch)).toBe(true);
68
+ });
69
+ });
70
+
15
71
  describe('defaultGlBlurEffectRunner', () => {
16
72
  it('is a function', () => {
17
73
  expect(typeof defaultGlBlurEffectRunner).toBe('function');
18
74
  });
19
75
  });
76
+
77
+ describe('registerGlBlurEffect', () => {
78
+ it('registers the default blur runner on only the supplied state', () => {
79
+ const { state } = createGlState();
80
+ const { state: other } = createGlState();
81
+
82
+ registerGlBlurEffect(state);
83
+
84
+ expect(getGlRenderEffectRunner(state, 'BlurEffect')).toBe(defaultGlBlurEffectRunner);
85
+ expect(getGlRenderEffectRunner(other, 'BlurEffect')).toBeNull();
86
+ });
87
+ });
@@ -1,4 +1,8 @@
1
- import { applyBokehDepthOfFieldEffectToGl, defaultGlBokehDepthOfFieldEffectRunner } from './glBokehDepthOfFieldEffect';
1
+ import {
2
+ applyBokehDepthOfFieldEffectToGl,
3
+ defaultGlBokehDepthOfFieldEffectRunner,
4
+ registerGlBokehDepthOfFieldEffect,
5
+ } from './glBokehDepthOfFieldEffect';
2
6
 
3
7
  describe('applyBokehDepthOfFieldEffectToGl', () => {
4
8
  it('is a function', () => {
@@ -11,3 +15,9 @@ describe('defaultGlBokehDepthOfFieldEffectRunner', () => {
11
15
  expect(typeof defaultGlBokehDepthOfFieldEffectRunner).toBe('function');
12
16
  });
13
17
  });
18
+
19
+ describe('registerGlBokehDepthOfFieldEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlBokehDepthOfFieldEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -1,4 +1,8 @@
1
- import { applyCameraMotionBlurEffectToGl, defaultGlCameraMotionBlurEffectRunner } from './glCameraMotionBlurEffect';
1
+ import {
2
+ applyCameraMotionBlurEffectToGl,
3
+ defaultGlCameraMotionBlurEffectRunner,
4
+ registerGlCameraMotionBlurEffect,
5
+ } from './glCameraMotionBlurEffect';
2
6
 
3
7
  describe('applyCameraMotionBlurEffectToGl', () => {
4
8
  it('is a function', () => {
@@ -11,3 +15,9 @@ describe('defaultGlCameraMotionBlurEffectRunner', () => {
11
15
  expect(typeof defaultGlCameraMotionBlurEffectRunner).toBe('function');
12
16
  });
13
17
  });
18
+
19
+ describe('registerGlCameraMotionBlurEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlCameraMotionBlurEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  applyChromaticAberrationEffectToGl,
3
3
  defaultGlChromaticAberrationEffectRunner,
4
+ registerGlChromaticAberrationEffect,
4
5
  } from './glChromaticAberrationEffect';
5
6
 
6
7
  describe('applyChromaticAberrationEffectToGl', () => {
@@ -14,3 +15,9 @@ describe('defaultGlChromaticAberrationEffectRunner', () => {
14
15
  expect(typeof defaultGlChromaticAberrationEffectRunner).toBe('function');
15
16
  });
16
17
  });
18
+
19
+ describe('registerGlChromaticAberrationEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlChromaticAberrationEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -4,6 +4,7 @@ import {
4
4
  applyCompositeEffectToGl,
5
5
  defaultGlCompositeEffectRunner,
6
6
  getCompositeEffectOperatorIndex,
7
+ registerGlCompositeEffect,
7
8
  } from './glCompositeEffect';
8
9
 
9
10
  // The pure operator→index mapping is asserted here; the compile/draw path is exercised pixel-for-pixel by
@@ -37,3 +38,9 @@ describe('getCompositeEffectOperatorIndex', () => {
37
38
  expect(getCompositeEffectOperatorIndex('acme.custom')).toBe(0);
38
39
  });
39
40
  });
41
+
42
+ describe('registerGlCompositeEffect', () => {
43
+ it('is a separately importable registration primitive', () => {
44
+ expect(registerGlCompositeEffect).toBeTypeOf('function');
45
+ });
46
+ });
@@ -0,0 +1,43 @@
1
+ vi.mock('./glSsaoEffect', () => ({ applySsaoEffectToGl: vi.fn() }));
2
+
3
+ import { createGlRenderState } from '@flighthq/render-gl/contract';
4
+
5
+ import {
6
+ applyContactShadowsEffectToGl,
7
+ defaultGlContactShadowsEffectRunner,
8
+ registerGlContactShadowsEffect,
9
+ } from './glContactShadowsEffect';
10
+ import { getGlRenderEffectRunner } from './glRenderEffectRegistry';
11
+ import { applySsaoEffectToGl } from './glSsaoEffect';
12
+
13
+ describe('applyContactShadowsEffectToGl', () => {
14
+ it('maps the contact descriptor into the shared local-occlusion realization', () => {
15
+ applyContactShadowsEffectToGl({} as never, {} as never, {} as never, {
16
+ distance: 3,
17
+ kind: 'ContactShadowsEffect',
18
+ opacity: 0.75,
19
+ samples: 24,
20
+ });
21
+
22
+ expect(applySsaoEffectToGl).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.anything(), {
23
+ intensity: 0.75,
24
+ kind: 'SsaoEffect',
25
+ radius: 3,
26
+ samples: 24,
27
+ });
28
+ });
29
+ });
30
+
31
+ describe('defaultGlContactShadowsEffectRunner', () => {
32
+ it('is a function', () => {
33
+ expect(typeof defaultGlContactShadowsEffectRunner).toBe('function');
34
+ });
35
+ });
36
+
37
+ describe('registerGlContactShadowsEffect', () => {
38
+ it('installs the contact-shadows runner on the supplied state', () => {
39
+ const state = createGlRenderState(document.createElement('canvas'));
40
+ registerGlContactShadowsEffect(state);
41
+ expect(getGlRenderEffectRunner(state, 'ContactShadowsEffect')).toBe(defaultGlContactShadowsEffectRunner);
42
+ });
43
+ });
@@ -1,4 +1,8 @@
1
- import { applyConvolutionEffectToGl, defaultGlConvolutionEffectRunner } from './glConvolutionEffect';
1
+ import {
2
+ applyConvolutionEffectToGl,
3
+ defaultGlConvolutionEffectRunner,
4
+ registerGlConvolutionEffect,
5
+ } from './glConvolutionEffect';
2
6
 
3
7
  describe('applyConvolutionEffectToGl', () => {
4
8
  it('is a function', () => {
@@ -11,3 +15,9 @@ describe('defaultGlConvolutionEffectRunner', () => {
11
15
  expect(typeof defaultGlConvolutionEffectRunner).toBe('function');
12
16
  });
13
17
  });
18
+
19
+ describe('registerGlConvolutionEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlConvolutionEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -1,4 +1,4 @@
1
- import { applyCrtEffectToGl, defaultGlCrtEffectRunner } from './glCrtEffect';
1
+ import { applyCrtEffectToGl, defaultGlCrtEffectRunner, registerGlCrtEffect } from './glCrtEffect';
2
2
 
3
3
  describe('applyCrtEffectToGl', () => {
4
4
  it('is a function', () => {
@@ -11,3 +11,9 @@ describe('defaultGlCrtEffectRunner', () => {
11
11
  expect(typeof defaultGlCrtEffectRunner).toBe('function');
12
12
  });
13
13
  });
14
+
15
+ describe('registerGlCrtEffect', () => {
16
+ it('is a separately importable registration primitive', () => {
17
+ expect(registerGlCrtEffect).toBeTypeOf('function');
18
+ });
19
+ });
@@ -5,6 +5,7 @@ import {
5
5
  defaultGlCustomShaderEffectRunner,
6
6
  getGlCustomShaderSource,
7
7
  registerGlCustomShaderSource,
8
+ registerGlCustomShaderEffect,
8
9
  } from './glCustomShaderEffect';
9
10
 
10
11
  // The source registry is a plain WeakMap keyed by the render state, with no GL calls, so a bare
@@ -51,6 +52,12 @@ describe('getGlCustomShaderSource', () => {
51
52
  });
52
53
  });
53
54
 
55
+ describe('registerGlCustomShaderEffect', () => {
56
+ it('is a separately importable registration primitive', () => {
57
+ expect(registerGlCustomShaderEffect).toBeTypeOf('function');
58
+ });
59
+ });
60
+
54
61
  describe('registerGlCustomShaderSource', () => {
55
62
  it('registers a source that getGlCustomShaderSource then resolves', () => {
56
63
  const state = makeState();
@@ -1,4 +1,8 @@
1
- import { applyDirectionalBlurEffectToGl, defaultGlDirectionalBlurEffectRunner } from './glDirectionalBlurEffect';
1
+ import {
2
+ applyDirectionalBlurEffectToGl,
3
+ defaultGlDirectionalBlurEffectRunner,
4
+ registerGlDirectionalBlurEffect,
5
+ } from './glDirectionalBlurEffect';
2
6
 
3
7
  describe('applyDirectionalBlurEffectToGl', () => {
4
8
  it('is a function', () => {
@@ -11,3 +15,9 @@ describe('defaultGlDirectionalBlurEffectRunner', () => {
11
15
  expect(typeof defaultGlDirectionalBlurEffectRunner).toBe('function');
12
16
  });
13
17
  });
18
+
19
+ describe('registerGlDirectionalBlurEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlDirectionalBlurEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -1,4 +1,8 @@
1
- import { applyDisplacementEffectToGl, defaultGlDisplacementEffectRunner } from './glDisplacementEffect';
1
+ import {
2
+ applyDisplacementEffectToGl,
3
+ defaultGlDisplacementEffectRunner,
4
+ registerGlDisplacementEffect,
5
+ } from './glDisplacementEffect';
2
6
 
3
7
  describe('applyDisplacementEffectToGl', () => {
4
8
  it('is a function', () => {
@@ -11,3 +15,9 @@ describe('defaultGlDisplacementEffectRunner', () => {
11
15
  expect(typeof defaultGlDisplacementEffectRunner).toBe('function');
12
16
  });
13
17
  });
18
+
19
+ describe('registerGlDisplacementEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlDisplacementEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -1,4 +1,4 @@
1
- import { applyDitherEffectToGl, defaultGlDitherEffectRunner } from './glDitherEffect';
1
+ import { applyDitherEffectToGl, defaultGlDitherEffectRunner, registerGlDitherEffect } from './glDitherEffect';
2
2
 
3
3
  describe('applyDitherEffectToGl', () => {
4
4
  it('is a function', () => {
@@ -11,3 +11,9 @@ describe('defaultGlDitherEffectRunner', () => {
11
11
  expect(typeof defaultGlDitherEffectRunner).toBe('function');
12
12
  });
13
13
  });
14
+
15
+ describe('registerGlDitherEffect', () => {
16
+ it('is a separately importable registration primitive', () => {
17
+ expect(registerGlDitherEffect).toBeTypeOf('function');
18
+ });
19
+ });
@@ -29,7 +29,11 @@ vi.mock('./glEffectTintShader', () => ({
29
29
  applyGlEffectTintPass: vi.fn(),
30
30
  }));
31
31
 
32
- import { applyDropShadowEffectToGl, defaultGlDropShadowEffectRunner } from './glDropShadowEffect';
32
+ import {
33
+ applyDropShadowEffectToGl,
34
+ defaultGlDropShadowEffectRunner,
35
+ registerGlDropShadowEffect,
36
+ } from './glDropShadowEffect';
33
37
  import { applyGlEffectBlitPass, applyGlEffectErasePass } from './glEffectBlitShader';
34
38
 
35
39
  describe('applyDropShadowEffectToGl', () => {
@@ -84,14 +88,6 @@ beforeEach(() => {
84
88
  vi.clearAllMocks();
85
89
  });
86
90
 
87
- afterAll(() => {
88
- vi.doUnmock('@flighthq/render-gl');
89
- vi.doUnmock('./glEffectBlitShader');
90
- vi.doUnmock('./glEffectBoxBlur');
91
- vi.doUnmock('./glEffectTintShader');
92
- vi.resetModules();
93
- });
94
-
95
91
  function createState(): never {
96
92
  return { gl: {} } as never;
97
93
  }
@@ -103,3 +99,9 @@ function createPool(): never {
103
99
  function createTarget(id: string): never {
104
100
  return { id, width: 32, height: 16, format: 'rgba8', texture: {} } as never;
105
101
  }
102
+
103
+ describe('registerGlDropShadowEffect', () => {
104
+ it('is a separately importable registration primitive', () => {
105
+ expect(registerGlDropShadowEffect).toBeTypeOf('function');
106
+ });
107
+ });
@@ -43,11 +43,6 @@ beforeEach(() => {
43
43
  vi.clearAllMocks();
44
44
  });
45
45
 
46
- afterAll(() => {
47
- vi.doUnmock('@flighthq/render-gl');
48
- vi.resetModules();
49
- });
50
-
51
46
  function createState(): never {
52
47
  return { gl: { getUniformLocation: vi.fn((_program, name) => name) } } as never;
53
48
  }
@@ -1,4 +1,8 @@
1
- import { applyFilmGrainEffectToGl, defaultGlFilmGrainEffectRunner } from './glFilmGrainEffect';
1
+ import {
2
+ applyFilmGrainEffectToGl,
3
+ defaultGlFilmGrainEffectRunner,
4
+ registerGlFilmGrainEffect,
5
+ } from './glFilmGrainEffect';
2
6
 
3
7
  describe('applyFilmGrainEffectToGl', () => {
4
8
  it('is a function', () => {
@@ -11,3 +15,9 @@ describe('defaultGlFilmGrainEffectRunner', () => {
11
15
  expect(typeof defaultGlFilmGrainEffectRunner).toBe('function');
12
16
  });
13
17
  });
18
+
19
+ describe('registerGlFilmGrainEffect', () => {
20
+ it('is a separately importable registration primitive', () => {
21
+ expect(registerGlFilmGrainEffect).toBeTypeOf('function');
22
+ });
23
+ });
@@ -1,4 +1,4 @@
1
- import { applyFxaaEffectToGl, defaultGlFxaaEffectRunner } from './glFxaaEffect';
1
+ import { applyFxaaEffectToGl, defaultGlFxaaEffectRunner, registerGlFxaaEffect } from './glFxaaEffect';
2
2
 
3
3
  describe('applyFxaaEffectToGl', () => {
4
4
  it('is a function', () => {
@@ -11,3 +11,9 @@ describe('defaultGlFxaaEffectRunner', () => {
11
11
  expect(typeof defaultGlFxaaEffectRunner).toBe('function');
12
12
  });
13
13
  });
14
+
15
+ describe('registerGlFxaaEffect', () => {
16
+ it('is a separately importable registration primitive', () => {
17
+ expect(registerGlFxaaEffect).toBeTypeOf('function');
18
+ });
19
+ });
@@ -1,4 +1,4 @@
1
- import { applyGlitchEffectToGl, defaultGlGlitchEffectRunner } from './glGlitchEffect';
1
+ import { applyGlitchEffectToGl, defaultGlGlitchEffectRunner, registerGlGlitchEffect } from './glGlitchEffect';
2
2
 
3
3
  describe('applyGlitchEffectToGl', () => {
4
4
  it('is a function', () => {
@@ -11,3 +11,9 @@ describe('defaultGlGlitchEffectRunner', () => {
11
11
  expect(typeof defaultGlGlitchEffectRunner).toBe('function');
12
12
  });
13
13
  });
14
+
15
+ describe('registerGlGlitchEffect', () => {
16
+ it('is a separately importable registration primitive', () => {
17
+ expect(registerGlGlitchEffect).toBeTypeOf('function');
18
+ });
19
+ });