@solidtv/renderer 1.5.6 → 1.6.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 (97) hide show
  1. package/dist/src/core/CoreNode.js +2 -2
  2. package/dist/src/core/CoreNode.js.map +1 -1
  3. package/dist/src/core/Stage.js +9 -1
  4. package/dist/src/core/Stage.js.map +1 -1
  5. package/dist/src/core/lib/WebGlContextWrapper.d.ts +17 -1
  6. package/dist/src/core/lib/WebGlContextWrapper.js +21 -0
  7. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  8. package/dist/src/core/lib/colorParser.d.ts +4 -1
  9. package/dist/src/core/lib/colorParser.js +16 -6
  10. package/dist/src/core/lib/colorParser.js.map +1 -1
  11. package/dist/src/core/platforms/web/WebPlatform.js +60 -50
  12. package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
  13. package/dist/src/core/renderers/canvas/CanvasRenderer.d.ts +4 -0
  14. package/dist/src/core/renderers/canvas/CanvasRenderer.js +28 -13
  15. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  16. package/dist/src/core/renderers/canvas/CanvasShaderNode.js +1 -0
  17. package/dist/src/core/renderers/canvas/CanvasShaderNode.js.map +1 -1
  18. package/dist/src/core/renderers/webgl/SdfRenderOp.js +2 -2
  19. package/dist/src/core/renderers/webgl/SdfRenderOp.js.map +1 -1
  20. package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +43 -0
  21. package/dist/src/core/renderers/webgl/WebGlRenderer.js +109 -10
  22. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  23. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +21 -13
  24. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  25. package/dist/src/core/renderers/webgl/internal/ShaderUtils.d.ts +8 -3
  26. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js +12 -11
  27. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js.map +1 -1
  28. package/dist/src/core/shaders/canvas/Rounded.js +3 -3
  29. package/dist/src/core/shaders/canvas/Rounded.js.map +1 -1
  30. package/dist/src/core/shaders/canvas/RoundedWithShadow.js +3 -3
  31. package/dist/src/core/shaders/canvas/RoundedWithShadow.js.map +1 -1
  32. package/dist/src/core/shaders/canvas/utils/render.js +3 -3
  33. package/dist/src/core/shaders/canvas/utils/render.js.map +1 -1
  34. package/dist/src/core/shaders/webgl/Border.js +12 -11
  35. package/dist/src/core/shaders/webgl/Border.js.map +1 -1
  36. package/dist/src/core/shaders/webgl/HolePunch.js +12 -5
  37. package/dist/src/core/shaders/webgl/HolePunch.js.map +1 -1
  38. package/dist/src/core/shaders/webgl/LinearGradient.js +3 -19
  39. package/dist/src/core/shaders/webgl/LinearGradient.js.map +1 -1
  40. package/dist/src/core/shaders/webgl/RadialGradient.js +3 -20
  41. package/dist/src/core/shaders/webgl/RadialGradient.js.map +1 -1
  42. package/dist/src/core/shaders/webgl/RadialProgress.js +15 -23
  43. package/dist/src/core/shaders/webgl/RadialProgress.js.map +1 -1
  44. package/dist/src/core/shaders/webgl/RoundedWithBorder.js +35 -32
  45. package/dist/src/core/shaders/webgl/RoundedWithBorder.js.map +1 -1
  46. package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js +58 -45
  47. package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js.map +1 -1
  48. package/dist/src/core/shaders/webgl/RoundedWithShadow.js +15 -13
  49. package/dist/src/core/shaders/webgl/RoundedWithShadow.js.map +1 -1
  50. package/dist/src/core/shaders/webgl/Shadow.js +4 -5
  51. package/dist/src/core/shaders/webgl/Shadow.js.map +1 -1
  52. package/dist/src/core/text-rendering/SdfTextRenderer.js +23 -10
  53. package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
  54. package/dist/src/core/text-rendering/TextLayoutEngine.js +14 -2
  55. package/dist/src/core/text-rendering/TextLayoutEngine.js.map +1 -1
  56. package/dist/src/utils.js +1 -1
  57. package/dist/src/utils.js.map +1 -1
  58. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  59. package/package.json +1 -1
  60. package/src/core/CoreNode.ts +2 -2
  61. package/src/core/Stage.renderListUpdate.test.ts +73 -0
  62. package/src/core/Stage.ts +11 -1
  63. package/src/core/lib/WebGlContextWrapper.test.ts +23 -1
  64. package/src/core/lib/WebGlContextWrapper.ts +23 -1
  65. package/src/core/lib/colorParser.test.ts +46 -0
  66. package/src/core/lib/colorParser.ts +17 -6
  67. package/src/core/platforms/web/WebPlatform.ts +63 -52
  68. package/src/core/renderers/canvas/CanvasRenderer.test.ts +230 -0
  69. package/src/core/renderers/canvas/CanvasRenderer.ts +29 -15
  70. package/src/core/renderers/canvas/CanvasShaderNode.test.ts +73 -0
  71. package/src/core/renderers/canvas/CanvasShaderNode.ts +1 -0
  72. package/src/core/renderers/webgl/SdfRenderOp.ts +2 -2
  73. package/src/core/renderers/webgl/WebGlRenderer.reuseRenderOp.test.ts +122 -0
  74. package/src/core/renderers/webgl/WebGlRenderer.sdfBuffer.test.ts +302 -0
  75. package/src/core/renderers/webgl/WebGlRenderer.ts +151 -11
  76. package/src/core/renderers/webgl/WebGlShaderProgram.reuseRenderOp.test.ts +150 -0
  77. package/src/core/renderers/webgl/WebGlShaderProgram.ts +27 -15
  78. package/src/core/renderers/webgl/WebGlShaderProgram.uniformDedup.test.ts +4 -2
  79. package/src/core/renderers/webgl/internal/ShaderUtils.test.ts +124 -0
  80. package/src/core/renderers/webgl/internal/ShaderUtils.ts +14 -13
  81. package/src/core/shaders/canvas/Rounded.ts +3 -3
  82. package/src/core/shaders/canvas/RoundedWithShadow.ts +3 -3
  83. package/src/core/shaders/canvas/utils/render.ts +3 -3
  84. package/src/core/shaders/webgl/Border.ts +12 -11
  85. package/src/core/shaders/webgl/HolePunch.ts +12 -5
  86. package/src/core/shaders/webgl/LinearGradient.ts +3 -19
  87. package/src/core/shaders/webgl/RadialGradient.ts +3 -20
  88. package/src/core/shaders/webgl/RadialProgress.ts +15 -23
  89. package/src/core/shaders/webgl/RoundedWithBorder.ts +35 -32
  90. package/src/core/shaders/webgl/RoundedWithBorderAndShadow.ts +58 -45
  91. package/src/core/shaders/webgl/RoundedWithShadow.ts +15 -13
  92. package/src/core/shaders/webgl/Shadow.ts +4 -5
  93. package/src/core/text-rendering/SdfTextRenderer.test.ts +177 -0
  94. package/src/core/text-rendering/SdfTextRenderer.ts +38 -10
  95. package/src/core/text-rendering/TextLayoutEngine.ts +15 -2
  96. package/src/core/text-rendering/tests/TextLayoutEngine.test.ts +99 -0
  97. package/src/utils.ts +1 -1
@@ -133,6 +133,183 @@ describe('SdfTextRenderer layout cache', () => {
133
133
  });
134
134
  });
135
135
 
136
+ describe('SdfTextRenderer renderQuads cache dispatch', () => {
137
+ type FakeRenderer = {
138
+ stage: unknown;
139
+ sdfBufferIdx: number;
140
+ fSdfBuffer: Float32Array;
141
+ addSdfQuads: ReturnType<typeof vi.fn>;
142
+ addSdfCachedQuads: ReturnType<typeof vi.fn>;
143
+ addSdfTranslatedQuads: ReturnType<typeof vi.fn>;
144
+ };
145
+
146
+ const GLYPHS = 2;
147
+
148
+ const makeRenderer = (): FakeRenderer => {
149
+ const renderer: FakeRenderer = {
150
+ stage: {
151
+ options: { textLayoutCacheSize: 10 },
152
+ shManager: {
153
+ registerShaderType: vi.fn(),
154
+ createShader: vi.fn(() => ({})),
155
+ },
156
+ },
157
+ sdfBufferIdx: 0,
158
+ fSdfBuffer: new Float32Array(1024),
159
+ // The miss path snapshots [startIdx, endIdx) of fSdfBuffer, so the
160
+ // fake must advance sdfBufferIdx like the real writer does.
161
+ addSdfQuads: vi.fn(() => {
162
+ renderer.sdfBufferIdx += GLYPHS * 24;
163
+ }),
164
+ addSdfCachedQuads: vi.fn(),
165
+ addSdfTranslatedQuads: vi.fn(),
166
+ };
167
+ return renderer;
168
+ };
169
+
170
+ const makeLayout = () => ({
171
+ glyphs: new Float32Array(GLYPHS * 8),
172
+ glyphCount: GLYPHS,
173
+ fontScale: 1,
174
+ distanceRange: 4,
175
+ width: 100,
176
+ height: 50,
177
+ });
178
+
179
+ const makeCache = () => ({
180
+ vertices: null as Float32Array | null,
181
+ glyphCount: 0,
182
+ color: 0,
183
+ alpha: -1,
184
+ transform: new Float32Array(6),
185
+ layoutRef: null as unknown,
186
+ });
187
+
188
+ const makeRenderProps = (
189
+ cache: ReturnType<typeof makeCache>,
190
+ transform: number[],
191
+ ) => ({
192
+ fontFamily: 'Test',
193
+ fontSize: 42,
194
+ color: 0xffffffff,
195
+ offsetY: 0,
196
+ worldAlpha: 1,
197
+ globalTransform: new Float32Array(transform),
198
+ clippingRect: { x: 0, y: 0, w: 0, h: 0, valid: false },
199
+ width: 100,
200
+ height: 50,
201
+ parentHasRenderTexture: false,
202
+ framebufferDimensions: null,
203
+ sdfCache: cache,
204
+ });
205
+
206
+ const IDENTITY = [1, 0, 0, 0, 1, 0, 0, 0, 1];
207
+ const TRANSLATED = [1, 0, 0, 0, 1, 0, 120, -40, 1];
208
+ const SCALED = [2, 0, 0, 0, 2, 0, 0, 0, 1];
209
+
210
+ const renderQuads = (
211
+ renderer: FakeRenderer,
212
+ layout: ReturnType<typeof makeLayout>,
213
+ renderProps: ReturnType<typeof makeRenderProps>,
214
+ ) =>
215
+ SdfTextRenderer.renderQuads(
216
+ renderer as never,
217
+ layout as never,
218
+ null as never,
219
+ renderProps as never,
220
+ );
221
+
222
+ const setupWithAtlas = () => {
223
+ initRenderer(10);
224
+ vi.mocked(SdfFontHandler.getAtlas).mockReturnValue({
225
+ ctxTexture: {},
226
+ } as never);
227
+ };
228
+
229
+ it('takes the miss path first and snapshots the cache', () => {
230
+ setupWithAtlas();
231
+ const renderer = makeRenderer();
232
+ const layout = makeLayout();
233
+ const cache = makeCache();
234
+
235
+ renderQuads(renderer, layout, makeRenderProps(cache, IDENTITY));
236
+
237
+ expect(renderer.addSdfQuads.mock.calls.length).toBe(1);
238
+ expect(cache.vertices).not.toBeNull();
239
+ expect(cache.layoutRef).toBe(layout);
240
+ expect(cache.glyphCount).toBe(GLYPHS);
241
+ });
242
+
243
+ it('takes the mem-copy path when nothing changed', () => {
244
+ setupWithAtlas();
245
+ const renderer = makeRenderer();
246
+ const layout = makeLayout();
247
+ const cache = makeCache();
248
+
249
+ renderQuads(renderer, layout, makeRenderProps(cache, IDENTITY));
250
+ renderQuads(renderer, layout, makeRenderProps(cache, IDENTITY));
251
+
252
+ expect(renderer.addSdfQuads.mock.calls.length).toBe(1);
253
+ expect(renderer.addSdfCachedQuads.mock.calls.length).toBe(1);
254
+ expect(renderer.addSdfTranslatedQuads.mock.calls.length).toBe(0);
255
+ });
256
+
257
+ it('takes the translation path when only tx/ty changed, keeping the cache base', () => {
258
+ setupWithAtlas();
259
+ const renderer = makeRenderer();
260
+ const layout = makeLayout();
261
+ const cache = makeCache();
262
+
263
+ renderQuads(renderer, layout, makeRenderProps(cache, IDENTITY));
264
+ const baseVertices = cache.vertices;
265
+
266
+ renderQuads(renderer, layout, makeRenderProps(cache, TRANSLATED));
267
+
268
+ expect(renderer.addSdfQuads.mock.calls.length).toBe(1);
269
+ expect(renderer.addSdfCachedQuads.mock.calls.length).toBe(0);
270
+ const calls = renderer.addSdfTranslatedQuads.mock.calls;
271
+ expect(calls.length).toBe(1);
272
+ // (cachedVertices, glyphCount, dx, dy, ...)
273
+ expect(calls[0]![0]).toBe(baseVertices);
274
+ expect(calls[0]![1]).toBe(GLYPHS);
275
+ expect(calls[0]![2]).toBe(120);
276
+ expect(calls[0]![3]).toBe(-40);
277
+ // The scroll path must not re-snapshot: base transform and vertices stay
278
+ expect(cache.vertices).toBe(baseVertices);
279
+ expect(cache.transform[4]).toBe(0);
280
+ expect(cache.transform[5]).toBe(0);
281
+ });
282
+
283
+ it('falls back to the miss path when scale or rotation changed', () => {
284
+ setupWithAtlas();
285
+ const renderer = makeRenderer();
286
+ const layout = makeLayout();
287
+ const cache = makeCache();
288
+
289
+ renderQuads(renderer, layout, makeRenderProps(cache, IDENTITY));
290
+ renderQuads(renderer, layout, makeRenderProps(cache, SCALED));
291
+
292
+ expect(renderer.addSdfQuads.mock.calls.length).toBe(2);
293
+ expect(renderer.addSdfTranslatedQuads.mock.calls.length).toBe(0);
294
+ });
295
+
296
+ it('falls back to the miss path when color or alpha changed', () => {
297
+ setupWithAtlas();
298
+ const renderer = makeRenderer();
299
+ const layout = makeLayout();
300
+ const cache = makeCache();
301
+
302
+ renderQuads(renderer, layout, makeRenderProps(cache, IDENTITY));
303
+
304
+ const faded = makeRenderProps(cache, TRANSLATED);
305
+ faded.worldAlpha = 0.5;
306
+ renderQuads(renderer, layout, faded);
307
+
308
+ expect(renderer.addSdfQuads.mock.calls.length).toBe(2);
309
+ expect(renderer.addSdfTranslatedQuads.mock.calls.length).toBe(0);
310
+ });
311
+ });
312
+
136
313
  describe('SdfTextRenderer lazy shader compile', () => {
137
314
  it('registers the SDF shader at init but defers compilation', () => {
138
315
  const shManager = {
@@ -150,11 +150,14 @@ const addQuads = (_layout?: TextLayout): Float32Array | null => {
150
150
  /**
151
151
  * Submit SDF glyphs to the renderer's shared batched buffer.
152
152
  *
153
- * Two paths:
154
- * 1. **Cache hit** — layout, transform, color, and alpha haven't changed.
155
- * The cached pre-transformed Float32Array is mem-copied directly into the
156
- * shared SDF buffer (no per-glyph matrix math).
157
- * 2. **Cache miss** — re-computes per-glyph world-space vertices via
153
+ * Three paths:
154
+ * 1. **Exact cache hit** — layout, transform, color, and alpha haven't
155
+ * changed. The cached pre-transformed Float32Array is mem-copied directly
156
+ * into the shared SDF buffer (no per-glyph matrix math).
157
+ * 2. **Translation hit** — only tx/ty changed (the scroll path). The cached
158
+ * vertices are copied with the position delta applied; the cache keeps its
159
+ * original base so nothing is re-snapshotted.
160
+ * 3. **Cache miss** — re-computes per-glyph world-space vertices via
158
161
  * `addSdfQuads`, then snapshots the result into the cache.
159
162
  */
160
163
  const renderQuads = (
@@ -176,7 +179,7 @@ const renderQuads = (
176
179
  // Compiles on the first real SDF draw; cheap memoized lookup thereafter.
177
180
  const shader = getSdfShader(webGlRenderer.stage);
178
181
 
179
- // --- Cache-hit fast path ------------------------------------------------
182
+ // --- Cache-hit fast paths -----------------------------------------------
180
183
  if (cache !== undefined && cache.vertices !== null) {
181
184
  const ct = cache.transform;
182
185
  const t = renderProps.globalTransform;
@@ -187,13 +190,38 @@ const renderQuads = (
187
190
  ct[0] === t[0] &&
188
191
  ct[1] === t[1] &&
189
192
  ct[2] === t[3] &&
190
- ct[3] === t[4] &&
191
- ct[4] === t[6] &&
192
- ct[5] === t[7]
193
+ ct[3] === t[4]
193
194
  ) {
194
- webGlRenderer.addSdfCachedQuads(
195
+ const dx = t[6]! - ct[4]!;
196
+ const dy = t[7]! - ct[5]!;
197
+
198
+ if (dx === 0 && dy === 0) {
199
+ // Fully static: mem-copy the cached vertices as-is.
200
+ webGlRenderer.addSdfCachedQuads(
201
+ cache.vertices,
202
+ cache.glyphCount,
203
+ ctxTexture,
204
+ renderProps.clippingRect,
205
+ renderProps.worldAlpha,
206
+ layout.width,
207
+ layout.height,
208
+ renderProps.parentHasRenderTexture,
209
+ renderProps.framebufferDimensions,
210
+ shader,
211
+ );
212
+ return null;
213
+ }
214
+
215
+ // Pure translation (the scroll path): same glyphs, same scale/rotation,
216
+ // only tx/ty moved. Copy the cached vertices shifted by the delta from
217
+ // the cached base transform. The cache keeps its original base, so
218
+ // every frame recomputes from the same reference — no drift and no
219
+ // per-frame re-snapshot.
220
+ webGlRenderer.addSdfTranslatedQuads(
195
221
  cache.vertices,
196
222
  cache.glyphCount,
223
+ dx,
224
+ dy,
197
225
  ctxTexture,
198
226
  renderProps.clippingRect,
199
227
  renderProps.worldAlpha,
@@ -368,6 +368,13 @@ export const wrapLine = (
368
368
  ): WrappedLinesStruct => {
369
369
  const words = line.split(spaceRegex);
370
370
  const spaces = line.match(spaceRegex) || [];
371
+ // Leading whitespace splits into an empty token; fold it into the next
372
+ // word so it isn't dropped and re-applied after the first word instead.
373
+ if (words[0] === '' && words.length > 1) {
374
+ const leadingSpace = spaces.shift() ?? '';
375
+ words.shift();
376
+ words[0] = leadingSpace + (words[0] ?? '');
377
+ }
371
378
  const wrappedLines: TextLineStruct[] = [];
372
379
  let currentLine = '';
373
380
  let currentLineWidth = 0;
@@ -454,8 +461,14 @@ export const wrapLine = (
454
461
  continue;
455
462
  }
456
463
  const space = spaces[spaceIdx++] || '';
457
- // For width calculation, treat ZWSP as having 0 width but regular space functionality
458
- const effectiveSpaceWidth = space === '\u200B' ? 0 : spaceWidth;
464
+ // Single ' ' is the dominant separator \u2014 reuse the precomputed width.
465
+ // Multi-space/ZWSP runs collapse into one match; measure those for real.
466
+ const effectiveSpaceWidth =
467
+ space === ' '
468
+ ? spaceWidth
469
+ : space.length > 0
470
+ ? measureText(space, fontFamily, letterSpacing)
471
+ : 0;
459
472
  const totalWidth = currentLineWidth + effectiveSpaceWidth + wordWidth;
460
473
 
461
474
  if (totalWidth < maxWidth) {
@@ -248,6 +248,105 @@ describe('SDF Text Utils', () => {
248
248
  expect(result[0][0]?.[2]).toBe(true);
249
249
  expect(result[1]).toBe(0);
250
250
  });
251
+
252
+ it('should keep leading whitespace attached to the first word, not the word after it', () => {
253
+ // Regression test: a line starting with padding spaces (used e.g. to
254
+ // reserve room for an inline badge before the first word) used to have
255
+ // those spaces silently reassigned as the gap AFTER the first word.
256
+ const result = wrapLine(
257
+ testMeasureText,
258
+ ' hello world',
259
+ 'Arial',
260
+ 1000,
261
+ 0,
262
+ 10, // spaceWidth
263
+ '',
264
+ 0,
265
+ 'break-word',
266
+ 10,
267
+ );
268
+
269
+ const [lines] = result;
270
+ expect(lines).toHaveLength(1);
271
+ expect(lines[0]?.[0]).toBe(' hello world');
272
+ });
273
+
274
+ it('should count the full width of a multi-space separator when wrapping', () => {
275
+ // 'aa bb' really measures 60 (6 chars * 10). Counting the separator
276
+ // as a single spaceWidth (50) used to keep it on one line past maxWidth.
277
+ const result = wrapLine(
278
+ testMeasureText,
279
+ 'aa bb',
280
+ 'Arial',
281
+ 55, // maxWidth — below the real 60, above the buggy 50
282
+ 0,
283
+ 10, // spaceWidth
284
+ '',
285
+ 0,
286
+ 'break-word',
287
+ 10,
288
+ );
289
+
290
+ const [lines] = result;
291
+ expect(lines).toHaveLength(2);
292
+ expect(lines[0]).toEqual(['aa', 20, false, 0, 0]);
293
+ expect(lines[1]).toEqual(['bb', 20, false, 0, 0]);
294
+ });
295
+
296
+ it('should preserve a multi-space separator and its width when the line fits', () => {
297
+ const result = wrapLine(
298
+ testMeasureText,
299
+ 'aa bb',
300
+ 'Arial',
301
+ 1000,
302
+ 0,
303
+ 10, // spaceWidth
304
+ '',
305
+ 0,
306
+ 'break-word',
307
+ 10,
308
+ );
309
+
310
+ expect(result[0][0]).toEqual(['aa bb', 60, false, 0, 0]);
311
+ });
312
+
313
+ it('should treat a run of multiple ZWSPs as a zero-width separator', () => {
314
+ const result = wrapLine(
315
+ testMeasureText,
316
+ 'aa\u200B\u200Bbb',
317
+ 'Arial',
318
+ 1000,
319
+ 0,
320
+ 10, // spaceWidth
321
+ '',
322
+ 0,
323
+ 'break-word',
324
+ 10,
325
+ );
326
+
327
+ // Zero-width separator: no visible gap added, width is just the glyphs
328
+ expect(result[0][0]).toEqual(['aabb', 40, false, 0, 0]);
329
+ });
330
+
331
+ it('should use the precomputed spaceWidth for a single-space separator', () => {
332
+ // spaceWidth (4) deliberately differs from testMeasureText(' ') (10):
333
+ // the single-space fast path must reuse the precomputed value instead
334
+ // of re-measuring the separator.
335
+ const result = wrapLine(
336
+ testMeasureText,
337
+ 'aa bb',
338
+ 'Arial',
339
+ 1000,
340
+ 0,
341
+ 4, // spaceWidth
342
+ '',
343
+ 0,
344
+ 'break-word',
345
+ 10,
346
+ );
347
+
348
+ expect(result[0][0]).toEqual(['aa bb', 44, false, 0, 0]);
349
+ });
251
350
  });
252
351
 
253
352
  describe('wrapText', () => {
package/src/utils.ts CHANGED
@@ -9,7 +9,7 @@ export function createWebGLContext(
9
9
  alpha: true,
10
10
  antialias: false,
11
11
  depth: false,
12
- stencil: true,
12
+ stencil: false,
13
13
  desynchronized: false,
14
14
  // Disabled because it prevents Visual Regression Tests from working
15
15
  // failIfMajorPerformanceCaveat: true,