@xterm/addon-webgl 0.20.0-beta.3 → 0.20.0-beta.300

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.
@@ -10,26 +10,32 @@ import { CursorBlinkStateManager } from './CursorBlinkStateManager';
10
10
  import { observeDevicePixelDimensions } from './DevicePixelObserver';
11
11
  import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
12
12
  import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
13
- import { CharData, IBufferLine, ICellData } from 'common/Types';
13
+ import { CharData, IBufferLine, ICellData } from 'common/buffer/Types';
14
14
  import { AttributeData } from 'common/buffer/AttributeData';
15
15
  import { CellData } from 'common/buffer/CellData';
16
- import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
17
- import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
16
+ import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
17
+ import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
18
+ import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
18
19
  import { Terminal } from '@xterm/xterm';
19
20
  import { GlyphRenderer } from './GlyphRenderer';
20
21
  import { RectangleRenderer } from './RectangleRenderer';
21
- import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel';
22
+ import { COMBINED_CHAR_BIT_MASK, RenderModel, RenderModelConstants } from './RenderModel';
22
23
  import { IWebGL2RenderingContext, type ITextureAtlas } from './Types';
23
24
  import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
24
25
  import { IRenderLayer } from './renderLayer/Types';
25
- import { Emitter, Event } from 'vs/base/common/event';
26
- import { addDisposableListener } from 'vs/base/browser/dom';
27
- import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
26
+ import { Emitter, EventUtils } from 'common/Event';
27
+ import { addDisposableListener } from 'browser/Dom';
28
+ import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
28
29
  import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
29
30
 
31
+ const enum Constants {
32
+ MERGE_RETRY_LIMIT = 32
33
+ }
34
+
30
35
  export class WebglRenderer extends Disposable implements IRenderer {
31
36
  private _renderLayers: IRenderLayer[];
32
- private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
37
+ private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this._register(new MutableDisposable());
38
+ private _textBlinkStateManager: TextBlinkStateManager;
33
39
  private _charAtlasDisposable = this._register(new MutableDisposable());
34
40
  private _charAtlas: ITextureAtlas | undefined;
35
41
  private _devicePixelRatio: number;
@@ -37,8 +43,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
37
43
  private _observerDisposable = this._register(new MutableDisposable());
38
44
 
39
45
  private _model: RenderModel = new RenderModel();
46
+ private _rowHasBlinkingCells: boolean[] = [];
47
+ private _rowHasBlinkingCellsCount: number = 0;
40
48
  private _workCell: ICellData = new CellData();
41
- private _workCell2: ICellData = new CellData();
42
49
  private _cellColorResolver: CellColorResolver;
43
50
 
44
51
  private _canvas: HTMLCanvasElement;
@@ -70,8 +77,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
70
77
  private readonly _coreBrowserService: ICoreBrowserService,
71
78
  private readonly _coreService: ICoreService,
72
79
  private readonly _decorationService: IDecorationService,
80
+ private readonly _logService: ILogService,
73
81
  private readonly _optionsService: IOptionsService,
74
82
  private readonly _themeService: IThemeService,
83
+ private readonly _customGlyphs: boolean = true,
75
84
  preserveDrawingBuffer?: boolean
76
85
  ) {
77
86
  super();
@@ -104,23 +113,29 @@ export class WebglRenderer extends Disposable implements IRenderer {
104
113
  this._updateDimensions();
105
114
  this._updateCursorBlink();
106
115
  this._register(_optionsService.onOptionChange(() => this._handleOptionsChanged()));
116
+ this._textBlinkStateManager = this._register(new TextBlinkStateManager(
117
+ () => this._requestRedrawViewport(),
118
+ this._coreBrowserService,
119
+ this._optionsService
120
+ ));
121
+ this._resetBlinkingRowState();
107
122
 
108
123
  this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);
109
124
 
110
125
  this._register(addDisposableListener(this._canvas, 'webglcontextlost', (e) => {
111
- console.log('webglcontextlost event received');
126
+ this._logService.debug('webglcontextlost event received');
112
127
  // Prevent the default behavior in order to enable WebGL context restoration.
113
128
  e.preventDefault();
114
129
  // Wait a few seconds to see if the 'webglcontextrestored' event is fired.
115
130
  // If not, dispatch the onContextLoss notification to observers.
116
131
  this._contextRestorationTimeout = setTimeout(() => {
117
132
  this._contextRestorationTimeout = undefined;
118
- console.warn('webgl context not restored; firing onContextLoss');
133
+ this._logService.warn('webgl context not restored; firing onContextLoss');
119
134
  this._onContextLoss.fire(e);
120
135
  }, 3000 /* ms */);
121
136
  }));
122
137
  this._register(addDisposableListener(this._canvas, 'webglcontextrestored', (e) => {
123
- console.warn('webglcontextrestored event received');
138
+ this._logService.warn('webglcontextrestored event received');
124
139
  clearTimeout(this._contextRestorationTimeout);
125
140
  this._contextRestorationTimeout = undefined;
126
141
  // The texture atlas and glyph renderer must be fully reinitialized
@@ -135,6 +150,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
135
150
  this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h));
136
151
  }));
137
152
 
153
+ this._register(addDisposableListener(this._coreBrowserService.mainDocument, 'mousedown', () => this._cursorBlinkStateManager.value?.restartBlinkAnimation()));
154
+
138
155
  this._core.screenElement!.appendChild(this._canvas);
139
156
 
140
157
  [this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState();
@@ -142,6 +159,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
142
159
  this._isAttached = this._core.screenElement!.isConnected;
143
160
 
144
161
  this._register(toDisposable(() => {
162
+ clearTimeout(this._contextRestorationTimeout);
163
+ this._contextRestorationTimeout = undefined;
145
164
  for (const l of this._renderLayers) {
146
165
  l.dispose();
147
166
  }
@@ -175,6 +194,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
175
194
  this._updateDimensions();
176
195
 
177
196
  this._model.resize(this._terminal.cols, this._terminal.rows);
197
+ this._resetBlinkingRowState();
178
198
 
179
199
  // Resize all render layers
180
200
  for (const l of this._renderLayers) {
@@ -201,6 +221,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
201
221
  // Force a full refresh. Resizing `_glyphRenderer` should clear it already,
202
222
  // so there is no need to clear it again here.
203
223
  this._clearModel(false);
224
+
225
+ // Render synchronously to avoid flicker when the canvas is cleared
226
+ this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true });
204
227
  }
205
228
 
206
229
  public handleCharSizeChanged(): void {
@@ -225,6 +248,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
225
248
  this._requestRedrawViewport();
226
249
  }
227
250
 
251
+ public handleViewportVisibilityChange(isVisible: boolean): void {
252
+ this._textBlinkStateManager.setViewportVisible(isVisible);
253
+ }
254
+
228
255
  public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
229
256
  for (const l of this._renderLayers) {
230
257
  l.handleSelectionChanged(this._terminal, start, end, columnSelectMode);
@@ -250,8 +277,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
250
277
  * Initializes members dependent on WebGL context state.
251
278
  */
252
279
  private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] {
253
- this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService);
254
- this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService);
280
+ this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService, this._logService);
281
+ this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService, this._logService);
255
282
 
256
283
  // Update dimensions and acquire char atlas
257
284
  this.handleCharSizeChanged();
@@ -278,13 +305,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
278
305
  this.dimensions.device.char.width,
279
306
  this.dimensions.device.char.height,
280
307
  this._coreBrowserService.dpr,
281
- this._deviceMaxTextureSize
308
+ this._deviceMaxTextureSize,
309
+ this._customGlyphs
282
310
  );
283
311
  if (this._charAtlas !== atlas) {
284
312
  this._onChangeTextureAtlas.fire(atlas.pages[0].canvas);
285
313
  this._charAtlasDisposable.value = combinedDisposable(
286
- Event.forward(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas),
287
- Event.forward(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)
314
+ EventUtils.forward(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas),
315
+ EventUtils.forward(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)
288
316
  );
289
317
  }
290
318
  this._charAtlas = atlas;
@@ -316,6 +344,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
316
344
  l.reset(this._terminal);
317
345
  }
318
346
 
347
+ this._resetBlinkingRowState();
348
+ this._textBlinkStateManager.setNeedsBlinkInViewport(false);
349
+
319
350
  this._cursorBlinkStateManager.value?.restartBlinkAnimation();
320
351
  this._updateCursorBlink();
321
352
  }
@@ -351,6 +382,19 @@ export class WebglRenderer extends Disposable implements IRenderer {
351
382
  this._updateModel(start, end);
352
383
  }
353
384
 
385
+ // A mid-update atlas page merge invalidates vertex data and may not bump the host
386
+ // page's version, so re-run the update and force a full texture rebind.
387
+ let merged = false;
388
+ let mergeRetries = 0;
389
+ while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) {
390
+ merged = true;
391
+ this._clearModel(true);
392
+ this._updateModel(0, this._terminal.rows - 1);
393
+ }
394
+ if (merged) {
395
+ this._glyphRenderer.value.invalidateAtlasTextures();
396
+ }
397
+
354
398
  // Render
355
399
  this._rectangleRenderer.value.renderBackgrounds();
356
400
  this._glyphRenderer.value.render(this._model);
@@ -383,8 +427,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
383
427
  let line: IBufferLine;
384
428
  let joinedRanges: [number, number][];
385
429
  let isJoined: boolean;
386
- let skipJoinedCheckUntilX: number = 0;
387
- let isValidJoinRange: boolean = true;
430
+ let skipJoinedCheckUntilX: number;
431
+ let isValidJoinRange: boolean;
388
432
  let lastCharX: number;
389
433
  let range: [number, number];
390
434
  let isCursorRow: boolean;
@@ -412,7 +456,19 @@ export class WebglRenderer extends Disposable implements IRenderer {
412
456
 
413
457
  for (y = start; y <= end; y++) {
414
458
  row = y + terminal.buffer.ydisp;
415
- line = terminal.buffer.lines.get(row)!;
459
+ const bufferLine = terminal.buffer.lines.get(row);
460
+ if (!bufferLine) {
461
+ this._model.lineLengths[y] = 0;
462
+ for (x = 0; x < terminal.cols; x++) {
463
+ j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
464
+ modelUpdated = true;
465
+ this._nullModelCell(x, y, j, 0, 0, 0);
466
+ }
467
+ this._setRowBlinkState(y, false);
468
+ continue;
469
+ }
470
+ line = bufferLine;
471
+ let rowHasBlinkingCells = false;
416
472
  this._model.lineLengths[y] = 0;
417
473
  isCursorRow = cursorY === row;
418
474
  skipJoinedCheckUntilX = 0;
@@ -468,10 +524,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
468
524
 
469
525
  chars = cell.getChars();
470
526
  code = cell.getCode();
471
- i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
527
+ i = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
528
+
529
+ if (!rowHasBlinkingCells && cell.isBlink()) {
530
+ rowHasBlinkingCells = true;
531
+ }
472
532
 
473
533
  // Load colors/resolve overrides into work colors
474
- this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width);
534
+ this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width, this.dimensions.device.cell.height);
475
535
 
476
536
  // Override colors for cursor cell
477
537
  if (isCursorVisible && row === cursorY) {
@@ -499,15 +559,19 @@ export class WebglRenderer extends Disposable implements IRenderer {
499
559
  }
500
560
  }
501
561
 
562
+ if (this._textBlinkStateManager.isEnabled && !this._textBlinkStateManager.isBlinkOn && cell.isBlink()) {
563
+ this._cellColorResolver.result.fg |= FgFlags.INVISIBLE;
564
+ }
565
+
502
566
  if (code !== NULL_CELL_CODE) {
503
567
  this._model.lineLengths[y] = x + 1;
504
568
  }
505
569
 
506
570
  // Nothing has changed, no updates needed
507
571
  if (this._model.cells[i] === code &&
508
- this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._cellColorResolver.result.bg &&
509
- this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._cellColorResolver.result.fg &&
510
- this._model.cells[i + RENDER_MODEL_EXT_OFFSET] === this._cellColorResolver.result.ext) {
572
+ this._model.cells[i + RenderModelConstants.BG_OFFSET] === this._cellColorResolver.result.bg &&
573
+ this._model.cells[i + RenderModelConstants.FG_OFFSET] === this._cellColorResolver.result.fg &&
574
+ this._model.cells[i + RenderModelConstants.EXT_OFFSET] === this._cellColorResolver.result.ext) {
511
575
  continue;
512
576
  }
513
577
 
@@ -520,9 +584,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
520
584
 
521
585
  // Cache the results in the model
522
586
  this._model.cells[i] = code;
523
- this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
524
- this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
525
- this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
587
+ this._model.cells[i + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg;
588
+ this._model.cells[i + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg;
589
+ this._model.cells[i + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext;
526
590
 
527
591
  width = cell.getWidth();
528
592
  this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, width, lastBg);
@@ -533,23 +597,47 @@ export class WebglRenderer extends Disposable implements IRenderer {
533
597
 
534
598
  // Null out non-first cells
535
599
  for (x++; x <= lastCharX; x++) {
536
- j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
537
- this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
538
- this._model.cells[j] = NULL_CELL_CODE;
600
+ j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
539
601
  // Don't re-resolve the cell color since multi-colored ligature backgrounds are not
540
602
  // supported
541
- this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
542
- this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
543
- this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
603
+ this._nullModelCell(x, y, j, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
544
604
  }
545
605
  x--; // Go back to the previous update cell for next iteration
546
606
  }
547
607
  }
608
+ this._setRowBlinkState(y, rowHasBlinkingCells);
548
609
  }
549
610
  if (modelUpdated) {
550
611
  this._rectangleRenderer.value!.updateBackgrounds(this._model);
551
612
  }
552
613
  this._rectangleRenderer.value!.updateCursor(this._model);
614
+ this._updateTextBlinkState();
615
+ }
616
+
617
+ private _nullModelCell(x: number, y: number, cellIndex: number, bg: number, fg: number, ext: number): void {
618
+ this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, bg, fg, ext, NULL_CELL_CHAR, 0, 0);
619
+ this._model.cells[cellIndex] = NULL_CELL_CODE;
620
+ this._model.cells[cellIndex + RenderModelConstants.BG_OFFSET] = bg;
621
+ this._model.cells[cellIndex + RenderModelConstants.FG_OFFSET] = fg;
622
+ this._model.cells[cellIndex + RenderModelConstants.EXT_OFFSET] = ext;
623
+ }
624
+
625
+ private _resetBlinkingRowState(): void {
626
+ this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false);
627
+ this._rowHasBlinkingCellsCount = 0;
628
+ }
629
+
630
+ private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void {
631
+ const previous = this._rowHasBlinkingCells[row];
632
+ if (previous === hasBlinkingCells) {
633
+ return;
634
+ }
635
+ this._rowHasBlinkingCells[row] = hasBlinkingCells;
636
+ this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1;
637
+ }
638
+
639
+ private _updateTextBlinkState(): void {
640
+ this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0);
553
641
  }
554
642
 
555
643
  /**
@@ -591,10 +679,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
591
679
  this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height;
592
680
  this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width;
593
681
 
594
- // The the size of the canvas on the page. It's important that this rounds to nearest integer
682
+ // The size of the canvas on the page. It's important that this rounds to nearest integer
595
683
  // and not ceils as browsers often have floating point precision issues where
596
684
  // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when
597
- // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too
685
+ // it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too
598
686
  // large for the canvas element size.
599
687
  this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio);
600
688
  this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio);
@@ -615,7 +703,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
615
703
  // the change as it's an exact multiple of the cell sizes.
616
704
  this._canvas.width = width;
617
705
  this._canvas.height = height;
618
- this._requestRedrawViewport();
706
+ // Render synchronously to avoid flicker when the canvas is cleared
707
+ this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true });
619
708
  }
620
709
 
621
710
  private _requestRedrawViewport(): void {
package/src/WebglUtils.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
7
+ import type { ILogService } from 'common/services/Services';
7
8
 
8
9
  /**
9
10
  * A matrix that when multiplies will translate 0-1 coordinates (left to right,
@@ -16,21 +17,21 @@ export const PROJECTION_MATRIX = new Float32Array([
16
17
  -1, 1, 0, 1
17
18
  ]);
18
19
 
19
- export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | undefined {
20
+ export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string, logService: ILogService): WebGLProgram | undefined {
20
21
  const program = throwIfFalsy(gl.createProgram());
21
- gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource)));
22
- gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)));
22
+ gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource, logService)));
23
+ gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource, logService)));
23
24
  gl.linkProgram(program);
24
25
  const success = gl.getProgramParameter(program, gl.LINK_STATUS);
25
26
  if (success) {
26
27
  return program;
27
28
  }
28
29
 
29
- console.error(gl.getProgramInfoLog(program));
30
+ logService.error(gl.getProgramInfoLog(program));
30
31
  gl.deleteProgram(program);
31
32
  }
32
33
 
33
- export function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined {
34
+ export function createShader(gl: WebGLRenderingContext, type: number, source: string, logService: ILogService): WebGLShader | undefined {
34
35
  const shader = throwIfFalsy(gl.createShader(type));
35
36
  gl.shaderSource(shader, source);
36
37
  gl.compileShader(shader);
@@ -39,7 +40,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st
39
40
  return shader;
40
41
  }
41
42
 
42
- console.error(gl.getShaderInfoLog(shader));
43
+ logService.error(gl.getShaderInfoLog(shader));
43
44
  gl.deleteShader(shader);
44
45
  }
45
46