• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 7294397098

21 Dec 2023 11:56PM UTC coverage: 91.859% (-0.06%) from 91.915%
7294397098

push

github

web-flow
fix: #2848 Sprite tint respected in constructor (#2852)

Closes #2848 

## Changes:

- Fixes sprite tint constructor value passing
- Fixes sprite tint on cloned sprites
- Fixes width/height inconsistency discovered in new tests
- New tests

4602 of 5758 branches covered (0.0%)

7 of 7 new or added lines in 3 files covered. (100.0%)

7 existing lines in 2 files now uncovered.

10686 of 11633 relevant lines covered (91.86%)

27166.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.3
/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts
1
import {
2
  ExcaliburGraphicsContext,
3
  LineGraphicsOptions,
4
  RectGraphicsOptions,
5
  PointGraphicsOptions,
6
  ExcaliburGraphicsContextOptions,
7
  DebugDraw,
8
  HTMLImageSource
9
} from './ExcaliburGraphicsContext';
10

11
import { Matrix } from '../../Math/matrix';
12
import { TransformStack } from './transform-stack';
13
import { Vector, vec } from '../../Math/vector';
14
import { Color } from '../../Color';
15
import { StateStack } from './state-stack';
16
import { Logger } from '../../Util/Log';
17
import { DebugText } from './debug-text';
18
import { ScreenDimension } from '../../Screen';
19
import { RenderTarget } from './render-target';
20
import { PostProcessor } from '../PostProcessor/PostProcessor';
21
import { TextureLoader } from './texture-loader';
22
import { RendererPlugin } from './renderer';
23

24
// renderers
25
import { LineRenderer } from './line-renderer/line-renderer';
26
import { PointRenderer } from './point-renderer/point-renderer';
27
import { ScreenPassPainter } from './screen-pass-painter/screen-pass-painter';
28
import { ImageRenderer } from './image-renderer/image-renderer';
29
import { RectangleRenderer } from './rectangle-renderer/rectangle-renderer';
30
import { CircleRenderer } from './circle-renderer/circle-renderer';
31
import { Pool } from '../../Util/Pool';
32
import { DrawCall } from './draw-call';
33
import { AffineMatrix } from '../../Math/affine-matrix';
34
import { Material, MaterialOptions } from './material';
35
import { MaterialRenderer } from './material-renderer/material-renderer';
36
import { Shader, ShaderOptions } from './shader';
37

38
export const pixelSnapEpsilon = 0.0001;
1✔
39

40
class ExcaliburGraphicsContextWebGLDebug implements DebugDraw {
41
  private _debugText = new DebugText();
684✔
42
  constructor(private _webglCtx: ExcaliburGraphicsContextWebGL) {}
684✔
43

44
  /**
45
   * Draw a debugging rectangle to the context
46
   * @param x
47
   * @param y
48
   * @param width
49
   * @param height
50
   */
51
  drawRect(x: number, y: number, width: number, height: number, rectOptions: RectGraphicsOptions = { color: Color.Black }): void {
×
52
    this.drawLine(vec(x, y), vec(x + width, y), { ...rectOptions });
124✔
53
    this.drawLine(vec(x + width, y), vec(x + width, y + height), { ...rectOptions });
124✔
54
    this.drawLine(vec(x + width, y + height), vec(x, y + height), { ...rectOptions });
124✔
55
    this.drawLine(vec(x, y + height), vec(x, y), { ...rectOptions });
124✔
56
  }
57

58
  /**
59
   * Draw a debugging line to the context
60
   * @param start
61
   * @param end
62
   * @param lineOptions
63
   */
64
  drawLine(start: Vector, end: Vector, lineOptions: LineGraphicsOptions = { color: Color.Black }): void {
×
65
    this._webglCtx.draw<LineRenderer>('ex.line', start, end, lineOptions.color);
497✔
66
  }
67

68
  /**
69
   * Draw a debugging point to the context
70
   * @param point
71
   * @param pointOptions
72
   */
73
  drawPoint(point: Vector, pointOptions: PointGraphicsOptions = { color: Color.Black, size: 5 }): void {
×
74
    this._webglCtx.draw<PointRenderer>('ex.point', point, pointOptions.color, pointOptions.size);
35✔
75
  }
76

77
  drawText(text: string, pos: Vector) {
78
    this._debugText.write(this._webglCtx, text, pos);
23✔
79
  }
80
}
81

82
export interface WebGLGraphicsContextInfo {
83
  transform: TransformStack;
84
  state: StateStack;
85
  ortho: Matrix;
86
  context: ExcaliburGraphicsContextWebGL;
87
}
88

89
export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
90
  private _logger = Logger.getInstance();
684✔
91
  private _renderers: Map<string, RendererPlugin> = new Map<string, RendererPlugin>();
684✔
92
  private _isDrawLifecycle = false;
684✔
93
  public useDrawSorting = true;
684✔
94

95
  private _drawCallPool = new Pool<DrawCall>(
684✔
96
    () => new DrawCall(),
2,789,535✔
97
    (instance) => {
98
      instance.priority = 0;
15,716✔
99
      instance.z = 0;
15,716✔
100
      instance.renderer = undefined;
15,716✔
101
      instance.args = undefined;
15,716✔
102
      return instance;
15,716✔
103
    }, 4000);
104
  private _drawCalls: DrawCall[] = [];
684✔
105

106
  // Main render target
107
  private _renderTarget: RenderTarget;
108

109
  // Postprocessing is a tuple with 2 render targets, these are flip-flopped during the postprocessing process
110
  private _postProcessTargets: RenderTarget[] = [];
684✔
111

112
  private _screenRenderer: ScreenPassPainter;
113

114
  private _postprocessors: PostProcessor[] = [];
684✔
115
  /**
116
   * Meant for internal use only. Access the internal context at your own risk and no guarantees this will exist in the future.
117
   * @internal
118
   */
119
  public __gl: WebGL2RenderingContext;
120

121
  private _transform = new TransformStack();
684✔
122
  private _state = new StateStack();
684✔
123
  private _ortho!: Matrix;
124

125
  public snapToPixel: boolean = false;
684✔
126

127
  public smoothing: boolean = false;
684✔
128

129
  public backgroundColor: Color = Color.ExcaliburBlue;
684✔
130

131
  public textureLoader: TextureLoader;
132

133
  public materialScreenTexture: WebGLTexture;
134

135
  public get z(): number {
136
    return this._state.current.z;
×
137
  }
138

139
  public set z(value: number) {
140
    this._state.current.z = value;
2,619✔
141
  }
142

143
  public get opacity(): number {
144
    return this._state.current.opacity;
138,782✔
145
  }
146

147
  public set opacity(value: number) {
148
    this._state.current.opacity = value;
4,499✔
149
  }
150

151
  public get tint(): Color {
152
    return this._state.current.tint;
46,444✔
153
  }
154

155
  public set tint(color: Color) {
156
    this._state.current.tint = color;
1✔
157
  }
158

159
  public get width() {
160
    return this.__gl.canvas.width;
709✔
161
  }
162

163
  public get height() {
164
    return this.__gl.canvas.height;
709✔
165
  }
166

167
  public get ortho(): Matrix {
168
    return this._ortho;
4,477✔
169
  }
170

171
  /**
172
   * Checks the underlying webgl implementation if the requested internal resolution is supported
173
   * @param dim
174
   */
175
  public checkIfResolutionSupported(dim: ScreenDimension): boolean {
176
    // Slight hack based on this thread https://groups.google.com/g/webgl-dev-list/c/AHONvz3oQTo
177
    let supported = true;
6,166✔
178
    if (dim.width > 4096 || dim.height > 4096) {
6,166!
UNCOV
179
      supported = false;
×
180
    }
181
    return supported;
6,166✔
182
  }
183

184
  constructor(options: ExcaliburGraphicsContextOptions) {
185
    const { canvasElement, enableTransparency, smoothing, snapToPixel, backgroundColor, useDrawSorting } = options;
684✔
186
    this.__gl = canvasElement.getContext('webgl2', {
684✔
187
      antialias: smoothing ?? this.smoothing,
2,052✔
188
      premultipliedAlpha: false,
189
      alpha: enableTransparency ?? true,
2,052✔
190
      depth: true,
191
      powerPreference: 'high-performance'
192
      // TODO Chromium fixed the bug where this didn't work now it breaks CI :(
193
      // failIfMajorPerformanceCaveat: true
194
    });
195
    if (!this.__gl) {
684!
196
      throw Error('Failed to retrieve webgl context from browser');
×
197
    }
198
    this.textureLoader = new TextureLoader(this.__gl);
684✔
199
    this.snapToPixel = snapToPixel ?? this.snapToPixel;
684✔
200
    this.smoothing = smoothing ?? this.smoothing;
684✔
201
    this.backgroundColor = backgroundColor ?? this.backgroundColor;
684✔
202
    this.useDrawSorting = useDrawSorting ?? this.useDrawSorting;
684✔
203
    this._drawCallPool.disableWarnings = true;
684✔
204
    this._drawCallPool.preallocate();
684✔
205
    this._init();
684✔
206
  }
207

208
  private _init() {
209
    const gl = this.__gl;
684✔
210
    // Setup viewport and view matrix
211
    this._ortho = Matrix.ortho(0, gl.canvas.width, gl.canvas.height, 0, 400, -400);
684✔
212
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
684✔
213

214
    // Clear background
215
    gl.clearColor(this.backgroundColor.r / 255, this.backgroundColor.g / 255, this.backgroundColor.b / 255, this.backgroundColor.a);
684✔
216
    gl.clear(gl.COLOR_BUFFER_BIT);
684✔
217

218
    // Enable alpha blending
219
    // https://www.realtimerendering.com/blog/gpus-prefer-premultiplication/
220
    gl.enable(gl.BLEND);
684✔
221
    gl.blendEquation(gl.FUNC_ADD);
684✔
222
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
684✔
223
    gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
684✔
224
    gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
684✔
225

226
    // Setup builtin renderers
227
    this.register(new ImageRenderer());
684✔
228
    this.register(new MaterialRenderer());
684✔
229
    this.register(new RectangleRenderer());
684✔
230
    this.register(new CircleRenderer());
684✔
231
    this.register(new PointRenderer());
684✔
232
    this.register(new LineRenderer());
684✔
233

234

235
    this.materialScreenTexture = gl.createTexture();
684✔
236
    gl.bindTexture(gl.TEXTURE_2D, this.materialScreenTexture);
684✔
237
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
684✔
238
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
684✔
239
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
684✔
240
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
684✔
241
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
684✔
242
    gl.bindTexture(gl.TEXTURE_2D, null);
684✔
243

244
    this._screenRenderer = new ScreenPassPainter(gl);
684✔
245

246
    this._renderTarget = new RenderTarget({
684✔
247
      gl,
248
      width: gl.canvas.width,
249
      height: gl.canvas.height
250
    });
251

252

253
    this._postProcessTargets = [
684✔
254
      new RenderTarget({
255
        gl,
256
        width: gl.canvas.width,
257
        height: gl.canvas.height
258
      }),
259
      new RenderTarget({
260
        gl,
261
        width: gl.canvas.width,
262
        height: gl.canvas.height
263
      })
264
    ];
265
  }
266

267
  public register<T extends RendererPlugin>(renderer: T) {
268
    this._renderers.set(renderer.type, renderer);
4,104✔
269
    renderer.initialize(this.__gl, this);
4,104✔
270
  }
271

272
  public get(rendererName: string): RendererPlugin {
273
    return this._renderers.get(rendererName);
12✔
274
  }
275

276
  private _currentRenderer: RendererPlugin;
277

278
  private _isCurrentRenderer(renderer: RendererPlugin): boolean {
279
    if (!this._currentRenderer || this._currentRenderer === renderer) {
65,539✔
280
      return true;
65,536✔
281
    }
282
    return false;
3✔
283
  }
284

285
  public beginDrawLifecycle() {
286
    this._isDrawLifecycle = true;
1,538✔
287
  }
288

289
  public endDrawLifecycle() {
290
    this._isDrawLifecycle = false;
701✔
291
  }
292

293
  private _alreadyWarnedDrawLifecycle = false;
684✔
294

295
  public draw<TRenderer extends RendererPlugin>(rendererName: TRenderer['type'], ...args: Parameters<TRenderer['draw']>) {
296
    if (!this._isDrawLifecycle && !this._alreadyWarnedDrawLifecycle) {
134,791✔
297
      this._logger.warn(
77✔
298
        `Attempting to draw outside the the drawing lifecycle (preDraw/postDraw) is not supported and is a source of bugs/errors.\n` +
299
        `If you want to do custom drawing, use Actor.graphics, or any onPreDraw or onPostDraw handler.`);
300
      this._alreadyWarnedDrawLifecycle = true;
77✔
301
    }
302

303
    const renderer = this._renderers.get(rendererName);
134,791✔
304
    if (renderer) {
134,791✔
305
      if (this.useDrawSorting) {
134,790✔
306
        const drawCall = this._drawCallPool.get();
69,251✔
307
        drawCall.z = this._state.current.z;
69,251✔
308
        drawCall.priority = renderer.priority;
69,251✔
309
        drawCall.renderer = rendererName;
69,251✔
310
        this.getTransform().clone(drawCall.transform);
69,251✔
311
        drawCall.state.z = this._state.current.z;
69,251✔
312
        drawCall.state.opacity = this._state.current.opacity;
69,251✔
313
        drawCall.state.tint = this._state.current.tint;
69,251✔
314
        drawCall.state.material = this._state.current.material;
69,251✔
315
        drawCall.args = args;
69,251✔
316
        this._drawCalls.push(drawCall);
69,251✔
317
      } else {
318
        // Set the current renderer if not defined
319
        if (!this._currentRenderer) {
65,539✔
320
          this._currentRenderer = renderer;
4✔
321
        }
322

323
        if (!this._isCurrentRenderer(renderer)) {
65,539✔
324
          // switching graphics means we must flush the previous
325
          this._currentRenderer.flush();
3✔
326
        }
327

328
        // If we are still using the same renderer we can add to the current batch
329
        renderer.draw(...args);
65,539✔
330

331
        this._currentRenderer = renderer;
65,539✔
332
      }
333
    } else {
334
      throw Error(`No renderer with name ${rendererName} has been registered`);
1✔
335
    }
336
  }
337

338
  public resetTransform(): void {
339
    this._transform.current = AffineMatrix.identity();
6,167✔
340
  }
341

342
  public updateViewport(resolution: ScreenDimension): void {
343
    const gl = this.__gl;
6,167✔
344
    this._ortho = this._ortho = Matrix.ortho(0, resolution.width, resolution.height, 0, 400, -400);
6,167✔
345

346
    this._renderTarget.setResolution(gl.canvas.width, gl.canvas.height);
6,167✔
347
    this._postProcessTargets[0].setResolution(gl.canvas.width, gl.canvas.height);
6,167✔
348
    this._postProcessTargets[1].setResolution(gl.canvas.width, gl.canvas.height);
6,167✔
349
  }
350

351
  drawImage(image: HTMLImageSource, x: number, y: number): void;
352
  drawImage(image: HTMLImageSource, x: number, y: number, width: number, height: number): void;
353
  drawImage(
354
    image: HTMLImageSource,
355
    sx: number,
356
    sy: number,
357
    swidth?: number,
358
    sheight?: number,
359
    dx?: number,
360
    dy?: number,
361
    dwidth?: number,
362
    dheight?: number
363
  ): void;
364
  drawImage(
365
    image: HTMLImageSource,
366
    sx: number,
367
    sy: number,
368
    swidth?: number,
369
    sheight?: number,
370
    dx?: number,
371
    dy?: number,
372
    dwidth?: number,
373
    dheight?: number
374
  ): void {
375
    if (swidth === 0 || sheight === 0) {
46,458✔
376
      return; // zero dimension dest exit early
1✔
377
    } else if (dwidth === 0 || dheight === 0) {
46,457✔
378
      return; // zero dimension dest exit early
1✔
379
    } else if (image.width === 0 || image.height === 0) {
46,456✔
380
      return; // zero dimension source exit early
1✔
381
    }
382

383
    if (!image) {
46,455!
384
      Logger.getInstance().warn('Cannot draw a null or undefined image');
×
385
      // tslint:disable-next-line: no-console
386
      if (console.trace) {
×
387
        // tslint:disable-next-line: no-console
388
        console.trace();
×
389
      }
390
      return;
×
391
    }
392

393
    if (this._state.current.material) {
46,455✔
394
      this.draw<MaterialRenderer>('ex.material', image, sx, sy, swidth, sheight, dx, dy, dwidth, dheight);
6✔
395
    } else {
396
      this.draw<ImageRenderer>('ex.image', image, sx, sy, swidth, sheight, dx, dy, dwidth, dheight);
46,449✔
397
    }
398
  }
399

400
  public drawLine(start: Vector, end: Vector, color: Color, thickness = 1) {
×
401
    this.draw<RectangleRenderer>('ex.rectangle', start, end, color, thickness);
104✔
402
  }
403

404
  public drawRectangle(pos: Vector, width: number, height: number, color: Color, stroke?: Color, strokeThickness?: number) {
405
    this.draw<RectangleRenderer>('ex.rectangle', pos, width, height, color, stroke, strokeThickness);
43,695✔
406
  }
407

408
  public drawCircle(pos: Vector, radius: number, color: Color, stroke?: Color, thickness?: number) {
409
    this.draw<CircleRenderer>('ex.circle', pos, radius, color, stroke, thickness);
44,004✔
410
  }
411

412
  debug = new ExcaliburGraphicsContextWebGLDebug(this);
684✔
413

414
  public save(): void {
415
    this._transform.save();
6,689✔
416
    this._state.save();
6,689✔
417
  }
418

419
  public restore(): void {
420
    this._transform.restore();
6,689✔
421
    this._state.restore();
6,689✔
422
  }
423

424
  public translate(x: number, y: number): void {
425
    this._transform.translate(this.snapToPixel ? ~~(x + pixelSnapEpsilon) : x, this.snapToPixel ? ~~(y + pixelSnapEpsilon) : y);
6,774✔
426
  }
427

428
  public rotate(angle: number): void {
429
    this._transform.rotate(angle);
3,082✔
430
  }
431

432
  public scale(x: number, y: number): void {
433
    this._transform.scale(x, y);
4,455✔
434
  }
435

436
  public transform(matrix: AffineMatrix) {
437
    this._transform.current = matrix;
×
438
  }
439

440
  public getTransform(): AffineMatrix {
441
    return this._transform.current;
204,034✔
442
  }
443

444
  public multiply(m: AffineMatrix) {
445
    this._transform.current.multiply(m, this._transform.current);
3,791✔
446
  }
447

448
  public addPostProcessor(postprocessor: PostProcessor) {
449
    this._postprocessors.push(postprocessor);
8✔
450
    postprocessor.initialize(this.__gl);
8✔
451
  }
452

453
  public removePostProcessor(postprocessor: PostProcessor) {
454
    const index = this._postprocessors.indexOf(postprocessor);
6✔
455
    if (index !== -1) {
6!
456
      this._postprocessors.splice(index, 1);
×
457
    }
458
  }
459

460
  public clearPostProcessors() {
461
    this._postprocessors.length = 0;
×
462
  }
463

464
  private _totalPostProcessorTime = 0;
684✔
465
  public updatePostProcessors(delta: number) {
466
    for (const postprocessor of this._postprocessors) {
704✔
467
      const shader = postprocessor.getShader();
10✔
468
      shader.use();
10✔
469
      const uniforms = shader.getUniforms();
10✔
470
      this._totalPostProcessorTime += delta;
10✔
471

472
      if (uniforms.find(u => u.name ==='u_time_ms')) {
26✔
473
        shader.setUniformFloat('u_time_ms', this._totalPostProcessorTime);
4✔
474
      }
475
      if (uniforms.find(u => u.name ==='u_elapsed_ms')) {
30✔
476
        shader.setUniformFloat('u_elapsed_ms', delta);
4✔
477
      }
478
      if (uniforms.find(u => u.name ==='u_resolution')) {
22✔
479
        shader.setUniformFloatVector('u_resolution', vec(this.width, this.height));
4✔
480
      }
481

482
      if (postprocessor.onUpdate) {
10✔
483
        postprocessor.onUpdate(delta);
4✔
484
      }
485
    }
486
  }
487

488
  public set material(material: Material) {
489
    this._state.current.material = material;
6✔
490
  }
491

492
  public get material(): Material | null {
493
    return this._state.current.material;
17✔
494
  }
495

496
  /**
497
   * Creates and initializes the material which compiles the internal shader
498
   * @param options
499
   * @returns Material
500
   */
501
  public createMaterial(options: MaterialOptions): Material {
502
    const material = new Material(options);
2✔
503
    material.initialize(this.__gl, this);
2✔
504
    return material;
2✔
505
  }
506

507
  public createShader(options: Omit<ShaderOptions, 'gl'>): Shader {
508
    const gl = this.__gl;
6✔
509
    const { vertexSource, fragmentSource } = options;
6✔
510
    const shader = new Shader({
6✔
511
      gl,
512
      vertexSource,
513
      fragmentSource
514
    });
515
    shader.compile();
6✔
516
    return shader;
6✔
517
  }
518

519
  clear() {
520
    const gl = this.__gl;
1,591✔
521
    this._renderTarget.use();
1,591✔
522
    gl.clearColor(this.backgroundColor.r / 255, this.backgroundColor.g / 255, this.backgroundColor.b / 255, this.backgroundColor.a);
1,591✔
523
    // Clear the context with the newly set color. This is
524
    // the function call that actually does the drawing.
525
    gl.clear(gl.COLOR_BUFFER_BIT);
1,591✔
526
  }
527

528
  /**
529
   * Flushes all batched rendering to the screen
530
   */
531
  flush() {
532
    const gl = this.__gl;
1,623✔
533

534
    // render target captures all draws and redirects to the render target
535
    this._renderTarget.use();
1,623✔
536

537
    if (this.useDrawSorting) {
1,623✔
538
      // sort draw calls
539
      // Find the original order of the first instance of the draw call
540
      const originalSort = new Map<string, number>();
1,622✔
541
      for (const [name] of this._renderers) {
1,622✔
542
        const firstIndex = this._drawCalls.findIndex(dc => dc.renderer === name);
345,476✔
543
        originalSort.set(name, firstIndex);
9,732✔
544
      }
545

546
      this._drawCalls.sort((a, b) => {
1,622✔
547
        const zIndex = a.z - b.z;
69,176✔
548
        const originalSortOrder = originalSort.get(a.renderer) - originalSort.get(b.renderer);
69,176✔
549
        const priority = a.priority - b.priority;
69,176✔
550
        if (zIndex === 0) { // sort by z first
69,176✔
551
          if (priority === 0) { // sort by priority
69,005!
552
            return originalSortOrder; // use the original order to inform draw call packing to maximally preserve painter order
69,005✔
553
          }
554
          return priority;
×
555
        }
556
        return zIndex;
171✔
557
      });
558

559
      const oldTransform = this._transform.current;
1,622✔
560
      const oldState = this._state.current;
1,622✔
561

562
      if (this._drawCalls.length) {
1,622✔
563
        let currentRendererName = this._drawCalls[0].renderer;
999✔
564
        let currentRenderer = this._renderers.get(currentRendererName);
999✔
565
        for (let i = 0; i < this._drawCalls.length; i++) {
999✔
566
          // hydrate the state for renderers
567
          this._transform.current = this._drawCalls[i].transform;
69,242✔
568
          this._state.current = this._drawCalls[i].state;
69,242✔
569

570
          if (this._drawCalls[i].renderer !== currentRendererName) {
69,242✔
571
            // switching graphics renderer means we must flush the previous
572
            currentRenderer.flush();
29✔
573
            currentRendererName = this._drawCalls[i].renderer;
29✔
574
            currentRenderer = this._renderers.get(currentRendererName);
29✔
575
          }
576

577
          // ! hack to grab screen texture before materials run because they might want it
578
          if (currentRenderer instanceof MaterialRenderer && this.material.isUsingScreenTexture) {
69,242✔
579
            const gl = this.__gl;
2✔
580
            gl.bindTexture(gl.TEXTURE_2D, this.materialScreenTexture);
2✔
581
            gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, this.width, this.height, 0);
2✔
582
            this._renderTarget.use();
2✔
583
          }
584
          // If we are still using the same renderer we can add to the current batch
585
          currentRenderer.draw(...this._drawCalls[i].args);
69,242✔
586
        }
587
        if (currentRenderer.hasPendingDraws()) {
999✔
588
          currentRenderer.flush();
994✔
589
        }
590
      }
591

592
      // reset state
593
      this._transform.current = oldTransform;
1,622✔
594
      this._state.current = oldState;
1,622✔
595

596
      // reclaim draw calls
597
      this._drawCallPool.done();
1,622✔
598
      this._drawCalls.length = 0;
1,622✔
599
    } else {
600
      // This is the final flush at the moment to draw any leftover pending draw
601
      for (const renderer of this._renderers.values()) {
1✔
602
        if (renderer.hasPendingDraws()) {
6✔
603
          renderer.flush();
1✔
604
        }
605
      }
606
    }
607

608
    this._renderTarget.disable();
1,623✔
609

610
    // post process step
611
    const source = this._renderTarget.toRenderSource();
1,623✔
612
    source.use();
1,623✔
613

614
    // flip flop render targets
615
    for (let i = 0; i < this._postprocessors.length; i++) {
1,623✔
616
      this._postProcessTargets[i % 2].use();
6✔
617
      this._screenRenderer.renderWithPostProcessor(this._postprocessors[i]);
6✔
618
      this._postProcessTargets[i % 2].toRenderSource().use();
6✔
619
    }
620

621
    // passing null switches rendering back to the canvas
622
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
1,623✔
623
    this._screenRenderer.renderToScreen();
1,623✔
624
  }
625
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc