From dd7b325978c071abbacf7eef41b5ede8b72934ff Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Thu, 18 Jul 2019 20:17:10 -0700 Subject: [PATCH 01/28] [DO-NOT-REVIEW] webgl auto-version/feature WIP --- src/backends/webgl/canvas_util.ts | 37 ++++++++++++++++++----------- src/backends/webgl/gpgpu_context.ts | 3 +-- src/ops/arithmetic_test.ts | 2 +- src/webgl.ts | 1 - 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 447ab5b358..9864e6d8df 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -15,7 +15,11 @@ * ============================================================================= */ -const contexts: {[key: string]: WebGLRenderingContext} = {}; +// const contexts: {[key: string]: WebGLRenderingContext} = {}; + +// TODO(kreeger): Make this an enum +let version = 0; +let context: WebGLRenderingContext = null; const WEBGL_ATTRIBUTES: WebGLContextAttributes = { alpha: false, @@ -27,21 +31,23 @@ const WEBGL_ATTRIBUTES: WebGLContextAttributes = { failIfMajorPerformanceCaveat: true }; -export function setWebGLContext( - webGLVersion: number, gl: WebGLRenderingContext) { - contexts[webGLVersion] = gl; -} +// export function setWebGLContext( +// webGLVersion: number, gl: WebGLRenderingContext) { +// contexts[webGLVersion] = gl; +// } +// TODO(kreeger): drop version request? export function getWebGLContext(webGLVersion: number): WebGLRenderingContext { - if (!(webGLVersion in contexts)) { - contexts[webGLVersion] = getWebGLRenderingContext(webGLVersion); - } - const gl = contexts[webGLVersion]; - if (gl.isContextLost()) { - delete contexts[webGLVersion]; - return getWebGLContext(webGLVersion); + if (context !== null) { + if (webGLVersion === version && !context.isContextLost()) { + return context; + } + context = null; // TODO(cleanup/dispose?) } + // Context doesn't match requested version or has lost context: + const gl = getWebGLRenderingContext(webGLVersion); + gl.disable(gl.DEPTH_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.BLEND); @@ -52,7 +58,9 @@ export function getWebGLContext(webGLVersion: number): WebGLRenderingContext { gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); - return contexts[webGLVersion]; + context = gl; + version = webGLVersion; + return context; } export function createCanvas(webGLVersion: number) { @@ -73,7 +81,8 @@ function getWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - delete contexts[webGLVersion]; + context = null; + // delete contexts[webGLVersion]; }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index a86ad11d95..53d556e5a7 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -19,7 +19,7 @@ import {ENV} from '../../environment'; import {PixelData, TypedArray} from '../../types'; import * as util from '../../util'; -import {getWebGLContext, setWebGLContext} from './canvas_util'; +import {getWebGLContext} from './canvas_util'; import * as gpgpu_util from './gpgpu_util'; import {TextureConfig} from './gpgpu_util'; import * as tex_util from './tex_util'; @@ -52,7 +52,6 @@ export class GPGPUContext { const glVersion = ENV.getNumber('WEBGL_VERSION'); if (gl != null) { this.gl = gl; - setWebGLContext(glVersion, gl); } else { this.gl = getWebGLContext(glVersion); } diff --git a/src/ops/arithmetic_test.ts b/src/ops/arithmetic_test.ts index 185b5b1c78..73b42f7286 100644 --- a/src/ops/arithmetic_test.ts +++ b/src/ops/arithmetic_test.ts @@ -863,7 +863,7 @@ describeWithFlags('pow', ALL_ENVS, () => { await db.data(), [3.0 * Math.pow(4, 1.5) * Math.log(4.0)]); }); - it('gradients: Tensor ^ Tensor', async () => { + it('KREEGER gradients: Tensor ^ Tensor', async () => { const a = tf.tensor1d([-1, .5, 2]); const b = tf.tensor1d([3, 2, -1], 'int32'); const dy = tf.tensor1d([1, 5, 10]); diff --git a/src/webgl.ts b/src/webgl.ts index 183068da05..e21c38a236 100644 --- a/src/webgl.ts +++ b/src/webgl.ts @@ -19,7 +19,6 @@ import * as gpgpu_util from './backends/webgl/gpgpu_util'; import * as webgl_util from './backends/webgl/webgl_util'; export {MathBackendWebGL, WebGLMemoryInfo, WebGLTimingInfo} from './backends/webgl/backend_webgl'; -export {setWebGLContext} from './backends/webgl/canvas_util'; export {GPGPUContext} from './backends/webgl/gpgpu_context'; export {GPGPUProgram} from './backends/webgl/gpgpu_math'; // WebGL specific utils. From 28448713c4627009ae375c95551e7dca5e7b8ab8 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Fri, 19 Jul 2019 09:09:35 -0700 Subject: [PATCH 02/28] save --- src/backends/webgl/gpgpu_context.ts | 1 + src/backends/webgl/webgl_util.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index 53d556e5a7..427e448945 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -52,6 +52,7 @@ export class GPGPUContext { const glVersion = ENV.getNumber('WEBGL_VERSION'); if (gl != null) { this.gl = gl; + // TODO - this sucks for the reference for everything using canvas_util.ts } else { this.gl = getWebGLContext(glVersion); } diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index f35ce52082..b5ebe5c92b 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -542,6 +542,9 @@ function hasExtension(gl: WebGLRenderingContext, extensionName: string) { } export function isWebGLVersionEnabled(webGLVersion: 1|2) { + // + // TODO(kreeger): feature flag check here. + // try { const gl = getWebGLContext(webGLVersion); if (gl != null) { From 6948cb1537494b60beefca181f9494f5e5aeda7d Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Fri, 19 Jul 2019 09:18:06 -0700 Subject: [PATCH 03/28] reset --- src/backends/webgl/canvas_util.ts | 37 +++++++++++------------------ src/backends/webgl/gpgpu_context.ts | 4 ++-- src/backends/webgl/webgl_util.ts | 3 --- src/ops/arithmetic_test.ts | 2 +- src/webgl.ts | 1 + 5 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 9864e6d8df..447ab5b358 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -15,11 +15,7 @@ * ============================================================================= */ -// const contexts: {[key: string]: WebGLRenderingContext} = {}; - -// TODO(kreeger): Make this an enum -let version = 0; -let context: WebGLRenderingContext = null; +const contexts: {[key: string]: WebGLRenderingContext} = {}; const WEBGL_ATTRIBUTES: WebGLContextAttributes = { alpha: false, @@ -31,22 +27,20 @@ const WEBGL_ATTRIBUTES: WebGLContextAttributes = { failIfMajorPerformanceCaveat: true }; -// export function setWebGLContext( -// webGLVersion: number, gl: WebGLRenderingContext) { -// contexts[webGLVersion] = gl; -// } +export function setWebGLContext( + webGLVersion: number, gl: WebGLRenderingContext) { + contexts[webGLVersion] = gl; +} -// TODO(kreeger): drop version request? export function getWebGLContext(webGLVersion: number): WebGLRenderingContext { - if (context !== null) { - if (webGLVersion === version && !context.isContextLost()) { - return context; - } - context = null; // TODO(cleanup/dispose?) + if (!(webGLVersion in contexts)) { + contexts[webGLVersion] = getWebGLRenderingContext(webGLVersion); + } + const gl = contexts[webGLVersion]; + if (gl.isContextLost()) { + delete contexts[webGLVersion]; + return getWebGLContext(webGLVersion); } - - // Context doesn't match requested version or has lost context: - const gl = getWebGLRenderingContext(webGLVersion); gl.disable(gl.DEPTH_TEST); gl.disable(gl.STENCIL_TEST); @@ -58,9 +52,7 @@ export function getWebGLContext(webGLVersion: number): WebGLRenderingContext { gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); - context = gl; - version = webGLVersion; - return context; + return contexts[webGLVersion]; } export function createCanvas(webGLVersion: number) { @@ -81,8 +73,7 @@ function getWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - context = null; - // delete contexts[webGLVersion]; + delete contexts[webGLVersion]; }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index 427e448945..a86ad11d95 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -19,7 +19,7 @@ import {ENV} from '../../environment'; import {PixelData, TypedArray} from '../../types'; import * as util from '../../util'; -import {getWebGLContext} from './canvas_util'; +import {getWebGLContext, setWebGLContext} from './canvas_util'; import * as gpgpu_util from './gpgpu_util'; import {TextureConfig} from './gpgpu_util'; import * as tex_util from './tex_util'; @@ -52,7 +52,7 @@ export class GPGPUContext { const glVersion = ENV.getNumber('WEBGL_VERSION'); if (gl != null) { this.gl = gl; - // TODO - this sucks for the reference for everything using canvas_util.ts + setWebGLContext(glVersion, gl); } else { this.gl = getWebGLContext(glVersion); } diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index b5ebe5c92b..f35ce52082 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -542,9 +542,6 @@ function hasExtension(gl: WebGLRenderingContext, extensionName: string) { } export function isWebGLVersionEnabled(webGLVersion: 1|2) { - // - // TODO(kreeger): feature flag check here. - // try { const gl = getWebGLContext(webGLVersion); if (gl != null) { diff --git a/src/ops/arithmetic_test.ts b/src/ops/arithmetic_test.ts index 73b42f7286..185b5b1c78 100644 --- a/src/ops/arithmetic_test.ts +++ b/src/ops/arithmetic_test.ts @@ -863,7 +863,7 @@ describeWithFlags('pow', ALL_ENVS, () => { await db.data(), [3.0 * Math.pow(4, 1.5) * Math.log(4.0)]); }); - it('KREEGER gradients: Tensor ^ Tensor', async () => { + it('gradients: Tensor ^ Tensor', async () => { const a = tf.tensor1d([-1, .5, 2]); const b = tf.tensor1d([3, 2, -1], 'int32'); const dy = tf.tensor1d([1, 5, 10]); diff --git a/src/webgl.ts b/src/webgl.ts index e21c38a236..183068da05 100644 --- a/src/webgl.ts +++ b/src/webgl.ts @@ -19,6 +19,7 @@ import * as gpgpu_util from './backends/webgl/gpgpu_util'; import * as webgl_util from './backends/webgl/webgl_util'; export {MathBackendWebGL, WebGLMemoryInfo, WebGLTimingInfo} from './backends/webgl/backend_webgl'; +export {setWebGLContext} from './backends/webgl/canvas_util'; export {GPGPUContext} from './backends/webgl/gpgpu_context'; export {GPGPUProgram} from './backends/webgl/gpgpu_math'; // WebGL specific utils. From f068cfc8ddc0dcca3d112ac7cd1186ec5cb0fd3c Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Fri, 19 Jul 2019 15:24:46 -0700 Subject: [PATCH 04/28] Move all GL reference to global class. --- src/backends/webgl/backend_webgl.ts | 29 ++-- src/backends/webgl/canvas_util.ts | 38 +---- src/backends/webgl/canvas_util_test.ts | 47 +++--- src/backends/webgl/clip_gpu.ts | 6 +- src/backends/webgl/clip_packed_gpu.ts | 6 +- src/backends/webgl/fill_gpu.ts | 3 +- src/backends/webgl/flags_webgl_test.ts | 50 +++--- src/backends/webgl/gpgpu_context.ts | 164 ++++++++++++-------- src/backends/webgl/gpgpu_context_test.ts | 10 +- src/backends/webgl/gpgpu_math.ts | 12 +- src/backends/webgl/gpgpu_util_test.ts | 119 +++++++------- src/backends/webgl/multinomial_gpu.ts | 3 +- src/backends/webgl/slice_gpu.ts | 3 +- src/backends/webgl/slice_packed_gpu.ts | 7 +- src/backends/webgl/webgl_context_manager.ts | 85 ++++++++++ src/backends/webgl/webgl_util.ts | 24 ++- src/platforms/platform_browser.ts | 7 + src/webgl.ts | 1 - 18 files changed, 373 insertions(+), 241 deletions(-) create mode 100644 src/backends/webgl/webgl_context_manager.ts diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index efc85a2960..bbf2386144 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -62,7 +62,6 @@ import * as binaryop_gpu from './binaryop_gpu'; import {BinaryOpProgram} from './binaryop_gpu'; import * as binaryop_packed_gpu from './binaryop_packed_gpu'; import {BinaryOpPackedProgram} from './binaryop_packed_gpu'; -import {createCanvas, getWebGLContext} from './canvas_util'; import {ClipProgram} from './clip_gpu'; import {ClipPackedProgram} from './clip_packed_gpu'; import {ComplexAbsProgram} from './complex_abs_gpu'; @@ -221,7 +220,6 @@ export class MathBackendWebGL implements KernelBackend { private dataRefCount = new WeakMap(); private numBytesInGPU = 0; - private canvas: HTMLCanvasElement; private fromPixels2DContext: CanvasRenderingContext2D| OffscreenCanvasRenderingContext2D; @@ -248,15 +246,15 @@ export class MathBackendWebGL implements KernelBackend { } if (gpgpu == null) { - const gl = getWebGLContext(ENV.getNumber('WEBGL_VERSION')); + // TODO(kreeger): Fix this. this.binaryCache = getBinaryCache(ENV.getNumber('WEBGL_VERSION')); - this.gpgpu = new GPGPUContext(gl); - this.canvas = gl.canvas; + this.gpgpu = new GPGPUContext(); + // this.canvas = gl.canvas; this.gpgpuCreatedLocally = true; } else { this.binaryCache = {}; this.gpgpuCreatedLocally = false; - this.canvas = gpgpu.gl.canvas; + // this.canvas = gpgpu.gl.canvas; } this.textureManager = new TextureManager(this.gpgpu); this.numMBBeforeWarning = numMBBeforeWarning(); @@ -313,8 +311,9 @@ export class MathBackendWebGL implements KernelBackend { 'on the document object'); } //@ts-ignore - this.fromPixels2DContext = - createCanvas(ENV.getNumber('WEBGL_VERSION')).getContext('2d'); + // TODO(kreeger): Fix this. + // this.fromPixels2DContext = + // createCanvas(ENV.getNumber('WEBGL_VERSION')).getContext('2d'); } this.fromPixels2DContext.canvas.width = pixels.width; this.fromPixels2DContext.canvas.height = pixels.height; @@ -2500,11 +2499,15 @@ export class MathBackendWebGL implements KernelBackend { return; } this.textureManager.dispose(); - if (this.canvas != null && this.canvas.remove != null) { - this.canvas.remove(); - } else { - this.canvas = null; - } + + // TODO(kreeger): This should be cleaned up in the WebGLContextManager + // right? + // if (this.canvas != null && this.canvas.remove != null) { + // this.canvas.remove(); + // } else { + // this.canvas = null; + // } + if (this.fromPixels2DContext != null && //@ts-ignore this.fromPixels2DContext.canvas.remove) { diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 447ab5b358..ecbd60e48d 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -15,8 +15,6 @@ * ============================================================================= */ -const contexts: {[key: string]: WebGLRenderingContext} = {}; - const WEBGL_ATTRIBUTES: WebGLContextAttributes = { alpha: false, antialias: false, @@ -27,35 +25,7 @@ const WEBGL_ATTRIBUTES: WebGLContextAttributes = { failIfMajorPerformanceCaveat: true }; -export function setWebGLContext( - webGLVersion: number, gl: WebGLRenderingContext) { - contexts[webGLVersion] = gl; -} - -export function getWebGLContext(webGLVersion: number): WebGLRenderingContext { - if (!(webGLVersion in contexts)) { - contexts[webGLVersion] = getWebGLRenderingContext(webGLVersion); - } - const gl = contexts[webGLVersion]; - if (gl.isContextLost()) { - delete contexts[webGLVersion]; - return getWebGLContext(webGLVersion); - } - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.STENCIL_TEST); - gl.disable(gl.BLEND); - gl.disable(gl.DITHER); - gl.disable(gl.POLYGON_OFFSET_FILL); - gl.disable(gl.SAMPLE_COVERAGE); - gl.enable(gl.SCISSOR_TEST); - gl.enable(gl.CULL_FACE); - gl.cullFace(gl.BACK); - - return contexts[webGLVersion]; -} - -export function createCanvas(webGLVersion: number) { +function createCanvas(webGLVersion: number) { if (typeof OffscreenCanvas !== 'undefined' && webGLVersion === 2) { return new OffscreenCanvas(300, 150); } else if (typeof document !== 'undefined') { @@ -65,7 +35,8 @@ export function createCanvas(webGLVersion: number) { } } -function getWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { +export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): + WebGLRenderingContext { if (webGLVersion !== 1 && webGLVersion !== 2) { throw new Error('Cannot get WebGL rendering context, WebGL is disabled.'); } @@ -73,7 +44,8 @@ function getWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - delete contexts[webGLVersion]; + // TODO(kreeger): callback to manager??? + // delete contexts[webGLVersion]; }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || diff --git a/src/backends/webgl/canvas_util_test.ts b/src/backends/webgl/canvas_util_test.ts index e4e01ec072..dacc2ffa27 100644 --- a/src/backends/webgl/canvas_util_test.ts +++ b/src/backends/webgl/canvas_util_test.ts @@ -15,30 +15,31 @@ * ============================================================================= */ -import {ENV} from '../../environment'; -import {BROWSER_ENVS, describeWithFlags} from '../../jasmine_util'; +// import {ENV} from '../../environment'; +// import {BROWSER_ENVS, describeWithFlags} from '../../jasmine_util'; -import {getWebGLContext} from './canvas_util'; +// import {getWebGLContext} from './canvas_util'; -describeWithFlags('canvas_util', BROWSER_ENVS, () => { - it('Returns a valid canvas', () => { - const canvas = getWebGLContext(ENV.getNumber('WEBGL_VERSION')).canvas as ( - HTMLCanvasElement | OffscreenCanvas); - expect( - (canvas instanceof HTMLCanvasElement) || - (canvas instanceof OffscreenCanvas)) - .toBe(true); - }); +// describeWithFlags('canvas_util', BROWSER_ENVS, () => { +// it('Returns a valid canvas', () => { +// const canvas = getWebGLContext(ENV.getNumber('WEBGL_VERSION')).canvas as +// ( +// HTMLCanvasElement | OffscreenCanvas); +// expect( +// (canvas instanceof HTMLCanvasElement) || +// (canvas instanceof OffscreenCanvas)) +// .toBe(true); +// }); - it('Returns a valid gl context', () => { - const gl = getWebGLContext(ENV.getNumber('WEBGL_VERSION')); - expect(gl.isContextLost()).toBe(false); - }); -}); +// it('Returns a valid gl context', () => { +// const gl = getWebGLContext(ENV.getNumber('WEBGL_VERSION')); +// expect(gl.isContextLost()).toBe(false); +// }); +// }); -describeWithFlags('canvas_util webgl2', {flags: {WEBGL_VERSION: 2}}, () => { - it('is ok when the user requests webgl 1 canvas', () => { - const canvas = getWebGLContext(1).canvas; - expect((canvas instanceof HTMLCanvasElement)).toBe(true); - }); -}); +// describeWithFlags('canvas_util webgl2', {flags: {WEBGL_VERSION: 2}}, () => { +// it('is ok when the user requests webgl 1 canvas', () => { +// const canvas = getWebGLContext(1).canvas; +// expect((canvas instanceof HTMLCanvasElement)).toBe(true); +// }); +// }); diff --git a/src/backends/webgl/clip_gpu.ts b/src/backends/webgl/clip_gpu.ts index bb9dde1b9e..8fe30e1cd7 100644 --- a/src/backends/webgl/clip_gpu.ts +++ b/src/backends/webgl/clip_gpu.ts @@ -17,6 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; +import {WebGLContextManager} from './webgl_context_manager'; export class ClipProgram implements GPGPUProgram { variableNames = ['A']; @@ -51,8 +52,9 @@ export class ClipProgram implements GPGPUProgram { this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min'); this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max'); } - gpgpu.gl.uniform1f(this.minLoc, min); - gpgpu.gl.uniform1f(this.maxLoc, max); + const gl = WebGLContextManager.getActiveContext(); + gl.uniform1f(this.minLoc, min); + gl.uniform1f(this.maxLoc, max); }; } } diff --git a/src/backends/webgl/clip_packed_gpu.ts b/src/backends/webgl/clip_packed_gpu.ts index 566eb088cf..d90ff39cab 100644 --- a/src/backends/webgl/clip_packed_gpu.ts +++ b/src/backends/webgl/clip_packed_gpu.ts @@ -17,6 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; +import {WebGLContextManager} from './webgl_context_manager'; export class ClipPackedProgram implements GPGPUProgram { variableNames = ['A']; @@ -53,8 +54,9 @@ export class ClipPackedProgram implements GPGPUProgram { this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min'); this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max'); } - gpgpu.gl.uniform1f(this.minLoc, min); - gpgpu.gl.uniform1f(this.maxLoc, max); + const gl = WebGLContextManager.getActiveContext(); + gl.uniform1f(this.minLoc, min); + gl.uniform1f(this.maxLoc, max); }; } } diff --git a/src/backends/webgl/fill_gpu.ts b/src/backends/webgl/fill_gpu.ts index 3822ed0746..8348a0ae55 100644 --- a/src/backends/webgl/fill_gpu.ts +++ b/src/backends/webgl/fill_gpu.ts @@ -17,6 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; +import {WebGLContextManager} from './webgl_context_manager'; export class FillProgram implements GPGPUProgram { variableNames: string[]; @@ -43,7 +44,7 @@ export class FillProgram implements GPGPUProgram { if (this.valueLoc == null) { this.valueLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'value'); } - gpgpu.gl.uniform1f(this.valueLoc, value); + WebGLContextManager.getActiveContext().uniform1f(this.valueLoc, value); }; } } diff --git a/src/backends/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts index 2cd07d0ac3..57643e540f 100644 --- a/src/backends/webgl/flags_webgl_test.ts +++ b/src/backends/webgl/flags_webgl_test.ts @@ -19,8 +19,6 @@ import * as device_util from '../../device_util'; import {ENV} from '../../environment'; import {webgl_util} from '../../webgl'; -import * as canvas_util from './canvas_util'; - describe('HAS_WEBGL', () => { beforeEach(() => ENV.reset()); afterAll(() => ENV.reset()); @@ -197,15 +195,16 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { ENV.reset(); webgl_util.MAX_TEXTURE_SIZE = null; - spyOn(canvas_util, 'getWebGLContext').and.returnValue({ - MAX_TEXTURE_SIZE: 101, - getParameter: (param: number) => { - if (param === 101) { - return 50; - } - throw new Error(`Got undefined param ${param}.`); - } - }); + // TODO(kreeger): Fix this + // spyOn(canvas_util, 'getWebGLContext').and.returnValue({ + // MAX_TEXTURE_SIZE: 101, + // getParameter: (param: number) => { + // if (param === 101) { + // return 50; + // } + // throw new Error(`Got undefined param ${param}.`); + // } + // }); }); afterAll(() => { ENV.reset(); @@ -218,22 +217,23 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { }); describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { - let maxTextures: number; + // let maxTextures: number; beforeEach(() => { ENV.reset(); webgl_util.MAX_TEXTURES_IN_SHADER = null; - spyOn(canvas_util, 'getWebGLContext').and.callFake(() => { - return { - MAX_TEXTURE_IMAGE_UNITS: 101, - getParameter: (param: number) => { - if (param === 101) { - return maxTextures; - } - throw new Error(`Got undefined param ${param}.`); - } - }; - }); + // TODO(kreeger): Fix this. + // spyOn(canvas_util, 'getWebGLContext').and.callFake(() => { + // return { + // MAX_TEXTURE_IMAGE_UNITS: 101, + // getParameter: (param: number) => { + // if (param === 101) { + // return maxTextures; + // } + // throw new Error(`Got undefined param ${param}.`); + // } + // }; + // }); }); afterAll(() => { ENV.reset(); @@ -241,12 +241,12 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { }); it('is a function of gl.getParameter(MAX_TEXTURE_IMAGE_UNITS)', () => { - maxTextures = 10; + // maxTextures = 10; expect(ENV.getNumber('WEBGL_MAX_TEXTURES_IN_SHADER')).toBe(10); }); it('is capped at 16', () => { - maxTextures = 20; + // maxTextures = 20; expect(ENV.getNumber('WEBGL_MAX_TEXTURES_IN_SHADER')).toBe(16); }); }); diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index a86ad11d95..ccce7e9c81 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -19,10 +19,10 @@ import {ENV} from '../../environment'; import {PixelData, TypedArray} from '../../types'; import * as util from '../../util'; -import {getWebGLContext, setWebGLContext} from './canvas_util'; import * as gpgpu_util from './gpgpu_util'; import {TextureConfig} from './gpgpu_util'; import * as tex_util from './tex_util'; +import {WebGLContextManager} from './webgl_context_manager'; import {WebGL1DisjointQueryTimerExtension, WebGL2DisjointQueryTimerExtension} from './webgl_types'; import * as webgl_util from './webgl_util'; @@ -32,7 +32,7 @@ export interface FenceContext { } export class GPGPUContext { - gl: WebGLRenderingContext; + // gl: WebGLRenderingContext; textureFloatExtension: {}; textureHalfFloatExtension: {}; colorBufferFloatExtension: {}; @@ -48,38 +48,33 @@ export class GPGPUContext { private disjoint: boolean; private textureConfig: TextureConfig; - constructor(gl?: WebGLRenderingContext) { - const glVersion = ENV.getNumber('WEBGL_VERSION'); - if (gl != null) { - this.gl = gl; - setWebGLContext(glVersion, gl); - } else { - this.gl = getWebGLContext(glVersion); - } + constructor() { + const gl = WebGLContextManager.getActiveContext(); + // WebGL 2.0 enables texture floats without an extension. if (ENV.getNumber('WEBGL_VERSION') === 1) { - this.textureFloatExtension = webgl_util.getExtensionOrThrow( - this.gl, this.debug, 'OES_texture_float'); + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(gl, this.debug, 'OES_texture_float'); this.colorBufferFloatExtension = - this.gl.getExtension('WEBGL_color_buffer_float'); + gl.getExtension('WEBGL_color_buffer_float'); if (!ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED')) { this.textureHalfFloatExtension = webgl_util.getExtensionOrThrow( - this.gl, this.debug, 'OES_texture_half_float'); + gl, this.debug, 'OES_texture_half_float'); this.colorBufferHalfFloatExtension = - this.gl.getExtension('EXT_color_buffer_half_float'); + gl.getExtension('EXT_color_buffer_half_float'); } } else { this.colorBufferFloatExtension = webgl_util.getExtensionOrThrow( - this.gl, this.debug, 'EXT_color_buffer_float'); + gl, this.debug, 'EXT_color_buffer_float'); } - this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl, this.debug); - this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl, this.debug); - this.framebuffer = webgl_util.createFramebuffer(this.gl, this.debug); + this.vertexBuffer = gpgpu_util.createVertexBuffer(gl, this.debug); + this.indexBuffer = gpgpu_util.createIndexBuffer(gl, this.debug); + this.framebuffer = webgl_util.createFramebuffer(gl, this.debug); this.textureConfig = - gpgpu_util.getTextureConfig(this.gl, this.textureHalfFloatExtension); + gpgpu_util.getTextureConfig(gl, this.textureHalfFloatExtension); } private get debug(): boolean { @@ -103,7 +98,7 @@ export class GPGPUContext { 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + 'disposing.'); } - const gl = this.gl; + const gl = WebGLContextManager.getActiveContext(); webgl_util.callAndCheck(gl, this.debug, () => gl.finish()); webgl_util.callAndCheck( gl, this.debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); @@ -122,60 +117,68 @@ export class GPGPUContext { WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createFloat32MatrixTexture( - this.gl, this.debug, rows, columns, this.textureConfig); + WebGLContextManager.getActiveContext(), this.debug, rows, columns, + this.textureConfig); } public createFloat16MatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createFloat16MatrixTexture( - this.gl, this.debug, rows, columns, this.textureConfig); + WebGLContextManager.getActiveContext(), this.debug, rows, columns, + this.textureConfig); } public createUnsignedBytesMatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createUnsignedBytesMatrixTexture( - this.gl, this.debug, rows, columns, this.textureConfig); + WebGLContextManager.getActiveContext(), this.debug, rows, columns, + this.textureConfig); } public uploadPixelDataToTexture( texture: WebGLTexture, pixels: PixelData|ImageData|HTMLImageElement|HTMLCanvasElement) { this.throwIfDisposed(); - gpgpu_util.uploadPixelDataToTexture(this.gl, this.debug, texture, pixels); + gpgpu_util.uploadPixelDataToTexture( + WebGLContextManager.getActiveContext(), this.debug, texture, pixels); } public uploadDenseMatrixToTexture( texture: WebGLTexture, width: number, height: number, data: TypedArray) { this.throwIfDisposed(); gpgpu_util.uploadDenseMatrixToTexture( - this.gl, this.debug, texture, width, height, data, this.textureConfig); + WebGLContextManager.getActiveContext(), this.debug, texture, width, + height, data, this.textureConfig); } public createFloat16PackedMatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createFloat16PackedMatrixTexture( - this.gl, this.debug, rows, columns, this.textureConfig); + WebGLContextManager.getActiveContext(), this.debug, rows, columns, + this.textureConfig); } public createPackedMatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createPackedMatrixTexture( - this.gl, this.debug, rows, columns, this.textureConfig); + WebGLContextManager.getActiveContext(), this.debug, rows, columns, + this.textureConfig); } public deleteMatrixTexture(texture: WebGLTexture) { this.throwIfDisposed(); if (this.outputTexture === texture) { webgl_util.unbindColorTextureFromFramebuffer( - this.gl, this.debug, this.framebuffer); + WebGLContextManager.getActiveContext(), this.debug, this.framebuffer); this.outputTexture = null; } webgl_util.callAndCheck( - this.gl, this.debug, () => this.gl.deleteTexture(texture)); + WebGLContextManager.getActiveContext(), this.debug, + () => WebGLContextManager.getActiveContext().deleteTexture(texture)); } public downloadByteEncodedFloatMatrixFromOutputTexture( @@ -183,34 +186,37 @@ export class GPGPUContext { return this.downloadMatrixDriver( texture, () => gpgpu_util.downloadByteEncodedFloatMatrixFromOutputTexture( - this.gl, this.debug, rows, columns, this.textureConfig)); + WebGLContextManager.getActiveContext(), this.debug, rows, columns, + this.textureConfig)); } public downloadPackedMatrixFromBuffer( buffer: WebGLBuffer, batch: number, rows: number, columns: number, physicalRows: number, physicalCols: number): Float32Array { return gpgpu_util.downloadPackedMatrixFromBuffer( - this.gl, buffer, batch, rows, columns, physicalRows, physicalCols, - this.textureConfig); + WebGLContextManager.getActiveContext(), buffer, batch, rows, columns, + physicalRows, physicalCols, this.textureConfig); } public downloadFloat32MatrixFromBuffer(buffer: WebGLBuffer, size: number): Float32Array { - return gpgpu_util.downloadFloat32MatrixFromBuffer(this.gl, buffer, size); + return gpgpu_util.downloadFloat32MatrixFromBuffer( + WebGLContextManager.getActiveContext(), buffer, size); } public createBufferFromTexture( texture: WebGLTexture, rows: number, columns: number): WebGLBuffer { this.bindTextureToFrameBuffer(texture); const result = gpgpu_util.createBufferFromOutputTexture( - this.gl as WebGL2RenderingContext, this.debug, rows, columns, - this.textureConfig); + WebGLContextManager.getActiveContext() as WebGL2RenderingContext, + this.debug, rows, columns, this.textureConfig); this.unbindTextureToFrameBuffer(); return result; } public createAndWaitForFence(): Promise { - const fenceContext = this.createFence(this.gl); + const fenceContext = + this.createFence(WebGLContextManager.getActiveContext()); return this.pollFence(fenceContext); } @@ -254,14 +260,15 @@ export class GPGPUContext { return this.downloadMatrixDriver( texture, () => gpgpu_util.downloadMatrixFromPackedOutputTexture( - this.gl, this.debug, physicalRows, physicalCols)); + WebGLContextManager.getActiveContext(), this.debug, physicalRows, + physicalCols)); } private vertexAttrsAreBound = false; public createProgram(fragmentShaderSource: string): WebGLProgram { this.throwIfDisposed(); - const gl = this.gl; + const gl = WebGLContextManager.getActiveContext(); const fragmentShader: WebGLShader = webgl_util.createFragmentShader(gl, this.debug, fragmentShaderSource); const vertexShader: WebGLShader = @@ -293,7 +300,10 @@ export class GPGPUContext { } if (program != null) { webgl_util.callAndCheck( - this.gl, this.debug, () => this.gl.deleteProgram(program)); + WebGLContextManager.getActiveContext(), this.debug, + () => WebGLContextManager.getInstance() + .getActiveContext() + .deleteProgram(program)); } } @@ -301,10 +311,12 @@ export class GPGPUContext { this.throwIfDisposed(); this.program = program; if ((this.program != null) && this.debug) { - webgl_util.validateProgram(this.gl, this.debug, this.program); + webgl_util.validateProgram( + WebGLContextManager.getActiveContext(), this.debug, this.program); } webgl_util.callAndCheck( - this.gl, this.debug, () => this.gl.useProgram(program)); + WebGLContextManager.getActiveContext(), this.debug, + () => WebGLContextManager.getActiveContext().useProgram(program)); } public getUniformLocation( @@ -313,10 +325,11 @@ export class GPGPUContext { this.throwIfDisposed(); if (shouldThrow) { return webgl_util.getProgramUniformLocationOrThrow( - this.gl, this.debug, program, uniformName); + WebGLContextManager.getActiveContext(), this.debug, program, + uniformName); } else { return webgl_util.getProgramUniformLocation( - this.gl, program, uniformName); + WebGLContextManager.getActiveContext(), program, uniformName); } } @@ -324,14 +337,18 @@ export class GPGPUContext { number { this.throwIfDisposed(); return webgl_util.callAndCheck( - this.gl, this.debug, - () => this.gl.getAttribLocation(program, attribute)); + WebGLContextManager.getActiveContext(), this.debug, + () => WebGLContextManager.getInstance() + .getActiveContext() + .getAttribLocation(program, attribute)); } public getUniformLocationNoThrow(program: WebGLProgram, uniformName: string): WebGLUniformLocation { this.throwIfDisposed(); - return this.gl.getUniformLocation(program, uniformName); + return WebGLContextManager.getInstance() + .getActiveContext() + .getUniformLocation(program, uniformName); } public setInputMatrixTexture( @@ -340,8 +357,8 @@ export class GPGPUContext { this.throwIfDisposed(); this.throwIfNoProgram(); webgl_util.bindTextureToProgramUniformSampler( - this.gl, this.debug, this.program, inputMatrixTexture, uniformLocation, - textureUnit); + WebGLContextManager.getActiveContext(), this.debug, this.program, + inputMatrixTexture, uniformLocation, textureUnit); } public setOutputMatrixTexture( @@ -372,15 +389,16 @@ export class GPGPUContext { public debugValidate() { if (this.program != null) { - webgl_util.validateProgram(this.gl, this.debug, this.program); + webgl_util.validateProgram( + WebGLContextManager.getActiveContext(), this.debug, this.program); } - webgl_util.validateFramebuffer(this.gl); + webgl_util.validateFramebuffer(WebGLContextManager.getActiveContext()); } public executeProgram() { this.throwIfDisposed(); this.throwIfNoProgram(); - const gl = this.gl; + const gl = WebGLContextManager.getActiveContext(); if (this.debug) { this.debugValidate(); } @@ -391,7 +409,9 @@ export class GPGPUContext { public blockUntilAllProgramsCompleted() { this.throwIfDisposed(); - webgl_util.callAndCheck(this.gl, this.debug, () => this.gl.finish()); + webgl_util.callAndCheck( + WebGLContextManager.getActiveContext(), this.debug, + () => WebGLContextManager.getActiveContext().finish()); } private getQueryTimerExtension(): WebGL1DisjointQueryTimerExtension @@ -399,7 +419,7 @@ export class GPGPUContext { if (this.disjointQueryTimerExtension == null) { this.disjointQueryTimerExtension = webgl_util.getExtensionOrThrow( - this.gl, this.debug, + WebGLContextManager.getActiveContext(), this.debug, ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2 ? 'EXT_disjoint_timer_query_webgl2' : @@ -420,7 +440,8 @@ export class GPGPUContext { beginQuery(): WebGLQuery { if (ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { - const gl2 = this.gl as WebGL2RenderingContext; + const gl2 = + WebGLContextManager.getActiveContext() as WebGL2RenderingContext; const ext = this.getQueryTimerExtensionWebGL2(); const query = gl2.createQuery(); @@ -435,7 +456,8 @@ export class GPGPUContext { endQuery() { if (ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { - const gl2 = this.gl as WebGL2RenderingContext; + const gl2 = + WebGLContextManager.getActiveContext() as WebGL2RenderingContext; const ext = this.getQueryTimerExtensionWebGL2(); gl2.endQuery(ext.TIME_ELAPSED_EXT); return; @@ -463,7 +485,8 @@ export class GPGPUContext { } if (queryTimerVersion === 2) { - const gl2 = this.gl as WebGL2RenderingContext; + const gl2 = + WebGLContextManager.getActiveContext() as WebGL2RenderingContext; const timeElapsedNanos = gl2.getQueryParameter(query, gl2.QUERY_RESULT); // Return milliseconds. @@ -485,13 +508,15 @@ export class GPGPUContext { } if (queryTimerVersion === 2) { - const gl2 = this.gl as WebGL2RenderingContext; + const gl2 = + WebGLContextManager.getActiveContext() as WebGL2RenderingContext; const ext = this.getQueryTimerExtensionWebGL2(); const available = gl2.getQueryParameter(query, gl2.QUERY_RESULT_AVAILABLE); if (this.disjoint == null) { - this.disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT); + this.disjoint = WebGLContextManager.getActiveContext().getParameter( + ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; @@ -501,7 +526,8 @@ export class GPGPUContext { const available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); if (this.disjoint == null) { - this.disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT); + this.disjoint = WebGLContextManager.getActiveContext().getParameter( + ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; @@ -543,22 +569,24 @@ export class GPGPUContext { private bindTextureToFrameBuffer(texture: WebGLTexture) { this.throwIfDisposed(); webgl_util.bindColorTextureToFramebuffer( - this.gl, this.debug, texture, this.framebuffer); + WebGLContextManager.getActiveContext(), this.debug, texture, + this.framebuffer); if (this.debug) { - webgl_util.validateFramebuffer(this.gl); + webgl_util.validateFramebuffer(WebGLContextManager.getActiveContext()); } } private unbindTextureToFrameBuffer() { if (this.outputTexture != null) { webgl_util.bindColorTextureToFramebuffer( - this.gl, this.debug, this.outputTexture, this.framebuffer); + WebGLContextManager.getActiveContext(), this.debug, + this.outputTexture, this.framebuffer); if (this.debug) { - webgl_util.validateFramebuffer(this.gl); + webgl_util.validateFramebuffer(WebGLContextManager.getActiveContext()); } } else { webgl_util.unbindColorTextureFromFramebuffer( - this.gl, this.debug, this.framebuffer); + WebGLContextManager.getActiveContext(), this.debug, this.framebuffer); } } @@ -576,7 +604,7 @@ export class GPGPUContext { outputMatrixTextureMaybePacked: WebGLTexture, width: number, height: number) { this.throwIfDisposed(); - const gl = this.gl; + const gl = WebGLContextManager.getActiveContext(); webgl_util.bindColorTextureToFramebuffer( gl, this.debug, outputMatrixTextureMaybePacked, this.framebuffer); if (this.debug) { @@ -593,7 +621,9 @@ export class GPGPUContext { x: number, y: number, width: number, height: number) { this.throwIfDisposed(); webgl_util.callAndCheck( - this.gl, this.debug, () => this.gl.scissor(x, y, width, height)); + WebGLContextManager.getActiveContext(), this.debug, + () => WebGLContextManager.getActiveContext().scissor( + x, y, width, height)); } private throwIfDisposed() { diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index d03bae8f32..269b887a96 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -22,6 +22,7 @@ import {WEBGL_ENVS} from './backend_webgl_test_registry'; import {getGlslDifferences} from './glsl_version'; import {GPGPUContext, linearSearchLastTrue} from './gpgpu_context'; import * as tex_util from './tex_util'; +import {WebGLContextManager} from './webgl_context_manager'; const DOWNLOAD_FLOAT_ENVS = { flags: {'WEBGL_DOWNLOAD_FLOAT_ENABLED': true}, @@ -57,7 +58,8 @@ describeWithFlags( const output = gpgpu.createFloat32MatrixTexture(rows, columns); gpgpu.setOutputMatrixTexture(output, rows, columns); const expected = new Int32Array([0, 0, columns, rows]); - expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + const gl = WebGLContextManager.getActiveContext(); + expect(gl.getParameter(gl.VIEWPORT)).toEqual(expected); gpgpu.deleteMatrixTexture(output); }); }); @@ -95,7 +97,8 @@ describeWithFlags( const [width, height] = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); const expected = new Int32Array([0, 0, width, height]); - expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + const gl = WebGLContextManager.getActiveContext(); + expect(gl.getParameter(gl.VIEWPORT)).toEqual(expected); }); }); @@ -133,7 +136,8 @@ describeWithFlags( it('sets the scissor box to the requested parameters', () => { gpgpu.setOutputMatrixWriteRegion(0, 1, 2, 3); - const scissorBox = gpgpu.gl.getParameter(gpgpu.gl.SCISSOR_BOX); + const gl = WebGLContextManager.getActiveContext(); + const scissorBox = gl.getParameter(gl.SCISSOR_BOX); expect(scissorBox[0]).toEqual(2); expect(scissorBox[1]).toEqual(0); expect(scissorBox[2]).toEqual(3); diff --git a/src/backends/webgl/gpgpu_math.ts b/src/backends/webgl/gpgpu_math.ts index a284da2550..52c07d9061 100644 --- a/src/backends/webgl/gpgpu_math.ts +++ b/src/backends/webgl/gpgpu_math.ts @@ -24,6 +24,7 @@ import {GPGPUContext} from './gpgpu_context'; import * as shader_compiler from './shader_compiler'; import {InputInfo, ShapeInfo} from './shader_compiler'; import {TextureData} from './tex_util'; +import {WebGLContextManager} from './webgl_context_manager'; export interface GPGPUProgram { variableNames: string[]; @@ -162,14 +163,15 @@ export function runProgram( } gpgpu.setProgram(binary.webGLProgram); + const gl = WebGLContextManager.getActiveContext(); // Set special uniforms (NAN, INFINITY) if (ENV.getNumber('WEBGL_VERSION') === 1) { if (binary.infLoc !== null) { - gpgpu.gl.uniform1f(binary.infLoc, Infinity); + gl.uniform1f(binary.infLoc, Infinity); } } if (binary.nanLoc !== null) { - gpgpu.gl.uniform1f(binary.nanLoc, NaN); + gl.uniform1f(binary.nanLoc, NaN); } // Set user-defined inputs @@ -186,20 +188,20 @@ export function runProgram( if (input.isUniform) { // Upload the values of the tensor as uniform. if (util.sizeFromShape(input.shape) < 2) { - gpgpu.gl.uniform1f(varLoc, input.uniformValues[0]); + gl.uniform1f(varLoc, input.uniformValues[0]); } else { let vals = input.uniformValues; if (!(vals instanceof Float32Array)) { vals = new Float32Array(vals); } - gpgpu.gl.uniform1fv(varLoc, vals); + gl.uniform1fv(varLoc, vals); } return; } // If the input was sliced, upload the flat offset index. if (input.texData.slice != null && varOffsetLoc != null) { - gpgpu.gl.uniform1i(varOffsetLoc, input.texData.slice.flatOffset); + gl.uniform1i(varOffsetLoc, input.texData.slice.flatOffset); } gpgpu.setInputMatrixTexture(input.texData.texture, varLoc, i); diff --git a/src/backends/webgl/gpgpu_util_test.ts b/src/backends/webgl/gpgpu_util_test.ts index 553e542dcc..3bba346229 100644 --- a/src/backends/webgl/gpgpu_util_test.ts +++ b/src/backends/webgl/gpgpu_util_test.ts @@ -19,114 +19,127 @@ import {describeWithFlags} from '../../jasmine_util'; import {WEBGL_ENVS} from './backend_webgl_test_registry'; import {GPGPUContext} from './gpgpu_context'; import * as gpgpu_util from './gpgpu_util'; +import {WebGLContextManager} from './webgl_context_manager'; describeWithFlags('gpgpu_util createWebGLContext', WEBGL_ENVS, () => { let gpgpu: GPGPUContext; + let gl: WebGLRenderingContext; beforeEach(() => { gpgpu = new GPGPUContext(); + gl = WebGLContextManager.getActiveContext(); }); afterEach(() => { gpgpu.dispose(); + gl = null; }); it('disables DEPTH_TEST and STENCIL_TEST', () => { - expect(gpgpu.gl.getParameter(gpgpu.gl.DEPTH_TEST)).toEqual(false); - expect(gpgpu.gl.getParameter(gpgpu.gl.STENCIL_TEST)).toEqual(false); + expect(gl.getParameter(gl.DEPTH_TEST)).toEqual(false); + expect(gl.getParameter(gl.STENCIL_TEST)).toEqual(false); }); it('disables BLEND', () => { - expect(gpgpu.gl.getParameter(gpgpu.gl.BLEND)).toEqual(false); + expect(gl.getParameter(gl.BLEND)).toEqual(false); }); it('disables DITHER, POLYGON_OFFSET_FILL', () => { - expect(gpgpu.gl.getParameter(gpgpu.gl.DITHER)).toEqual(false); - expect(gpgpu.gl.getParameter(gpgpu.gl.POLYGON_OFFSET_FILL)).toEqual(false); + expect(gl.getParameter(gl.DITHER)).toEqual(false); + expect(gl.getParameter(gl.POLYGON_OFFSET_FILL)).toEqual(false); }); it('enables CULL_FACE with BACK', () => { - expect(gpgpu.gl.getParameter(gpgpu.gl.CULL_FACE)).toEqual(true); - expect(gpgpu.gl.getParameter(gpgpu.gl.CULL_FACE_MODE)) - .toEqual(gpgpu.gl.BACK); + expect(gl.getParameter(gl.CULL_FACE)).toEqual(true); + expect(gl.getParameter(gl.CULL_FACE_MODE)).toEqual(gl.BACK); }); it('enables SCISSOR_TEST', () => { - expect(gpgpu.gl.getParameter(gpgpu.gl.SCISSOR_TEST)).toEqual(true); + expect(gl.getParameter(gl.SCISSOR_TEST)).toEqual(true); }); }); describeWithFlags('gpgpu_util createFloat32MatrixTexture', WEBGL_ENVS, () => { + let gl: WebGLRenderingContext; + beforeEach(() => { + gl = WebGLContextManager.getActiveContext(); + }); + + afterEach(() => { + gl = null; + }); + it('sets the TEXTURE_WRAP S+T parameters to CLAMP_TO_EDGE', () => { const gpgpu = new GPGPUContext(); - const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl); + const textureConfig = gpgpu_util.getTextureConfig(gl); const debug = false; - const tex = gpgpu_util.createFloat32MatrixTexture( - gpgpu.gl, debug, 32, 32, textureConfig); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); - expect( - gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_S)) - .toEqual(gpgpu.gl.CLAMP_TO_EDGE); - expect( - gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_T)) - .toEqual(gpgpu.gl.CLAMP_TO_EDGE); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + const tex = + gpgpu_util.createFloat32MatrixTexture(gl, debug, 32, 32, textureConfig); + gl.bindTexture(gl.TEXTURE_2D, tex); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)) + .toEqual(gl.CLAMP_TO_EDGE); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T)) + .toEqual(gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); gpgpu.deleteMatrixTexture(tex); gpgpu.dispose(); }); it('sets the TEXTURE_[MIN|MAG]_FILTER parameters to NEAREST', () => { const gpgpu = new GPGPUContext(); - const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl); + const textureConfig = gpgpu_util.getTextureConfig(gl); const debug = false; - const tex = gpgpu_util.createFloat32MatrixTexture( - gpgpu.gl, debug, 32, 32, textureConfig); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); - expect(gpgpu.gl.getTexParameter( - gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MIN_FILTER)) - .toEqual(gpgpu.gl.NEAREST); - expect(gpgpu.gl.getTexParameter( - gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MAG_FILTER)) - .toEqual(gpgpu.gl.NEAREST); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + const tex = + gpgpu_util.createFloat32MatrixTexture(gl, debug, 32, 32, textureConfig); + gl.bindTexture(gl.TEXTURE_2D, tex); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER)) + .toEqual(gl.NEAREST); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER)) + .toEqual(gl.NEAREST); + gl.bindTexture(gl.TEXTURE_2D, null); gpgpu.deleteMatrixTexture(tex); gpgpu.dispose(); }); }); describeWithFlags('gpgpu_util createPackedMatrixTexture', WEBGL_ENVS, () => { + let gl: WebGLRenderingContext; + beforeEach(() => { + gl = WebGLContextManager.getActiveContext(); + }); + + afterEach(() => { + gl = null; + }); + it('sets the TEXTURE_WRAP S+T parameters to CLAMP_TO_EDGE', () => { const gpgpu = new GPGPUContext(); - const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl); + const textureConfig = gpgpu_util.getTextureConfig(gl); const debug = false; - const tex = gpgpu_util.createPackedMatrixTexture( - gpgpu.gl, debug, 32, 32, textureConfig); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); - expect( - gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_S)) - .toEqual(gpgpu.gl.CLAMP_TO_EDGE); - expect( - gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_T)) - .toEqual(gpgpu.gl.CLAMP_TO_EDGE); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + const tex = + gpgpu_util.createPackedMatrixTexture(gl, debug, 32, 32, textureConfig); + gl.bindTexture(gl.TEXTURE_2D, tex); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)) + .toEqual(gl.CLAMP_TO_EDGE); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T)) + .toEqual(gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); gpgpu.deleteMatrixTexture(tex); gpgpu.dispose(); }); it('sets the TEXTURE_[MIN|MAG]_FILTER parameters to NEAREST', () => { const gpgpu = new GPGPUContext(); - const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl); + const textureConfig = gpgpu_util.getTextureConfig(gl); const debug = false; - const tex = gpgpu_util.createPackedMatrixTexture( - gpgpu.gl, debug, 32, 32, textureConfig); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); - expect(gpgpu.gl.getTexParameter( - gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MIN_FILTER)) - .toEqual(gpgpu.gl.NEAREST); - expect(gpgpu.gl.getTexParameter( - gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MAG_FILTER)) - .toEqual(gpgpu.gl.NEAREST); - gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + const tex = + gpgpu_util.createPackedMatrixTexture(gl, debug, 32, 32, textureConfig); + gl.bindTexture(gl.TEXTURE_2D, tex); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER)) + .toEqual(gl.NEAREST); + expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER)) + .toEqual(gl.NEAREST); + gl.bindTexture(gl.TEXTURE_2D, null); gpgpu.deleteMatrixTexture(tex); gpgpu.dispose(); }); diff --git a/src/backends/webgl/multinomial_gpu.ts b/src/backends/webgl/multinomial_gpu.ts index e1c8f1e165..fb89d28239 100644 --- a/src/backends/webgl/multinomial_gpu.ts +++ b/src/backends/webgl/multinomial_gpu.ts @@ -17,6 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; +import {WebGLContextManager} from './webgl_context_manager'; export class MultinomialProgram implements GPGPUProgram { variableNames = ['probs']; @@ -59,7 +60,7 @@ export class MultinomialProgram implements GPGPUProgram { if (this.seedLoc == null) { this.seedLoc = gpgpu.getUniformLocation(webGLProgram, 'seed'); } - gpgpu.gl.uniform1f(this.seedLoc, seed); + WebGLContextManager.getActiveContext().uniform1f(this.seedLoc, seed); }; } } diff --git a/src/backends/webgl/slice_gpu.ts b/src/backends/webgl/slice_gpu.ts index 1547fdd31e..d65766e8bd 100644 --- a/src/backends/webgl/slice_gpu.ts +++ b/src/backends/webgl/slice_gpu.ts @@ -18,6 +18,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; import {getCoordsDataType} from './shader_compiler'; +import {WebGLContextManager} from './webgl_context_manager'; export class SliceProgram implements GPGPUProgram { variableNames = ['source']; @@ -69,7 +70,7 @@ export class SliceProgram implements GPGPUProgram { return; } } - gpgpu.gl.uniform1iv(this.startLoc, start); + WebGLContextManager.getActiveContext().uniform1iv(this.startLoc, start); }; } } diff --git a/src/backends/webgl/slice_packed_gpu.ts b/src/backends/webgl/slice_packed_gpu.ts index 6fa26f58e7..788bb4152e 100644 --- a/src/backends/webgl/slice_packed_gpu.ts +++ b/src/backends/webgl/slice_packed_gpu.ts @@ -20,6 +20,7 @@ import {getChannels} from '../packing_util'; import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; import {getCoordsDataType} from './shader_compiler'; +import {WebGLContextManager} from './webgl_context_manager'; export class SlicePackedProgram implements GPGPUProgram { variableNames = ['source']; @@ -73,7 +74,7 @@ export class SlicePackedProgram implements GPGPUProgram { void main() { ${dtype} coords = getOutputCoords(); ${dtype} sourceLoc; - ${sourceLocSetup} + ${sourceLocSetup} vec4 result = vec4(0.); ${upperRow} ${lowerRow} @@ -97,7 +98,7 @@ export class SlicePackedProgram implements GPGPUProgram { return; } } - gpgpu.gl.uniform1iv(this.startLoc, start); + WebGLContextManager.getActiveContext().uniform1iv(this.startLoc, start); }; } -} \ No newline at end of file +} diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts new file mode 100644 index 0000000000..e8dc56a9ad --- /dev/null +++ b/src/backends/webgl/webgl_context_manager.ts @@ -0,0 +1,85 @@ +/** + * @license + * Copyright 2019 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {ENV} from '../../environment'; + +/** + * Manages global state of all WebGLRenderingContexts. + */ +export class WebGLContextManager { + private static instance: WebGLContextManager; + + contexts: {[key: string]: WebGLRenderingContext} = {}; + factory: (version: number) => WebGLRenderingContext; + + private constructor() {} + + static getInstance(): WebGLContextManager { + if (!WebGLContextManager.instance) { + WebGLContextManager.instance = new WebGLContextManager(); + } + return WebGLContextManager.instance; + } + + static getActiveContext(): WebGLRenderingContext { + return WebGLContextManager.getInstance().getActiveContext(); + } + + /** + * Sets callback for creating new WebGLRenderingContext instances. + * @param factory The callback function that returns a WebGLRenderingContext + * instance. + */ + setContextFactory(factory: (version: number) => WebGLRenderingContext) { + this.factory = factory; + } + + /** + * Returns the current WebGLContext + */ + getActiveContext(): WebGLRenderingContext { + return this.getContextByVersion(ENV.getNumber('WEBGL_VERSION')); + } + + /** + * TODO(kreeger): Doc me. + * @param version The specific version of WebGL to request. + */ + getContextByVersion(version: number): WebGLRenderingContext { + if (!(version in this.contexts)) { + this.contexts[version] = this.factory(version); + this.bootstrapWebGLContext(this.contexts[version]); + } + const gl = this.contexts[version]; + if (gl.isContextLost()) { + delete this.contexts[version]; + return this.getContextByVersion(version); + } + return this.contexts[version]; + } + + private bootstrapWebGLContext(gl: WebGLRenderingContext) { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.STENCIL_TEST); + gl.disable(gl.BLEND); + gl.disable(gl.DITHER); + gl.disable(gl.POLYGON_OFFSET_FILL); + gl.disable(gl.SAMPLE_COVERAGE); + gl.enable(gl.SCISSOR_TEST); + gl.enable(gl.CULL_FACE); + gl.cullFace(gl.BACK); + } +} diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index f35ce52082..6d9182081b 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -17,7 +17,8 @@ import {ENV} from '../../environment'; import * as util from '../../util'; -import {getWebGLContext} from './canvas_util'; +import {WebGLContextManager} from './webgl_context_manager'; +// import {getWebGLContext} from './canvas_util'; export function callAndCheck( gl: WebGLRenderingContext, debugMode: boolean, func: () => T): T { @@ -501,7 +502,8 @@ export let MAX_TEXTURES_IN_SHADER: number; export function getWebGLMaxTextureSize(webGLVersion: number): number { if (MAX_TEXTURE_SIZE == null) { - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE); } return MAX_TEXTURE_SIZE; @@ -509,7 +511,8 @@ export function getWebGLMaxTextureSize(webGLVersion: number): number { export function getMaxTexturesInShader(webGLVersion: number): number { if (MAX_TEXTURES_IN_SHADER == null) { - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); MAX_TEXTURES_IN_SHADER = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); } // We cap at 16 to avoid spurious runtime "memory exhausted" error. @@ -523,7 +526,8 @@ export function getWebGLDisjointQueryTimerVersion(webGLVersion: number): } let queryTimerVersion: number; - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); if (hasExtension(gl, 'EXT_disjoint_timer_query_webgl2') && webGLVersion === 2) { @@ -543,7 +547,8 @@ function hasExtension(gl: WebGLRenderingContext, extensionName: string) { export function isWebGLVersionEnabled(webGLVersion: 1|2) { try { - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); if (gl != null) { return true; } @@ -558,7 +563,8 @@ export function isRenderToFloatTextureEnabled(webGLVersion: number): boolean { return false; } - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { @@ -580,7 +586,8 @@ export function isDownloadFloatTextureEnabled(webGLVersion: number): boolean { return false; } - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { @@ -631,7 +638,8 @@ export function isWebGLFenceEnabled(webGLVersion: number) { if (webGLVersion !== 2) { return false; } - const gl = getWebGLContext(webGLVersion); + const gl = + WebGLContextManager.getInstance().getContextByVersion(webGLVersion); // tslint:disable-next-line:no-any const isEnabled = (gl as any).fenceSync != null; diff --git a/src/platforms/platform_browser.ts b/src/platforms/platform_browser.ts index 25d9d3028a..27d577e217 100644 --- a/src/platforms/platform_browser.ts +++ b/src/platforms/platform_browser.ts @@ -14,7 +14,10 @@ * limitations under the License. * ============================================================================= */ +import {createDOMCanvasWebGLRenderingContext} from '../backends/webgl/canvas_util'; +import {WebGLContextManager} from '../backends/webgl/webgl_context_manager'; import {ENV} from '../environment'; + import {Platform} from './platform'; export class PlatformBrowser implements Platform { @@ -24,6 +27,10 @@ export class PlatformBrowser implements Platform { // According to the spec, the built-in encoder can do only UTF-8 encoding. // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/TextEncoder this.textEncoder = new TextEncoder(); + + // Register default WebGL Context creation: + WebGLContextManager.getInstance().setContextFactory( + createDOMCanvasWebGLRenderingContext) } fetch(path: string, init?: RequestInit): Promise { diff --git a/src/webgl.ts b/src/webgl.ts index 183068da05..e21c38a236 100644 --- a/src/webgl.ts +++ b/src/webgl.ts @@ -19,7 +19,6 @@ import * as gpgpu_util from './backends/webgl/gpgpu_util'; import * as webgl_util from './backends/webgl/webgl_util'; export {MathBackendWebGL, WebGLMemoryInfo, WebGLTimingInfo} from './backends/webgl/backend_webgl'; -export {setWebGLContext} from './backends/webgl/canvas_util'; export {GPGPUContext} from './backends/webgl/gpgpu_context'; export {GPGPUProgram} from './backends/webgl/gpgpu_math'; // WebGL specific utils. From 908c7a33d87d72d70667f44b937056b699e0a620 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Mon, 22 Jul 2019 16:18:29 -0700 Subject: [PATCH 05/28] Fix some tests. --- src/backends/webgl/backend_webgl.ts | 6 ++-- src/backends/webgl/canvas_util.ts | 2 +- src/backends/webgl/flags_webgl_test.ts | 49 +++++++++++++------------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index bbf2386144..add846bb6f 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -62,6 +62,7 @@ import * as binaryop_gpu from './binaryop_gpu'; import {BinaryOpProgram} from './binaryop_gpu'; import * as binaryop_packed_gpu from './binaryop_packed_gpu'; import {BinaryOpPackedProgram} from './binaryop_packed_gpu'; +import {createCanvas} from './canvas_util'; import {ClipProgram} from './clip_gpu'; import {ClipPackedProgram} from './clip_packed_gpu'; import {ComplexAbsProgram} from './complex_abs_gpu'; @@ -311,9 +312,8 @@ export class MathBackendWebGL implements KernelBackend { 'on the document object'); } //@ts-ignore - // TODO(kreeger): Fix this. - // this.fromPixels2DContext = - // createCanvas(ENV.getNumber('WEBGL_VERSION')).getContext('2d'); + this.fromPixels2DContext = + createCanvas(ENV.getNumber('WEBGL_VERSION')).getContext('2d'); } this.fromPixels2DContext.canvas.width = pixels.width; this.fromPixels2DContext.canvas.height = pixels.height; diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index ecbd60e48d..26afbe035d 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -25,7 +25,7 @@ const WEBGL_ATTRIBUTES: WebGLContextAttributes = { failIfMajorPerformanceCaveat: true }; -function createCanvas(webGLVersion: number) { +export function createCanvas(webGLVersion: number) { if (typeof OffscreenCanvas !== 'undefined' && webGLVersion === 2) { return new OffscreenCanvas(300, 150); } else if (typeof document !== 'undefined') { diff --git a/src/backends/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts index 57643e540f..c74d09d5e9 100644 --- a/src/backends/webgl/flags_webgl_test.ts +++ b/src/backends/webgl/flags_webgl_test.ts @@ -18,6 +18,7 @@ import * as device_util from '../../device_util'; import {ENV} from '../../environment'; import {webgl_util} from '../../webgl'; +import {WebGLContextManager} from './webgl_context_manager'; describe('HAS_WEBGL', () => { beforeEach(() => ENV.reset()); @@ -195,16 +196,15 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { ENV.reset(); webgl_util.MAX_TEXTURE_SIZE = null; - // TODO(kreeger): Fix this - // spyOn(canvas_util, 'getWebGLContext').and.returnValue({ - // MAX_TEXTURE_SIZE: 101, - // getParameter: (param: number) => { - // if (param === 101) { - // return 50; - // } - // throw new Error(`Got undefined param ${param}.`); - // } - // }); + spyOn(WebGLContextManager, 'getActiveContext').and.returnValue({ + MAX_TEXTURE_SIZE: 101, + getParameter: (param: number) => { + if (param === 101) { + return 50; + } + throw new Error(`Got undefined param ${param}.`); + } + }); }); afterAll(() => { ENV.reset(); @@ -217,23 +217,22 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { }); describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { - // let maxTextures: number; + let maxTextures: number; beforeEach(() => { ENV.reset(); webgl_util.MAX_TEXTURES_IN_SHADER = null; - // TODO(kreeger): Fix this. - // spyOn(canvas_util, 'getWebGLContext').and.callFake(() => { - // return { - // MAX_TEXTURE_IMAGE_UNITS: 101, - // getParameter: (param: number) => { - // if (param === 101) { - // return maxTextures; - // } - // throw new Error(`Got undefined param ${param}.`); - // } - // }; - // }); + spyOn(WebGLContextManager, 'getActiveContext').and.callFake(() => { + return { + MAX_TEXTURE_IMAGE_UNITS: 101, + getParameter: (param: number) => { + if (param === 101) { + return maxTextures; + } + throw new Error(`Got undefined param ${param}.`); + } + }; + }); }); afterAll(() => { ENV.reset(); @@ -241,12 +240,12 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { }); it('is a function of gl.getParameter(MAX_TEXTURE_IMAGE_UNITS)', () => { - // maxTextures = 10; + maxTextures = 10; expect(ENV.getNumber('WEBGL_MAX_TEXTURES_IN_SHADER')).toBe(10); }); it('is capped at 16', () => { - // maxTextures = 20; + maxTextures = 20; expect(ENV.getNumber('WEBGL_MAX_TEXTURES_IN_SHADER')).toBe(16); }); }); From ce5c84924f6ca5a031612b4e9205513cdeb43c05 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Mon, 22 Jul 2019 16:33:36 -0700 Subject: [PATCH 06/28] save --- src/backends/webgl/flags_webgl_test.ts | 20 +++++++++++--------- src/backends/webgl/webgl_context_manager.ts | 3 +++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/backends/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts index c74d09d5e9..6c97b04902 100644 --- a/src/backends/webgl/flags_webgl_test.ts +++ b/src/backends/webgl/flags_webgl_test.ts @@ -196,14 +196,16 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { ENV.reset(); webgl_util.MAX_TEXTURE_SIZE = null; - spyOn(WebGLContextManager, 'getActiveContext').and.returnValue({ - MAX_TEXTURE_SIZE: 101, - getParameter: (param: number) => { - if (param === 101) { - return 50; + WebGLContextManager.getInstance().setContextFactory((version: number) => { + return { + MAX_TEXTURE_SIZE: 101, + getParameter: (param: number) => { + if (param === 101) { + return 50; + } + throw new Error(`Got undefined param ${param}.`); } - throw new Error(`Got undefined param ${param}.`); - } + } as WebGLRenderingContext; }); }); afterAll(() => { @@ -222,7 +224,7 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { ENV.reset(); webgl_util.MAX_TEXTURES_IN_SHADER = null; - spyOn(WebGLContextManager, 'getActiveContext').and.callFake(() => { + WebGLContextManager.getInstance().setContextFactory((version: number) => { return { MAX_TEXTURE_IMAGE_UNITS: 101, getParameter: (param: number) => { @@ -231,7 +233,7 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { } throw new Error(`Got undefined param ${param}.`); } - }; + } as WebGLRenderingContext; }); }); afterAll(() => { diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index e8dc56a9ad..b8cc95a7b0 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -45,6 +45,9 @@ export class WebGLContextManager { */ setContextFactory(factory: (version: number) => WebGLRenderingContext) { this.factory = factory; + + // Clear out items (TODO kreeger): write a unit test for this? + this.contexts = {}; } /** From 645a038a0ea4ce089005f6eab6c88df7730f5e08 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 13:48:41 -0700 Subject: [PATCH 07/28] save --- src/backends/webgl/clip_gpu.ts | 4 +- src/backends/webgl/clip_packed_gpu.ts | 4 +- src/backends/webgl/fill_gpu.ts | 4 +- src/backends/webgl/flags_webgl_test.ts | 23 ++-- src/backends/webgl/gpgpu_context.ts | 136 ++++++++------------ src/backends/webgl/gpgpu_context_test.ts | 22 ++-- src/backends/webgl/gpgpu_math.ts | 4 +- src/backends/webgl/gpgpu_util_test.ts | 8 +- src/backends/webgl/multinomial_gpu.ts | 4 +- src/backends/webgl/slice_gpu.ts | 4 +- src/backends/webgl/slice_packed_gpu.ts | 4 +- src/backends/webgl/webgl_context_manager.ts | 112 ++++++++-------- src/backends/webgl/webgl_util.ts | 24 ++-- src/platforms/platform_browser.ts | 6 - 14 files changed, 159 insertions(+), 200 deletions(-) diff --git a/src/backends/webgl/clip_gpu.ts b/src/backends/webgl/clip_gpu.ts index 8fe30e1cd7..55eaa68ba0 100644 --- a/src/backends/webgl/clip_gpu.ts +++ b/src/backends/webgl/clip_gpu.ts @@ -17,7 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export class ClipProgram implements GPGPUProgram { variableNames = ['A']; @@ -52,7 +52,7 @@ export class ClipProgram implements GPGPUProgram { this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min'); this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max'); } - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); gl.uniform1f(this.minLoc, min); gl.uniform1f(this.maxLoc, max); }; diff --git a/src/backends/webgl/clip_packed_gpu.ts b/src/backends/webgl/clip_packed_gpu.ts index d90ff39cab..aa176e6b31 100644 --- a/src/backends/webgl/clip_packed_gpu.ts +++ b/src/backends/webgl/clip_packed_gpu.ts @@ -17,7 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export class ClipPackedProgram implements GPGPUProgram { variableNames = ['A']; @@ -54,7 +54,7 @@ export class ClipPackedProgram implements GPGPUProgram { this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min'); this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max'); } - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); gl.uniform1f(this.minLoc, min); gl.uniform1f(this.maxLoc, max); }; diff --git a/src/backends/webgl/fill_gpu.ts b/src/backends/webgl/fill_gpu.ts index 8348a0ae55..5469d37321 100644 --- a/src/backends/webgl/fill_gpu.ts +++ b/src/backends/webgl/fill_gpu.ts @@ -17,7 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export class FillProgram implements GPGPUProgram { variableNames: string[]; @@ -44,7 +44,7 @@ export class FillProgram implements GPGPUProgram { if (this.valueLoc == null) { this.valueLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'value'); } - WebGLContextManager.getActiveContext().uniform1f(this.valueLoc, value); + getActiveContext().uniform1f(this.valueLoc, value); }; } } diff --git a/src/backends/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts index 6c97b04902..303e702eea 100644 --- a/src/backends/webgl/flags_webgl_test.ts +++ b/src/backends/webgl/flags_webgl_test.ts @@ -18,7 +18,8 @@ import * as device_util from '../../device_util'; import {ENV} from '../../environment'; import {webgl_util} from '../../webgl'; -import {WebGLContextManager} from './webgl_context_manager'; + +import * as webgl_context_manager from './webgl_context_manager'; describe('HAS_WEBGL', () => { beforeEach(() => ENV.reset()); @@ -196,16 +197,14 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { ENV.reset(); webgl_util.MAX_TEXTURE_SIZE = null; - WebGLContextManager.getInstance().setContextFactory((version: number) => { - return { - MAX_TEXTURE_SIZE: 101, - getParameter: (param: number) => { - if (param === 101) { - return 50; - } - throw new Error(`Got undefined param ${param}.`); + spyOn(webgl_context_manager, 'getActiveContext').and.returnValue({ + MAX_TEXTURE_SIZE: 101, + getParameter: (param: number) => { + if (param === 101) { + return 50; } - } as WebGLRenderingContext; + throw new Error(`Got undefined param ${param}.`); + } }); }); afterAll(() => { @@ -224,7 +223,7 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { ENV.reset(); webgl_util.MAX_TEXTURES_IN_SHADER = null; - WebGLContextManager.getInstance().setContextFactory((version: number) => { + spyOn(webgl_context_manager, 'getActiveContext').and.callFake(() => { return { MAX_TEXTURE_IMAGE_UNITS: 101, getParameter: (param: number) => { @@ -233,7 +232,7 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { } throw new Error(`Got undefined param ${param}.`); } - } as WebGLRenderingContext; + }; }); }); afterAll(() => { diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index ccce7e9c81..4d59a1f59f 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -22,7 +22,7 @@ import * as util from '../../util'; import * as gpgpu_util from './gpgpu_util'; import {TextureConfig} from './gpgpu_util'; import * as tex_util from './tex_util'; -import {WebGLContextManager} from './webgl_context_manager'; +import {disposeActiveContext, getActiveContext} from './webgl_context_manager'; import {WebGL1DisjointQueryTimerExtension, WebGL2DisjointQueryTimerExtension} from './webgl_types'; import * as webgl_util from './webgl_util'; @@ -49,7 +49,7 @@ export class GPGPUContext { private textureConfig: TextureConfig; constructor() { - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); // WebGL 2.0 enables texture floats without an extension. if (ENV.getNumber('WEBGL_VERSION') === 1) { @@ -98,7 +98,7 @@ export class GPGPUContext { 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + 'disposing.'); } - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); webgl_util.callAndCheck(gl, this.debug, () => gl.finish()); webgl_util.callAndCheck( gl, this.debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); @@ -111,30 +111,28 @@ export class GPGPUContext { webgl_util.callAndCheck( gl, this.debug, () => gl.deleteBuffer(this.indexBuffer)); this.disposed = true; + disposeActiveContext(); } public createFloat32MatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createFloat32MatrixTexture( - WebGLContextManager.getActiveContext(), this.debug, rows, columns, - this.textureConfig); + getActiveContext(), this.debug, rows, columns, this.textureConfig); } public createFloat16MatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createFloat16MatrixTexture( - WebGLContextManager.getActiveContext(), this.debug, rows, columns, - this.textureConfig); + getActiveContext(), this.debug, rows, columns, this.textureConfig); } public createUnsignedBytesMatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createUnsignedBytesMatrixTexture( - WebGLContextManager.getActiveContext(), this.debug, rows, columns, - this.textureConfig); + getActiveContext(), this.debug, rows, columns, this.textureConfig); } public uploadPixelDataToTexture( @@ -142,43 +140,41 @@ export class GPGPUContext { pixels: PixelData|ImageData|HTMLImageElement|HTMLCanvasElement) { this.throwIfDisposed(); gpgpu_util.uploadPixelDataToTexture( - WebGLContextManager.getActiveContext(), this.debug, texture, pixels); + getActiveContext(), this.debug, texture, pixels); } public uploadDenseMatrixToTexture( texture: WebGLTexture, width: number, height: number, data: TypedArray) { this.throwIfDisposed(); gpgpu_util.uploadDenseMatrixToTexture( - WebGLContextManager.getActiveContext(), this.debug, texture, width, - height, data, this.textureConfig); + getActiveContext(), this.debug, texture, width, height, data, + this.textureConfig); } public createFloat16PackedMatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createFloat16PackedMatrixTexture( - WebGLContextManager.getActiveContext(), this.debug, rows, columns, - this.textureConfig); + getActiveContext(), this.debug, rows, columns, this.textureConfig); } public createPackedMatrixTexture(rows: number, columns: number): WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createPackedMatrixTexture( - WebGLContextManager.getActiveContext(), this.debug, rows, columns, - this.textureConfig); + getActiveContext(), this.debug, rows, columns, this.textureConfig); } public deleteMatrixTexture(texture: WebGLTexture) { this.throwIfDisposed(); if (this.outputTexture === texture) { webgl_util.unbindColorTextureFromFramebuffer( - WebGLContextManager.getActiveContext(), this.debug, this.framebuffer); + getActiveContext(), this.debug, this.framebuffer); this.outputTexture = null; } webgl_util.callAndCheck( - WebGLContextManager.getActiveContext(), this.debug, - () => WebGLContextManager.getActiveContext().deleteTexture(texture)); + getActiveContext(), this.debug, + () => getActiveContext().deleteTexture(texture)); } public downloadByteEncodedFloatMatrixFromOutputTexture( @@ -186,37 +182,35 @@ export class GPGPUContext { return this.downloadMatrixDriver( texture, () => gpgpu_util.downloadByteEncodedFloatMatrixFromOutputTexture( - WebGLContextManager.getActiveContext(), this.debug, rows, columns, - this.textureConfig)); + getActiveContext(), this.debug, rows, columns, this.textureConfig)); } public downloadPackedMatrixFromBuffer( buffer: WebGLBuffer, batch: number, rows: number, columns: number, physicalRows: number, physicalCols: number): Float32Array { return gpgpu_util.downloadPackedMatrixFromBuffer( - WebGLContextManager.getActiveContext(), buffer, batch, rows, columns, - physicalRows, physicalCols, this.textureConfig); + getActiveContext(), buffer, batch, rows, columns, physicalRows, + physicalCols, this.textureConfig); } public downloadFloat32MatrixFromBuffer(buffer: WebGLBuffer, size: number): Float32Array { return gpgpu_util.downloadFloat32MatrixFromBuffer( - WebGLContextManager.getActiveContext(), buffer, size); + getActiveContext(), buffer, size); } public createBufferFromTexture( texture: WebGLTexture, rows: number, columns: number): WebGLBuffer { this.bindTextureToFrameBuffer(texture); const result = gpgpu_util.createBufferFromOutputTexture( - WebGLContextManager.getActiveContext() as WebGL2RenderingContext, - this.debug, rows, columns, this.textureConfig); + getActiveContext() as WebGL2RenderingContext, this.debug, rows, columns, + this.textureConfig); this.unbindTextureToFrameBuffer(); return result; } public createAndWaitForFence(): Promise { - const fenceContext = - this.createFence(WebGLContextManager.getActiveContext()); + const fenceContext = this.createFence(getActiveContext()); return this.pollFence(fenceContext); } @@ -260,15 +254,14 @@ export class GPGPUContext { return this.downloadMatrixDriver( texture, () => gpgpu_util.downloadMatrixFromPackedOutputTexture( - WebGLContextManager.getActiveContext(), this.debug, physicalRows, - physicalCols)); + getActiveContext(), this.debug, physicalRows, physicalCols)); } private vertexAttrsAreBound = false; public createProgram(fragmentShaderSource: string): WebGLProgram { this.throwIfDisposed(); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); const fragmentShader: WebGLShader = webgl_util.createFragmentShader(gl, this.debug, fragmentShaderSource); const vertexShader: WebGLShader = @@ -300,10 +293,8 @@ export class GPGPUContext { } if (program != null) { webgl_util.callAndCheck( - WebGLContextManager.getActiveContext(), this.debug, - () => WebGLContextManager.getInstance() - .getActiveContext() - .deleteProgram(program)); + getActiveContext(), this.debug, + () => getActiveContext().deleteProgram(program)); } } @@ -311,12 +302,11 @@ export class GPGPUContext { this.throwIfDisposed(); this.program = program; if ((this.program != null) && this.debug) { - webgl_util.validateProgram( - WebGLContextManager.getActiveContext(), this.debug, this.program); + webgl_util.validateProgram(getActiveContext(), this.debug, this.program); } webgl_util.callAndCheck( - WebGLContextManager.getActiveContext(), this.debug, - () => WebGLContextManager.getActiveContext().useProgram(program)); + getActiveContext(), this.debug, + () => getActiveContext().useProgram(program)); } public getUniformLocation( @@ -325,11 +315,10 @@ export class GPGPUContext { this.throwIfDisposed(); if (shouldThrow) { return webgl_util.getProgramUniformLocationOrThrow( - WebGLContextManager.getActiveContext(), this.debug, program, - uniformName); + getActiveContext(), this.debug, program, uniformName); } else { return webgl_util.getProgramUniformLocation( - WebGLContextManager.getActiveContext(), program, uniformName); + getActiveContext(), program, uniformName); } } @@ -337,18 +326,14 @@ export class GPGPUContext { number { this.throwIfDisposed(); return webgl_util.callAndCheck( - WebGLContextManager.getActiveContext(), this.debug, - () => WebGLContextManager.getInstance() - .getActiveContext() - .getAttribLocation(program, attribute)); + getActiveContext(), this.debug, + () => getActiveContext().getAttribLocation(program, attribute)); } public getUniformLocationNoThrow(program: WebGLProgram, uniformName: string): WebGLUniformLocation { this.throwIfDisposed(); - return WebGLContextManager.getInstance() - .getActiveContext() - .getUniformLocation(program, uniformName); + return getActiveContext().getUniformLocation(program, uniformName); } public setInputMatrixTexture( @@ -357,8 +342,8 @@ export class GPGPUContext { this.throwIfDisposed(); this.throwIfNoProgram(); webgl_util.bindTextureToProgramUniformSampler( - WebGLContextManager.getActiveContext(), this.debug, this.program, - inputMatrixTexture, uniformLocation, textureUnit); + getActiveContext(), this.debug, this.program, inputMatrixTexture, + uniformLocation, textureUnit); } public setOutputMatrixTexture( @@ -389,16 +374,15 @@ export class GPGPUContext { public debugValidate() { if (this.program != null) { - webgl_util.validateProgram( - WebGLContextManager.getActiveContext(), this.debug, this.program); + webgl_util.validateProgram(getActiveContext(), this.debug, this.program); } - webgl_util.validateFramebuffer(WebGLContextManager.getActiveContext()); + webgl_util.validateFramebuffer(getActiveContext()); } public executeProgram() { this.throwIfDisposed(); this.throwIfNoProgram(); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); if (this.debug) { this.debugValidate(); } @@ -410,8 +394,7 @@ export class GPGPUContext { public blockUntilAllProgramsCompleted() { this.throwIfDisposed(); webgl_util.callAndCheck( - WebGLContextManager.getActiveContext(), this.debug, - () => WebGLContextManager.getActiveContext().finish()); + getActiveContext(), this.debug, () => getActiveContext().finish()); } private getQueryTimerExtension(): WebGL1DisjointQueryTimerExtension @@ -419,7 +402,7 @@ export class GPGPUContext { if (this.disjointQueryTimerExtension == null) { this.disjointQueryTimerExtension = webgl_util.getExtensionOrThrow( - WebGLContextManager.getActiveContext(), this.debug, + getActiveContext(), this.debug, ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2 ? 'EXT_disjoint_timer_query_webgl2' : @@ -440,8 +423,7 @@ export class GPGPUContext { beginQuery(): WebGLQuery { if (ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { - const gl2 = - WebGLContextManager.getActiveContext() as WebGL2RenderingContext; + const gl2 = getActiveContext() as WebGL2RenderingContext; const ext = this.getQueryTimerExtensionWebGL2(); const query = gl2.createQuery(); @@ -456,8 +438,7 @@ export class GPGPUContext { endQuery() { if (ENV.getNumber('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { - const gl2 = - WebGLContextManager.getActiveContext() as WebGL2RenderingContext; + const gl2 = getActiveContext() as WebGL2RenderingContext; const ext = this.getQueryTimerExtensionWebGL2(); gl2.endQuery(ext.TIME_ELAPSED_EXT); return; @@ -485,8 +466,7 @@ export class GPGPUContext { } if (queryTimerVersion === 2) { - const gl2 = - WebGLContextManager.getActiveContext() as WebGL2RenderingContext; + const gl2 = getActiveContext() as WebGL2RenderingContext; const timeElapsedNanos = gl2.getQueryParameter(query, gl2.QUERY_RESULT); // Return milliseconds. @@ -508,15 +488,13 @@ export class GPGPUContext { } if (queryTimerVersion === 2) { - const gl2 = - WebGLContextManager.getActiveContext() as WebGL2RenderingContext; + const gl2 = getActiveContext() as WebGL2RenderingContext; const ext = this.getQueryTimerExtensionWebGL2(); const available = gl2.getQueryParameter(query, gl2.QUERY_RESULT_AVAILABLE); if (this.disjoint == null) { - this.disjoint = WebGLContextManager.getActiveContext().getParameter( - ext.GPU_DISJOINT_EXT); + this.disjoint = getActiveContext().getParameter(ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; @@ -526,8 +504,7 @@ export class GPGPUContext { const available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); if (this.disjoint == null) { - this.disjoint = WebGLContextManager.getActiveContext().getParameter( - ext.GPU_DISJOINT_EXT); + this.disjoint = getActiveContext().getParameter(ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; @@ -569,24 +546,22 @@ export class GPGPUContext { private bindTextureToFrameBuffer(texture: WebGLTexture) { this.throwIfDisposed(); webgl_util.bindColorTextureToFramebuffer( - WebGLContextManager.getActiveContext(), this.debug, texture, - this.framebuffer); + getActiveContext(), this.debug, texture, this.framebuffer); if (this.debug) { - webgl_util.validateFramebuffer(WebGLContextManager.getActiveContext()); + webgl_util.validateFramebuffer(getActiveContext()); } } private unbindTextureToFrameBuffer() { if (this.outputTexture != null) { webgl_util.bindColorTextureToFramebuffer( - WebGLContextManager.getActiveContext(), this.debug, - this.outputTexture, this.framebuffer); + getActiveContext(), this.debug, this.outputTexture, this.framebuffer); if (this.debug) { - webgl_util.validateFramebuffer(WebGLContextManager.getActiveContext()); + webgl_util.validateFramebuffer(getActiveContext()); } } else { webgl_util.unbindColorTextureFromFramebuffer( - WebGLContextManager.getActiveContext(), this.debug, this.framebuffer); + getActiveContext(), this.debug, this.framebuffer); } } @@ -604,7 +579,7 @@ export class GPGPUContext { outputMatrixTextureMaybePacked: WebGLTexture, width: number, height: number) { this.throwIfDisposed(); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); webgl_util.bindColorTextureToFramebuffer( gl, this.debug, outputMatrixTextureMaybePacked, this.framebuffer); if (this.debug) { @@ -621,9 +596,8 @@ export class GPGPUContext { x: number, y: number, width: number, height: number) { this.throwIfDisposed(); webgl_util.callAndCheck( - WebGLContextManager.getActiveContext(), this.debug, - () => WebGLContextManager.getActiveContext().scissor( - x, y, width, height)); + getActiveContext(), this.debug, + () => getActiveContext().scissor(x, y, width, height)); } private throwIfDisposed() { diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index 269b887a96..0bdd3ec63b 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -22,7 +22,7 @@ import {WEBGL_ENVS} from './backend_webgl_test_registry'; import {getGlslDifferences} from './glsl_version'; import {GPGPUContext, linearSearchLastTrue} from './gpgpu_context'; import * as tex_util from './tex_util'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; const DOWNLOAD_FLOAT_ENVS = { flags: {'WEBGL_DOWNLOAD_FLOAT_ENABLED': true}, @@ -58,7 +58,7 @@ describeWithFlags( const output = gpgpu.createFloat32MatrixTexture(rows, columns); gpgpu.setOutputMatrixTexture(output, rows, columns); const expected = new Int32Array([0, 0, columns, rows]); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); expect(gl.getParameter(gl.VIEWPORT)).toEqual(expected); gpgpu.deleteMatrixTexture(output); }); @@ -97,7 +97,7 @@ describeWithFlags( const [width, height] = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); const expected = new Int32Array([0, 0, width, height]); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); expect(gl.getParameter(gl.VIEWPORT)).toEqual(expected); }); }); @@ -136,7 +136,7 @@ describeWithFlags( it('sets the scissor box to the requested parameters', () => { gpgpu.setOutputMatrixWriteRegion(0, 1, 2, 3); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); const scissorBox = gl.getParameter(gl.SCISSOR_BOX); expect(scissorBox[0]).toEqual(2); expect(scissorBox[1]).toEqual(0); @@ -159,12 +159,6 @@ describeWithFlags('GPGPUContext', DOWNLOAD_FLOAT_ENVS, () => { gpgpu.dispose(); }); - it('throws an error if used after dispose', () => { - const gpgpuContext = new GPGPUContext(); - gpgpuContext.dispose(); - expect(gpgpuContext.dispose).toThrowError(); - }); - it('throws an error if validation is on and framebuffer incomplete', () => { const glsl = getGlslDifferences(); const src = `${glsl.version} @@ -181,6 +175,14 @@ describeWithFlags('GPGPUContext', DOWNLOAD_FLOAT_ENVS, () => { }); }); +describeWithFlags('GPGPUContext dispose', DOWNLOAD_FLOAT_ENVS, () => { + it('throws an error if used after dispose', () => { + const gpgpuContext = new GPGPUContext(); + gpgpuContext.dispose(); + expect(gpgpuContext.dispose).toThrowError(); + }); +}); + describe('gpgpu_context linearSearchLastTrue', () => { it('[false]', () => { const a: boolean[] = [false]; diff --git a/src/backends/webgl/gpgpu_math.ts b/src/backends/webgl/gpgpu_math.ts index 52c07d9061..d57bb1e2f5 100644 --- a/src/backends/webgl/gpgpu_math.ts +++ b/src/backends/webgl/gpgpu_math.ts @@ -24,7 +24,7 @@ import {GPGPUContext} from './gpgpu_context'; import * as shader_compiler from './shader_compiler'; import {InputInfo, ShapeInfo} from './shader_compiler'; import {TextureData} from './tex_util'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export interface GPGPUProgram { variableNames: string[]; @@ -163,7 +163,7 @@ export function runProgram( } gpgpu.setProgram(binary.webGLProgram); - const gl = WebGLContextManager.getActiveContext(); + const gl = getActiveContext(); // Set special uniforms (NAN, INFINITY) if (ENV.getNumber('WEBGL_VERSION') === 1) { if (binary.infLoc !== null) { diff --git a/src/backends/webgl/gpgpu_util_test.ts b/src/backends/webgl/gpgpu_util_test.ts index 3bba346229..29dd250ee7 100644 --- a/src/backends/webgl/gpgpu_util_test.ts +++ b/src/backends/webgl/gpgpu_util_test.ts @@ -19,7 +19,7 @@ import {describeWithFlags} from '../../jasmine_util'; import {WEBGL_ENVS} from './backend_webgl_test_registry'; import {GPGPUContext} from './gpgpu_context'; import * as gpgpu_util from './gpgpu_util'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; describeWithFlags('gpgpu_util createWebGLContext', WEBGL_ENVS, () => { let gpgpu: GPGPUContext; @@ -27,7 +27,7 @@ describeWithFlags('gpgpu_util createWebGLContext', WEBGL_ENVS, () => { beforeEach(() => { gpgpu = new GPGPUContext(); - gl = WebGLContextManager.getActiveContext(); + gl = getActiveContext(); }); afterEach(() => { @@ -62,7 +62,7 @@ describeWithFlags('gpgpu_util createWebGLContext', WEBGL_ENVS, () => { describeWithFlags('gpgpu_util createFloat32MatrixTexture', WEBGL_ENVS, () => { let gl: WebGLRenderingContext; beforeEach(() => { - gl = WebGLContextManager.getActiveContext(); + gl = getActiveContext(); }); afterEach(() => { @@ -105,7 +105,7 @@ describeWithFlags('gpgpu_util createFloat32MatrixTexture', WEBGL_ENVS, () => { describeWithFlags('gpgpu_util createPackedMatrixTexture', WEBGL_ENVS, () => { let gl: WebGLRenderingContext; beforeEach(() => { - gl = WebGLContextManager.getActiveContext(); + gl = getActiveContext(); }); afterEach(() => { diff --git a/src/backends/webgl/multinomial_gpu.ts b/src/backends/webgl/multinomial_gpu.ts index fb89d28239..0b8dd46508 100644 --- a/src/backends/webgl/multinomial_gpu.ts +++ b/src/backends/webgl/multinomial_gpu.ts @@ -17,7 +17,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export class MultinomialProgram implements GPGPUProgram { variableNames = ['probs']; @@ -60,7 +60,7 @@ export class MultinomialProgram implements GPGPUProgram { if (this.seedLoc == null) { this.seedLoc = gpgpu.getUniformLocation(webGLProgram, 'seed'); } - WebGLContextManager.getActiveContext().uniform1f(this.seedLoc, seed); + getActiveContext().uniform1f(this.seedLoc, seed); }; } } diff --git a/src/backends/webgl/slice_gpu.ts b/src/backends/webgl/slice_gpu.ts index d65766e8bd..eee02ff65a 100644 --- a/src/backends/webgl/slice_gpu.ts +++ b/src/backends/webgl/slice_gpu.ts @@ -18,7 +18,7 @@ import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; import {getCoordsDataType} from './shader_compiler'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export class SliceProgram implements GPGPUProgram { variableNames = ['source']; @@ -70,7 +70,7 @@ export class SliceProgram implements GPGPUProgram { return; } } - WebGLContextManager.getActiveContext().uniform1iv(this.startLoc, start); + getActiveContext().uniform1iv(this.startLoc, start); }; } } diff --git a/src/backends/webgl/slice_packed_gpu.ts b/src/backends/webgl/slice_packed_gpu.ts index 788bb4152e..fc5d197b2e 100644 --- a/src/backends/webgl/slice_packed_gpu.ts +++ b/src/backends/webgl/slice_packed_gpu.ts @@ -20,7 +20,7 @@ import {getChannels} from '../packing_util'; import {GPGPUContext} from './gpgpu_context'; import {GPGPUProgram} from './gpgpu_math'; import {getCoordsDataType} from './shader_compiler'; -import {WebGLContextManager} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; export class SlicePackedProgram implements GPGPUProgram { variableNames = ['source']; @@ -98,7 +98,7 @@ export class SlicePackedProgram implements GPGPUProgram { return; } } - WebGLContextManager.getActiveContext().uniform1iv(this.startLoc, start); + getActiveContext().uniform1iv(this.startLoc, start); }; } } diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index b8cc95a7b0..ce90d275a5 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -15,74 +15,72 @@ * ============================================================================= */ import {ENV} from '../../environment'; +import {createDOMCanvasWebGLRenderingContext} from './canvas_util'; + +let contexts: {[key: string]: WebGLRenderingContext} = {}; +let contextFactory: (version: number) => WebGLRenderingContext = null; /** - * Manages global state of all WebGLRenderingContexts. + * Sets callback for creating new WebGLRenderingContext instances. + * @param factory The callback function that returns a WebGLRenderingContext + * instance. */ -export class WebGLContextManager { - private static instance: WebGLContextManager; +export function setContextFactory( + factory: (version: number) => WebGLRenderingContext) { + contextFactory = factory; - contexts: {[key: string]: WebGLRenderingContext} = {}; - factory: (version: number) => WebGLRenderingContext; + // Clear out items (TODO kreeger): write a unit test for this? + contexts = {}; +} - private constructor() {} +/** + * Returns the current WebGLContext + */ +export function getActiveContext(): WebGLRenderingContext { + return getContextByVersion(ENV.getNumber('WEBGL_VERSION')); +} - static getInstance(): WebGLContextManager { - if (!WebGLContextManager.instance) { - WebGLContextManager.instance = new WebGLContextManager(); +/** + * TODO(kreeger): Doc me. + * @param version The specific version of WebGL to request. + */ +export function getContextByVersion(version: number): WebGLRenderingContext { + // Default to browser context creation is running in the browser. + if (contextFactory == null) { + if (ENV.getBool('IS_BROWSER')) { + contextFactory = createDOMCanvasWebGLRenderingContext; + } else { + throw new Error('Default WebGLRenderingContext factory was not set!'); } - return WebGLContextManager.instance; } - static getActiveContext(): WebGLRenderingContext { - return WebGLContextManager.getInstance().getActiveContext(); + if (!(version in contexts)) { + contexts[version] = contextFactory(version); + bootstrapWebGLContext(contexts[version]); } - - /** - * Sets callback for creating new WebGLRenderingContext instances. - * @param factory The callback function that returns a WebGLRenderingContext - * instance. - */ - setContextFactory(factory: (version: number) => WebGLRenderingContext) { - this.factory = factory; - - // Clear out items (TODO kreeger): write a unit test for this? - this.contexts = {}; - } - - /** - * Returns the current WebGLContext - */ - getActiveContext(): WebGLRenderingContext { - return this.getContextByVersion(ENV.getNumber('WEBGL_VERSION')); + const gl = contexts[version]; + if (gl.isContextLost()) { + delete contexts[version]; + return getContextByVersion(version); } + return contexts[version]; +} - /** - * TODO(kreeger): Doc me. - * @param version The specific version of WebGL to request. - */ - getContextByVersion(version: number): WebGLRenderingContext { - if (!(version in this.contexts)) { - this.contexts[version] = this.factory(version); - this.bootstrapWebGLContext(this.contexts[version]); - } - const gl = this.contexts[version]; - if (gl.isContextLost()) { - delete this.contexts[version]; - return this.getContextByVersion(version); - } - return this.contexts[version]; - } +/** + * TODO(kreeger): Doc me. + */ +export function disposeActiveContext() { + delete contexts[ENV.getNumber('WEBGL_VERSION')]; +} - private bootstrapWebGLContext(gl: WebGLRenderingContext) { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.STENCIL_TEST); - gl.disable(gl.BLEND); - gl.disable(gl.DITHER); - gl.disable(gl.POLYGON_OFFSET_FILL); - gl.disable(gl.SAMPLE_COVERAGE); - gl.enable(gl.SCISSOR_TEST); - gl.enable(gl.CULL_FACE); - gl.cullFace(gl.BACK); - } +function bootstrapWebGLContext(gl: WebGLRenderingContext) { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.STENCIL_TEST); + gl.disable(gl.BLEND); + gl.disable(gl.DITHER); + gl.disable(gl.POLYGON_OFFSET_FILL); + gl.disable(gl.SAMPLE_COVERAGE); + gl.enable(gl.SCISSOR_TEST); + gl.enable(gl.CULL_FACE); + gl.cullFace(gl.BACK); } diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index 6d9182081b..2ec3c26a5f 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -17,8 +17,7 @@ import {ENV} from '../../environment'; import * as util from '../../util'; -import {WebGLContextManager} from './webgl_context_manager'; -// import {getWebGLContext} from './canvas_util'; +import {getContextByVersion} from './webgl_context_manager'; export function callAndCheck( gl: WebGLRenderingContext, debugMode: boolean, func: () => T): T { @@ -502,8 +501,7 @@ export let MAX_TEXTURES_IN_SHADER: number; export function getWebGLMaxTextureSize(webGLVersion: number): number { if (MAX_TEXTURE_SIZE == null) { - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE); } return MAX_TEXTURE_SIZE; @@ -511,8 +509,7 @@ export function getWebGLMaxTextureSize(webGLVersion: number): number { export function getMaxTexturesInShader(webGLVersion: number): number { if (MAX_TEXTURES_IN_SHADER == null) { - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); MAX_TEXTURES_IN_SHADER = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); } // We cap at 16 to avoid spurious runtime "memory exhausted" error. @@ -526,8 +523,7 @@ export function getWebGLDisjointQueryTimerVersion(webGLVersion: number): } let queryTimerVersion: number; - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); if (hasExtension(gl, 'EXT_disjoint_timer_query_webgl2') && webGLVersion === 2) { @@ -547,8 +543,7 @@ function hasExtension(gl: WebGLRenderingContext, extensionName: string) { export function isWebGLVersionEnabled(webGLVersion: 1|2) { try { - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); if (gl != null) { return true; } @@ -563,8 +558,7 @@ export function isRenderToFloatTextureEnabled(webGLVersion: number): boolean { return false; } - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { @@ -586,8 +580,7 @@ export function isDownloadFloatTextureEnabled(webGLVersion: number): boolean { return false; } - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { @@ -638,8 +631,7 @@ export function isWebGLFenceEnabled(webGLVersion: number) { if (webGLVersion !== 2) { return false; } - const gl = - WebGLContextManager.getInstance().getContextByVersion(webGLVersion); + const gl = getContextByVersion(webGLVersion); // tslint:disable-next-line:no-any const isEnabled = (gl as any).fenceSync != null; diff --git a/src/platforms/platform_browser.ts b/src/platforms/platform_browser.ts index 27d577e217..ad8c337eb2 100644 --- a/src/platforms/platform_browser.ts +++ b/src/platforms/platform_browser.ts @@ -14,8 +14,6 @@ * limitations under the License. * ============================================================================= */ -import {createDOMCanvasWebGLRenderingContext} from '../backends/webgl/canvas_util'; -import {WebGLContextManager} from '../backends/webgl/webgl_context_manager'; import {ENV} from '../environment'; import {Platform} from './platform'; @@ -27,10 +25,6 @@ export class PlatformBrowser implements Platform { // According to the spec, the built-in encoder can do only UTF-8 encoding. // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/TextEncoder this.textEncoder = new TextEncoder(); - - // Register default WebGL Context creation: - WebGLContextManager.getInstance().setContextFactory( - createDOMCanvasWebGLRenderingContext) } fetch(path: string, init?: RequestInit): Promise { From da1db423414467dc861790515286e24bd95523fd Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 13:53:45 -0700 Subject: [PATCH 08/28] save --- src/backends/webgl/backend_webgl.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index add846bb6f..0e6307d37e 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -130,6 +130,7 @@ import {UnaryOpProgram} from './unaryop_gpu'; import * as unary_packed_op from './unaryop_packed_gpu'; import {UnaryOpPackedProgram} from './unaryop_packed_gpu'; import {UnpackProgram} from './unpack_gpu'; +import {getActiveContext} from './webgl_context_manager'; import * as webgl_util from './webgl_util'; type KernelInfo = { @@ -2500,13 +2501,13 @@ export class MathBackendWebGL implements KernelBackend { } this.textureManager.dispose(); - // TODO(kreeger): This should be cleaned up in the WebGLContextManager - // right? - // if (this.canvas != null && this.canvas.remove != null) { - // this.canvas.remove(); - // } else { - // this.canvas = null; - // } + // TODO(kreeger): Should this be cleaned up somewhere else? + if (ENV.getBool('IS_BROWSER')) { + const canvas = getActiveContext().canvas; + if (canvas != null && canvas.remove != null) { + canvas.remove(); + } + } if (this.fromPixels2DContext != null && //@ts-ignore From 1a75a9b182fa66f069816a8d703222ca893d871b Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 14:02:29 -0700 Subject: [PATCH 09/28] save --- src/backends/webgl/backend_webgl.ts | 8 ------ src/backends/webgl/canvas_util.ts | 13 +++++++-- src/backends/webgl/webgl_context_manager.ts | 29 +++++++++++++++++++-- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index 0e6307d37e..49db16fafe 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -2501,14 +2501,6 @@ export class MathBackendWebGL implements KernelBackend { } this.textureManager.dispose(); - // TODO(kreeger): Should this be cleaned up somewhere else? - if (ENV.getBool('IS_BROWSER')) { - const canvas = getActiveContext().canvas; - if (canvas != null && canvas.remove != null) { - canvas.remove(); - } - } - if (this.fromPixels2DContext != null && //@ts-ignore this.fromPixels2DContext.canvas.remove) { diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 26afbe035d..d45b26a4ba 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -1,3 +1,5 @@ +import {disposeActiveContext} from './webgl_context_manager'; + /** * @license * Copyright 2018 Google LLC. All Rights Reserved. @@ -35,6 +37,14 @@ export function createCanvas(webGLVersion: number) { } } +export function cleanupDOMCanvasWebGLRenderingContext( + context: WebGLRenderingContext) { + const canvas = context.canvas; + if (canvas != null && canvas.remove != null) { + canvas.remove(); + } +} + export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { if (webGLVersion !== 1 && webGLVersion !== 2) { @@ -44,8 +54,7 @@ export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - // TODO(kreeger): callback to manager??? - // delete contexts[webGLVersion]; + disposeActiveContext(); }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index ce90d275a5..9b3d3d0283 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -15,10 +15,12 @@ * ============================================================================= */ import {ENV} from '../../environment'; -import {createDOMCanvasWebGLRenderingContext} from './canvas_util'; + +import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; let contexts: {[key: string]: WebGLRenderingContext} = {}; let contextFactory: (version: number) => WebGLRenderingContext = null; +let contextCleanup: (context: WebGLRenderingContext) => void = null; /** * Sets callback for creating new WebGLRenderingContext instances. @@ -33,6 +35,15 @@ export function setContextFactory( contexts = {}; } +/** + * TODO(kreeger): doc me. + * @param cleanup + */ +export function setContextCleanup( + cleanup: (context: WebGLRenderingContext) => void) { + contextCleanup = cleanup; +} + /** * Returns the current WebGLContext */ @@ -48,6 +59,7 @@ export function getContextByVersion(version: number): WebGLRenderingContext { // Default to browser context creation is running in the browser. if (contextFactory == null) { if (ENV.getBool('IS_BROWSER')) { + // TODO - is there a better place to register this? contextFactory = createDOMCanvasWebGLRenderingContext; } else { throw new Error('Default WebGLRenderingContext factory was not set!'); @@ -70,7 +82,20 @@ export function getContextByVersion(version: number): WebGLRenderingContext { * TODO(kreeger): Doc me. */ export function disposeActiveContext() { - delete contexts[ENV.getNumber('WEBGL_VERSION')]; + disposeWebGLContext(ENV.getNumber('WEBGL_VERSION')); +} + +function disposeWebGLContext(version: number) { + if (contextCleanup == null) { + if (ENV.getBool('IS_BROWSER')) { + // TODO - is there a better place to register this? + contextCleanup = cleanupDOMCanvasWebGLRenderingContext; + } + } + if (contextCleanup != null) { + contextCleanup(contexts[version]); + } + delete contexts[version]; } function bootstrapWebGLContext(gl: WebGLRenderingContext) { From ec015909fd684ab016ca661dd11fa25d3abd1e10 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 14:20:38 -0700 Subject: [PATCH 10/28] save --- src/backends/webgl/backend_webgl.ts | 1 - src/backends/webgl/canvas_util.ts | 3 +++ src/backends/webgl/webgl_context_manager.ts | 23 +++++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index 49db16fafe..9cfb34804d 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -130,7 +130,6 @@ import {UnaryOpProgram} from './unaryop_gpu'; import * as unary_packed_op from './unaryop_packed_gpu'; import {UnaryOpPackedProgram} from './unaryop_packed_gpu'; import {UnpackProgram} from './unpack_gpu'; -import {getActiveContext} from './webgl_context_manager'; import * as webgl_util from './webgl_util'; type KernelInfo = { diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index d45b26a4ba..df19e1017a 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -39,6 +39,9 @@ export function createCanvas(webGLVersion: number) { export function cleanupDOMCanvasWebGLRenderingContext( context: WebGLRenderingContext) { + if (context == null) { + throw new Error('Shold not hit this case'); + } const canvas = context.canvas; if (canvas != null && canvas.remove != null) { canvas.remove(); diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index 9b3d3d0283..7cc21fa671 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -32,6 +32,9 @@ export function setContextFactory( contextFactory = factory; // Clear out items (TODO kreeger): write a unit test for this? + for (const ctx in contexts) { + console.log('ctx: ' + ctx); + } contexts = {}; } @@ -72,7 +75,7 @@ export function getContextByVersion(version: number): WebGLRenderingContext { } const gl = contexts[version]; if (gl.isContextLost()) { - delete contexts[version]; + disposeWebGLContext(version); return getContextByVersion(version); } return contexts[version]; @@ -86,16 +89,18 @@ export function disposeActiveContext() { } function disposeWebGLContext(version: number) { - if (contextCleanup == null) { - if (ENV.getBool('IS_BROWSER')) { - // TODO - is there a better place to register this? - contextCleanup = cleanupDOMCanvasWebGLRenderingContext; + if ((version in contexts)) { + if (contextCleanup == null) { + if (ENV.getBool('IS_BROWSER')) { + // TODO - is there a better place to register this? + contextCleanup = cleanupDOMCanvasWebGLRenderingContext; + } } + if (contextCleanup != null) { + contextCleanup(contexts[version]); + } + delete contexts[version]; } - if (contextCleanup != null) { - contextCleanup(contexts[version]); - } - delete contexts[version]; } function bootstrapWebGLContext(gl: WebGLRenderingContext) { From 488798a32a1db35085e91001bcfc0c889edab57d Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 14:38:27 -0700 Subject: [PATCH 11/28] save --- src/backends/webgl/backend_webgl_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts index a8986db51c..5e94b8a58f 100644 --- a/src/backends/webgl/backend_webgl_test.ts +++ b/src/backends/webgl/backend_webgl_test.ts @@ -319,7 +319,7 @@ describeWithFlags('upload tensors as uniforms', FLOAT32_WEBGL_ENVS, () => { expect(m.numBytesInGPU).toBe(a.size * 4 * 2); }); - it('download and re-upload an output of a shader', async () => { + it('download and re-upload an output of a shader KREEGER', async () => { const vals = new Float32Array(SIZE_UPLOAD_UNIFORM + 1); vals.fill(2); const a = tf.square(vals); From 6482ed2360c01d4d060bcfae811c19667867b716 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 14:49:58 -0700 Subject: [PATCH 12/28] save --- src/backends/webgl/canvas_util.ts | 5 ++--- src/backends/webgl/gpgpu_context.ts | 4 ++-- src/backends/webgl/webgl_context_manager.ts | 8 +------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index df19e1017a..0f6651bc4c 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -1,5 +1,3 @@ -import {disposeActiveContext} from './webgl_context_manager'; - /** * @license * Copyright 2018 Google LLC. All Rights Reserved. @@ -50,6 +48,7 @@ export function cleanupDOMCanvasWebGLRenderingContext( export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { + console.log('$$$$ CREATING CANVAS: ' + webGLVersion); if (webGLVersion !== 1 && webGLVersion !== 2) { throw new Error('Cannot get WebGL rendering context, WebGL is disabled.'); } @@ -57,7 +56,7 @@ export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - disposeActiveContext(); + // disposeActiveContext(); }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index 4d59a1f59f..c4ec27ba65 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -22,7 +22,7 @@ import * as util from '../../util'; import * as gpgpu_util from './gpgpu_util'; import {TextureConfig} from './gpgpu_util'; import * as tex_util from './tex_util'; -import {disposeActiveContext, getActiveContext} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; import {WebGL1DisjointQueryTimerExtension, WebGL2DisjointQueryTimerExtension} from './webgl_types'; import * as webgl_util from './webgl_util'; @@ -111,7 +111,7 @@ export class GPGPUContext { webgl_util.callAndCheck( gl, this.debug, () => gl.deleteBuffer(this.indexBuffer)); this.disposed = true; - disposeActiveContext(); + // disposeActiveContext(); } public createFloat32MatrixTexture(rows: number, columns: number): diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index 7cc21fa671..459c12814c 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -18,7 +18,7 @@ import {ENV} from '../../environment'; import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; -let contexts: {[key: string]: WebGLRenderingContext} = {}; +const contexts: {[key: string]: WebGLRenderingContext} = {}; let contextFactory: (version: number) => WebGLRenderingContext = null; let contextCleanup: (context: WebGLRenderingContext) => void = null; @@ -30,12 +30,6 @@ let contextCleanup: (context: WebGLRenderingContext) => void = null; export function setContextFactory( factory: (version: number) => WebGLRenderingContext) { contextFactory = factory; - - // Clear out items (TODO kreeger): write a unit test for this? - for (const ctx in contexts) { - console.log('ctx: ' + ctx); - } - contexts = {}; } /** From 32b6880864133cd476243ea7eac3ab11e8d6b6aa Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 15:01:49 -0700 Subject: [PATCH 13/28] save --- src/backends/webgl/canvas_util.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 0f6651bc4c..df19e1017a 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -1,3 +1,5 @@ +import {disposeActiveContext} from './webgl_context_manager'; + /** * @license * Copyright 2018 Google LLC. All Rights Reserved. @@ -48,7 +50,6 @@ export function cleanupDOMCanvasWebGLRenderingContext( export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): WebGLRenderingContext { - console.log('$$$$ CREATING CANVAS: ' + webGLVersion); if (webGLVersion !== 1 && webGLVersion !== 2) { throw new Error('Cannot get WebGL rendering context, WebGL is disabled.'); } @@ -56,7 +57,7 @@ export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - // disposeActiveContext(); + disposeActiveContext(); }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || From 4e651a8503958d40f056d875ef413b249949b014 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 15:08:26 -0700 Subject: [PATCH 14/28] save --- src/backends/webgl/backend_webgl.ts | 10 +++++++--- src/backends/webgl/backend_webgl_test.ts | 2 +- src/backends/webgl/canvas_util.ts | 3 +-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index 9cfb34804d..eaacbc10c9 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -130,6 +130,7 @@ import {UnaryOpProgram} from './unaryop_gpu'; import * as unary_packed_op from './unaryop_packed_gpu'; import {UnaryOpPackedProgram} from './unaryop_packed_gpu'; import {UnpackProgram} from './unpack_gpu'; +import {disposeActiveContext, getActiveContext} from './webgl_context_manager'; import * as webgl_util from './webgl_util'; type KernelInfo = { @@ -247,15 +248,12 @@ export class MathBackendWebGL implements KernelBackend { } if (gpgpu == null) { - // TODO(kreeger): Fix this. this.binaryCache = getBinaryCache(ENV.getNumber('WEBGL_VERSION')); this.gpgpu = new GPGPUContext(); - // this.canvas = gl.canvas; this.gpgpuCreatedLocally = true; } else { this.binaryCache = {}; this.gpgpuCreatedLocally = false; - // this.canvas = gpgpu.gl.canvas; } this.textureManager = new TextureManager(this.gpgpu); this.numMBBeforeWarning = numMBBeforeWarning(); @@ -2510,6 +2508,12 @@ export class MathBackendWebGL implements KernelBackend { this.gpgpu.program = null; this.gpgpu.dispose(); } + + const gl = getActiveContext(); + if (gl.canvas != null && gl.canvas.remove != null) { + gl.canvas.remove(); + } + disposeActiveContext(); this.disposed = true; } diff --git a/src/backends/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts index 5e94b8a58f..a8986db51c 100644 --- a/src/backends/webgl/backend_webgl_test.ts +++ b/src/backends/webgl/backend_webgl_test.ts @@ -319,7 +319,7 @@ describeWithFlags('upload tensors as uniforms', FLOAT32_WEBGL_ENVS, () => { expect(m.numBytesInGPU).toBe(a.size * 4 * 2); }); - it('download and re-upload an output of a shader KREEGER', async () => { + it('download and re-upload an output of a shader', async () => { const vals = new Float32Array(SIZE_UPLOAD_UNIFORM + 1); vals.fill(2); const a = tf.square(vals); diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index df19e1017a..0dfbf729ab 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -1,5 +1,3 @@ -import {disposeActiveContext} from './webgl_context_manager'; - /** * @license * Copyright 2018 Google LLC. All Rights Reserved. @@ -16,6 +14,7 @@ import {disposeActiveContext} from './webgl_context_manager'; * limitations under the License. * ============================================================================= */ +import {disposeActiveContext} from './webgl_context_manager'; const WEBGL_ATTRIBUTES: WebGLContextAttributes = { alpha: false, From 67bd9837e7903c59566446ad45578b8fb6b8c136 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 15:10:14 -0700 Subject: [PATCH 15/28] save --- src/backends/webgl/canvas_util.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 0dfbf729ab..4e91af103a 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -14,7 +14,6 @@ * limitations under the License. * ============================================================================= */ -import {disposeActiveContext} from './webgl_context_manager'; const WEBGL_ATTRIBUTES: WebGLContextAttributes = { alpha: false, @@ -56,7 +55,6 @@ export function createDOMCanvasWebGLRenderingContext(webGLVersion: number): canvas.addEventListener('webglcontextlost', (ev: Event) => { ev.preventDefault(); - disposeActiveContext(); }, false); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || From 54c9673c51ed27d5cb22ca2ad79999ae0bdfc688 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 16:27:22 -0700 Subject: [PATCH 16/28] save --- src/backends/webgl/backend_webgl.ts | 4 ++-- src/backends/webgl/flags_webgl_test.ts | 2 ++ src/backends/webgl/gpgpu_context.ts | 19 +++++++++++++++++-- src/backends/webgl/gpgpu_context_test.ts | 17 +++++++---------- src/backends/webgl/webgl_context_manager.ts | 20 ++++++++++++++++++++ 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index eaacbc10c9..6218c0c588 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -130,7 +130,7 @@ import {UnaryOpProgram} from './unaryop_gpu'; import * as unary_packed_op from './unaryop_packed_gpu'; import {UnaryOpPackedProgram} from './unaryop_packed_gpu'; import {UnpackProgram} from './unpack_gpu'; -import {disposeActiveContext, getActiveContext} from './webgl_context_manager'; +import {getActiveContext} from './webgl_context_manager'; import * as webgl_util from './webgl_util'; type KernelInfo = { @@ -2509,11 +2509,11 @@ export class MathBackendWebGL implements KernelBackend { this.gpgpu.dispose(); } + // TODO(kreeger): Is this actually used? const gl = getActiveContext(); if (gl.canvas != null && gl.canvas.remove != null) { gl.canvas.remove(); } - disposeActiveContext(); this.disposed = true; } diff --git a/src/backends/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts index 303e702eea..8fdd857d7e 100644 --- a/src/backends/webgl/flags_webgl_test.ts +++ b/src/backends/webgl/flags_webgl_test.ts @@ -197,6 +197,7 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { ENV.reset(); webgl_util.MAX_TEXTURE_SIZE = null; + // TODO(kreeger): This spy doesn't work anymore spyOn(webgl_context_manager, 'getActiveContext').and.returnValue({ MAX_TEXTURE_SIZE: 101, getParameter: (param: number) => { @@ -223,6 +224,7 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { ENV.reset(); webgl_util.MAX_TEXTURES_IN_SHADER = null; + // TODO(kreeger): This spy doesn't work anymore spyOn(webgl_context_manager, 'getActiveContext').and.callFake(() => { return { MAX_TEXTURE_IMAGE_UNITS: 101, diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index c4ec27ba65..df71e4d692 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -31,8 +31,16 @@ export interface FenceContext { isFencePassed(): boolean; } +function checkGL(msg: string, gl: WebGLRenderingContext) { + const error = gl.getError(); + if (error !== gl.NO_ERROR) { + console.log('>> ' + msg); + console.log('>> ERROR: ' + webgl_util.getWebGLErrorMessage(gl, error)); + console.log('>> VERSION: ' + gl); + } +} + export class GPGPUContext { - // gl: WebGLRenderingContext; textureFloatExtension: {}; textureHalfFloatExtension: {}; colorBufferFloatExtension: {}; @@ -50,6 +58,9 @@ export class GPGPUContext { constructor() { const gl = getActiveContext(); + // This causes an exception everytime on WebGL2 - need to figure out + // which test/check/call is setting the GL in a bad way. + checkGL('get active context', gl); // WebGL 2.0 enables texture floats without an extension. if (ENV.getNumber('WEBGL_VERSION') === 1) { @@ -68,13 +79,18 @@ export class GPGPUContext { this.colorBufferFloatExtension = webgl_util.getExtensionOrThrow( gl, this.debug, 'EXT_color_buffer_float'); } + checkGL('extension checks', gl); this.vertexBuffer = gpgpu_util.createVertexBuffer(gl, this.debug); + checkGL('vertex buffer', gl); this.indexBuffer = gpgpu_util.createIndexBuffer(gl, this.debug); + checkGL('index buffer', gl); this.framebuffer = webgl_util.createFramebuffer(gl, this.debug); + checkGL('frame buffer', gl); this.textureConfig = gpgpu_util.getTextureConfig(gl, this.textureHalfFloatExtension); + checkGL('texture config', gl); } private get debug(): boolean { @@ -111,7 +127,6 @@ export class GPGPUContext { webgl_util.callAndCheck( gl, this.debug, () => gl.deleteBuffer(this.indexBuffer)); this.disposed = true; - // disposeActiveContext(); } public createFloat32MatrixTexture(rows: number, columns: number): diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index 0bdd3ec63b..c255ec2346 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -32,25 +32,22 @@ const DOWNLOAD_FLOAT_ENVS = { describeWithFlags( 'GPGPUContext setOutputMatrixTexture', DOWNLOAD_FLOAT_ENVS, () => { let gpgpu: GPGPUContext; - let texture: WebGLTexture; beforeEach(() => { gpgpu = new GPGPUContext(); - // Silences debug warnings. - spyOn(console, 'warn'); - ENV.set('DEBUG', true); - texture = gpgpu.createFloat32MatrixTexture(1, 1); }); afterEach(() => { - gpgpu.deleteMatrixTexture(texture); gpgpu.dispose(); }); - it('sets the output texture property to the output texture', () => { - gpgpu.setOutputMatrixTexture(texture, 1, 1); - expect(gpgpu.outputTexture).toBe(texture); - }); + it('sets the output texture property to the output texture KREEGER', + () => { + const texture = gpgpu.createFloat32MatrixTexture(1, 1); + gpgpu.setOutputMatrixTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBe(texture); + gpgpu.deleteMatrixTexture(texture); + }); it('sets the gl viewport to the output texture dimensions', () => { const columns = 456; diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index 459c12814c..551b1b7241 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -15,6 +15,7 @@ * ============================================================================= */ import {ENV} from '../../environment'; +import {webgl_util} from '../../webgl'; import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; @@ -97,14 +98,33 @@ function disposeWebGLContext(version: number) { } } +function checkGL(msg: string, gl: WebGLRenderingContext) { + const error = gl.getError(); + if (error !== gl.NO_ERROR) { + console.log('# ' + msg); + console.log('# ERROR: ' + webgl_util.getWebGLErrorMessage(gl, error)); + console.log('# VERSION: ' + gl); + } +} + function bootstrapWebGLContext(gl: WebGLRenderingContext) { + // console.log(' --- bootstrap: ' + ENV.getNumber('WEBGL_VERSION')); gl.disable(gl.DEPTH_TEST); + checkGL('DEPTH_TEST', gl); gl.disable(gl.STENCIL_TEST); + checkGL('STENCIL_TEST', gl); gl.disable(gl.BLEND); + checkGL('BLEND', gl); gl.disable(gl.DITHER); + checkGL('DITHER', gl); gl.disable(gl.POLYGON_OFFSET_FILL); + checkGL('POLYGON_OFFSET_FILL', gl); gl.disable(gl.SAMPLE_COVERAGE); + checkGL('SAMPLE_COVERAGE1', gl); gl.enable(gl.SCISSOR_TEST); + checkGL('SCISSOR_TEST', gl); gl.enable(gl.CULL_FACE); + checkGL('CULL_FACE', gl); gl.cullFace(gl.BACK); + checkGL('BACK', gl); } From 5ef8592e6078378a8d6341155a78b7aa9b3fc77f Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Tue, 23 Jul 2019 16:39:04 -0700 Subject: [PATCH 17/28] save --- src/backends/webgl/webgl_context_manager.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index 551b1b7241..d8e3b5847d 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -73,6 +73,9 @@ export function getContextByVersion(version: number): WebGLRenderingContext { disposeWebGLContext(version); return getContextByVersion(version); } + // if (version === 2) { + // console.log(' getContextByVersion()'); + // } return contexts[version]; } From b5186450a1a73cffbb744f357ea36a98dceda0d2 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 14:08:26 -0700 Subject: [PATCH 18/28] save --- src/backends/webgl/canvas_util.ts | 2 +- src/backends/webgl/canvas_util_test.ts | 51 +++++++++++---------- src/backends/webgl/gpgpu_context.ts | 15 ------ src/backends/webgl/webgl_context_manager.ts | 23 ---------- 4 files changed, 27 insertions(+), 64 deletions(-) diff --git a/src/backends/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts index 4e91af103a..4acf6d1f59 100644 --- a/src/backends/webgl/canvas_util.ts +++ b/src/backends/webgl/canvas_util.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2018 Google LLC. All Rights Reserved. + * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/src/backends/webgl/canvas_util_test.ts b/src/backends/webgl/canvas_util_test.ts index dacc2ffa27..daa4a6db94 100644 --- a/src/backends/webgl/canvas_util_test.ts +++ b/src/backends/webgl/canvas_util_test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2018 Google LLC. All Rights Reserved. + * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -15,31 +15,32 @@ * ============================================================================= */ -// import {ENV} from '../../environment'; -// import {BROWSER_ENVS, describeWithFlags} from '../../jasmine_util'; +import {ENV} from '../../environment'; +import {BROWSER_ENVS, describeWithFlags} from '../../jasmine_util'; -// import {getWebGLContext} from './canvas_util'; +import {createDOMCanvasWebGLRenderingContext} from './canvas_util'; -// describeWithFlags('canvas_util', BROWSER_ENVS, () => { -// it('Returns a valid canvas', () => { -// const canvas = getWebGLContext(ENV.getNumber('WEBGL_VERSION')).canvas as -// ( -// HTMLCanvasElement | OffscreenCanvas); -// expect( -// (canvas instanceof HTMLCanvasElement) || -// (canvas instanceof OffscreenCanvas)) -// .toBe(true); -// }); +describeWithFlags('canvas_util', BROWSER_ENVS, () => { + it('Returns a valid canvas', () => { + const canvas = + createDOMCanvasWebGLRenderingContext(ENV.getNumber('WEBGL_VERSION')) + .canvas as (HTMLCanvasElement | OffscreenCanvas); + expect( + (canvas instanceof HTMLCanvasElement) || + (canvas instanceof OffscreenCanvas)) + .toBe(true); + }); -// it('Returns a valid gl context', () => { -// const gl = getWebGLContext(ENV.getNumber('WEBGL_VERSION')); -// expect(gl.isContextLost()).toBe(false); -// }); -// }); + it('Returns a valid gl context', () => { + const gl = + createDOMCanvasWebGLRenderingContext(ENV.getNumber('WEBGL_VERSION')); + expect(gl.isContextLost()).toBe(false); + }); +}); -// describeWithFlags('canvas_util webgl2', {flags: {WEBGL_VERSION: 2}}, () => { -// it('is ok when the user requests webgl 1 canvas', () => { -// const canvas = getWebGLContext(1).canvas; -// expect((canvas instanceof HTMLCanvasElement)).toBe(true); -// }); -// }); +describeWithFlags('canvas_util webgl2', {flags: {WEBGL_VERSION: 2}}, () => { + it('is ok when the user requests webgl 1 canvas', () => { + const canvas = createDOMCanvasWebGLRenderingContext(1).canvas; + expect((canvas instanceof HTMLCanvasElement)).toBe(true); + }); +}); diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index df71e4d692..7f0d58b235 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -31,15 +31,6 @@ export interface FenceContext { isFencePassed(): boolean; } -function checkGL(msg: string, gl: WebGLRenderingContext) { - const error = gl.getError(); - if (error !== gl.NO_ERROR) { - console.log('>> ' + msg); - console.log('>> ERROR: ' + webgl_util.getWebGLErrorMessage(gl, error)); - console.log('>> VERSION: ' + gl); - } -} - export class GPGPUContext { textureFloatExtension: {}; textureHalfFloatExtension: {}; @@ -60,7 +51,6 @@ export class GPGPUContext { const gl = getActiveContext(); // This causes an exception everytime on WebGL2 - need to figure out // which test/check/call is setting the GL in a bad way. - checkGL('get active context', gl); // WebGL 2.0 enables texture floats without an extension. if (ENV.getNumber('WEBGL_VERSION') === 1) { @@ -79,18 +69,13 @@ export class GPGPUContext { this.colorBufferFloatExtension = webgl_util.getExtensionOrThrow( gl, this.debug, 'EXT_color_buffer_float'); } - checkGL('extension checks', gl); this.vertexBuffer = gpgpu_util.createVertexBuffer(gl, this.debug); - checkGL('vertex buffer', gl); this.indexBuffer = gpgpu_util.createIndexBuffer(gl, this.debug); - checkGL('index buffer', gl); this.framebuffer = webgl_util.createFramebuffer(gl, this.debug); - checkGL('frame buffer', gl); this.textureConfig = gpgpu_util.getTextureConfig(gl, this.textureHalfFloatExtension); - checkGL('texture config', gl); } private get debug(): boolean { diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index d8e3b5847d..459c12814c 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -15,7 +15,6 @@ * ============================================================================= */ import {ENV} from '../../environment'; -import {webgl_util} from '../../webgl'; import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; @@ -73,9 +72,6 @@ export function getContextByVersion(version: number): WebGLRenderingContext { disposeWebGLContext(version); return getContextByVersion(version); } - // if (version === 2) { - // console.log(' getContextByVersion()'); - // } return contexts[version]; } @@ -101,33 +97,14 @@ function disposeWebGLContext(version: number) { } } -function checkGL(msg: string, gl: WebGLRenderingContext) { - const error = gl.getError(); - if (error !== gl.NO_ERROR) { - console.log('# ' + msg); - console.log('# ERROR: ' + webgl_util.getWebGLErrorMessage(gl, error)); - console.log('# VERSION: ' + gl); - } -} - function bootstrapWebGLContext(gl: WebGLRenderingContext) { - // console.log(' --- bootstrap: ' + ENV.getNumber('WEBGL_VERSION')); gl.disable(gl.DEPTH_TEST); - checkGL('DEPTH_TEST', gl); gl.disable(gl.STENCIL_TEST); - checkGL('STENCIL_TEST', gl); gl.disable(gl.BLEND); - checkGL('BLEND', gl); gl.disable(gl.DITHER); - checkGL('DITHER', gl); gl.disable(gl.POLYGON_OFFSET_FILL); - checkGL('POLYGON_OFFSET_FILL', gl); gl.disable(gl.SAMPLE_COVERAGE); - checkGL('SAMPLE_COVERAGE1', gl); gl.enable(gl.SCISSOR_TEST); - checkGL('SCISSOR_TEST', gl); gl.enable(gl.CULL_FACE); - checkGL('CULL_FACE', gl); gl.cullFace(gl.BACK); - checkGL('BACK', gl); } From 8f72579aba613cec9e2d80c97789327643ce11e2 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 14:14:38 -0700 Subject: [PATCH 19/28] save --- src/backends/webgl/gpgpu_context.ts | 2 -- src/backends/webgl/gpgpu_context_test.ts | 13 ++++++------- src/platforms/platform_browser.ts | 1 - 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index 7f0d58b235..c6b880aea9 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -49,8 +49,6 @@ export class GPGPUContext { constructor() { const gl = getActiveContext(); - // This causes an exception everytime on WebGL2 - need to figure out - // which test/check/call is setting the GL in a bad way. // WebGL 2.0 enables texture floats without an extension. if (ENV.getNumber('WEBGL_VERSION') === 1) { diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index c255ec2346..654d23b7e5 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -41,13 +41,12 @@ describeWithFlags( gpgpu.dispose(); }); - it('sets the output texture property to the output texture KREEGER', - () => { - const texture = gpgpu.createFloat32MatrixTexture(1, 1); - gpgpu.setOutputMatrixTexture(texture, 1, 1); - expect(gpgpu.outputTexture).toBe(texture); - gpgpu.deleteMatrixTexture(texture); - }); + it('sets the output texture property to the output texture', () => { + const texture = gpgpu.createFloat32MatrixTexture(1, 1); + gpgpu.setOutputMatrixTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBe(texture); + gpgpu.deleteMatrixTexture(texture); + }); it('sets the gl viewport to the output texture dimensions', () => { const columns = 456; diff --git a/src/platforms/platform_browser.ts b/src/platforms/platform_browser.ts index ad8c337eb2..25d9d3028a 100644 --- a/src/platforms/platform_browser.ts +++ b/src/platforms/platform_browser.ts @@ -15,7 +15,6 @@ * ============================================================================= */ import {ENV} from '../environment'; - import {Platform} from './platform'; export class PlatformBrowser implements Platform { From 9d0a172b6b78ee28b088d6731bdd21a7d83904a7 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 15:04:41 -0700 Subject: [PATCH 20/28] save --- src/backends/webgl/webgl_context_manager.ts | 26 +++--- .../webgl/webgl_context_manager_test.ts | 79 +++++++++++++++++++ 2 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 src/backends/webgl/webgl_context_manager_test.ts diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index 459c12814c..ffb35ef566 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -23,9 +23,8 @@ let contextFactory: (version: number) => WebGLRenderingContext = null; let contextCleanup: (context: WebGLRenderingContext) => void = null; /** - * Sets callback for creating new WebGLRenderingContext instances. - * @param factory The callback function that returns a WebGLRenderingContext - * instance. + * Sets the callback for creating new WebGLRenderingContext instances. + * @param factory The callback function that returns a context instance. */ export function setContextFactory( factory: (version: number) => WebGLRenderingContext) { @@ -33,8 +32,9 @@ export function setContextFactory( } /** - * TODO(kreeger): doc me. - * @param cleanup + * Sets the callback for cleaning up WebGLRenderingContext instances. + * @param cleanup The callback function to cleanup the passed in context + * instance. */ export function setContextCleanup( cleanup: (context: WebGLRenderingContext) => void) { @@ -42,21 +42,22 @@ export function setContextCleanup( } /** - * Returns the current WebGLContext + * Returns the current WebGLRenderingContext based on the ENV flag for + * 'WEBGL_VERSION'. */ export function getActiveContext(): WebGLRenderingContext { return getContextByVersion(ENV.getNumber('WEBGL_VERSION')); } /** - * TODO(kreeger): Doc me. + * Returns the WebGLRenderingContext for a given version number. * @param version The specific version of WebGL to request. */ export function getContextByVersion(version: number): WebGLRenderingContext { // Default to browser context creation is running in the browser. if (contextFactory == null) { if (ENV.getBool('IS_BROWSER')) { - // TODO - is there a better place to register this? + // TODO(kreeger): Is there a better place to register this? contextFactory = createDOMCanvasWebGLRenderingContext; } else { throw new Error('Default WebGLRenderingContext factory was not set!'); @@ -75,18 +76,11 @@ export function getContextByVersion(version: number): WebGLRenderingContext { return contexts[version]; } -/** - * TODO(kreeger): Doc me. - */ -export function disposeActiveContext() { - disposeWebGLContext(ENV.getNumber('WEBGL_VERSION')); -} - function disposeWebGLContext(version: number) { if ((version in contexts)) { if (contextCleanup == null) { if (ENV.getBool('IS_BROWSER')) { - // TODO - is there a better place to register this? + // TODO(kreeger): Is there a better place to register this? contextCleanup = cleanupDOMCanvasWebGLRenderingContext; } } diff --git a/src/backends/webgl/webgl_context_manager_test.ts b/src/backends/webgl/webgl_context_manager_test.ts new file mode 100644 index 0000000000..0bdc5f1463 --- /dev/null +++ b/src/backends/webgl/webgl_context_manager_test.ts @@ -0,0 +1,79 @@ +/** + * @license + * Copyright 2019 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {BROWSER_ENVS, describeWithFlags} from '../../jasmine_util'; + +import {getActiveContext, getContextByVersion, setContextCleanup, setContextFactory} from './webgl_context_manager'; + +describeWithFlags('webgl_context_manager', BROWSER_ENVS, () => { + it('returns the active context for browser WebGL', () => { + const canvas = getActiveContext(); + expect( + (canvas instanceof WebGL2RenderingContext) || + (canvas instanceof WebGLRenderingContext)) + .toBe(true); + }); +}); + +describeWithFlags( + 'webgl_context_manager webgl2', {flags: {WEBGL_VERSION: 2}}, () => { + it('returns webgl1 canvas under webgl2', () => { + const canvas = getContextByVersion(1); + expect(canvas instanceof WebGLRenderingContext).toBe(true); + }); + }); + +describe('webgl_context_manager create/cleanup', () => { + it('should call factory method to create WebGLRenderingContext', () => { + let created = false; + let cleanedup = false; + let contextLost = false; + const contextFake = { + disable: (cap: number) => {}, + enable: (cap: number) => {}, + cullFace: (cap: number) => {}, + isContextLost: () => { + return contextLost; + } + } as WebGLRenderingContext; + + setContextFactory((version: number) => { + created = true; + return contextFake; + }); + + // Request context version '10' to bypass any cached system WebGL versions: + const context = getContextByVersion(10); + expect(created).toBe(true); + expect(context).toBe(contextFake); + + // Mark fake context as disposed so it will be cleanedup on next context + // creation request: + setContextCleanup((context: WebGLRenderingContext) => { + expect(context).toBe(contextFake); + cleanedup = true; + + // Set context lost back to false to prevent an endless loop: + contextLost = false; + }); + + contextLost = true; + getContextByVersion(10); + + expect(cleanedup).toBe(true); + }); +}); From 50a22faf5668cdba652b2b97fc85ec3010a6bd09 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 15:11:31 -0700 Subject: [PATCH 21/28] save --- src/backends/webgl/webgl_context_manager_test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/backends/webgl/webgl_context_manager_test.ts b/src/backends/webgl/webgl_context_manager_test.ts index 0bdc5f1463..fe0a23d571 100644 --- a/src/backends/webgl/webgl_context_manager_test.ts +++ b/src/backends/webgl/webgl_context_manager_test.ts @@ -38,6 +38,12 @@ describeWithFlags( }); describe('webgl_context_manager create/cleanup', () => { + afterAll(() => { + // Reset global context creation and cleanup: + setContextCleanup(null); + setContextFactory(null); + }); + it('should call factory method to create WebGLRenderingContext', () => { let created = false; let cleanedup = false; From 9a6ae6670acc20b7684f8e9a0b61414d6d2a6a35 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 15:27:00 -0700 Subject: [PATCH 22/28] save --- src/backends/webgl/flags_webgl_test.ts | 6 ++---- src/backends/webgl/gpgpu_context_test.ts | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/backends/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts index 8fdd857d7e..1589fc8270 100644 --- a/src/backends/webgl/flags_webgl_test.ts +++ b/src/backends/webgl/flags_webgl_test.ts @@ -197,8 +197,7 @@ describe('WEBGL_MAX_TEXTURE_SIZE', () => { ENV.reset(); webgl_util.MAX_TEXTURE_SIZE = null; - // TODO(kreeger): This spy doesn't work anymore - spyOn(webgl_context_manager, 'getActiveContext').and.returnValue({ + spyOn(webgl_context_manager, 'getContextByVersion').and.returnValue({ MAX_TEXTURE_SIZE: 101, getParameter: (param: number) => { if (param === 101) { @@ -224,8 +223,7 @@ describe('WEBGL_MAX_TEXTURES_IN_SHADER', () => { ENV.reset(); webgl_util.MAX_TEXTURES_IN_SHADER = null; - // TODO(kreeger): This spy doesn't work anymore - spyOn(webgl_context_manager, 'getActiveContext').and.callFake(() => { + spyOn(webgl_context_manager, 'getContextByVersion').and.callFake(() => { return { MAX_TEXTURE_IMAGE_UNITS: 101, getParameter: (param: number) => { diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index 654d23b7e5..98da57f7ca 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -67,9 +67,6 @@ describeWithFlags( beforeEach(() => { gpgpu = new GPGPUContext(); - // Silences debug warnings. - spyOn(console, 'warn'); - ENV.set('DEBUG', true); }); afterEach(() => { From 3775bf4f7fe87c0286623d1f1fa1b3b71b92757b Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 15:38:28 -0700 Subject: [PATCH 23/28] save --- src/backends/webgl/backend_webgl.ts | 1 - src/backends/webgl/gpgpu_context_test.ts | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/backends/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts index 6218c0c588..aaee9a9254 100644 --- a/src/backends/webgl/backend_webgl.ts +++ b/src/backends/webgl/backend_webgl.ts @@ -2509,7 +2509,6 @@ export class MathBackendWebGL implements KernelBackend { this.gpgpu.dispose(); } - // TODO(kreeger): Is this actually used? const gl = getActiveContext(); if (gl.canvas != null && gl.canvas.remove != null) { gl.canvas.remove(); diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index 98da57f7ca..0bdd3ec63b 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -32,20 +32,24 @@ const DOWNLOAD_FLOAT_ENVS = { describeWithFlags( 'GPGPUContext setOutputMatrixTexture', DOWNLOAD_FLOAT_ENVS, () => { let gpgpu: GPGPUContext; + let texture: WebGLTexture; beforeEach(() => { gpgpu = new GPGPUContext(); + // Silences debug warnings. + spyOn(console, 'warn'); + ENV.set('DEBUG', true); + texture = gpgpu.createFloat32MatrixTexture(1, 1); }); afterEach(() => { + gpgpu.deleteMatrixTexture(texture); gpgpu.dispose(); }); it('sets the output texture property to the output texture', () => { - const texture = gpgpu.createFloat32MatrixTexture(1, 1); gpgpu.setOutputMatrixTexture(texture, 1, 1); expect(gpgpu.outputTexture).toBe(texture); - gpgpu.deleteMatrixTexture(texture); }); it('sets the gl viewport to the output texture dimensions', () => { @@ -67,6 +71,9 @@ describeWithFlags( beforeEach(() => { gpgpu = new GPGPUContext(); + // Silences debug warnings. + spyOn(console, 'warn'); + ENV.set('DEBUG', true); }); afterEach(() => { From 54021e1dae30e3016a5cf987366e1fa1ddf39383 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 16:09:34 -0700 Subject: [PATCH 24/28] save --- src/backends/webgl/gpgpu_context.ts | 2 ++ src/backends/webgl/gpgpu_context_test.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index c6b880aea9..7078caf1cd 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -99,6 +99,8 @@ export class GPGPUContext { } const gl = getActiveContext(); webgl_util.callAndCheck(gl, this.debug, () => gl.finish()); + // TODO(kreeger): This bind framebuffer call can throw an INVALID_OPERATION + // error on WebGL2 - fix this. webgl_util.callAndCheck( gl, this.debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); webgl_util.callAndCheck( diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index 0bdd3ec63b..95804b06d9 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -44,6 +44,7 @@ describeWithFlags( afterEach(() => { gpgpu.deleteMatrixTexture(texture); + ENV.set('DEBUG', false); gpgpu.dispose(); }); @@ -80,6 +81,7 @@ describeWithFlags( if (texture != null) { gpgpu.deleteMatrixTexture(texture); } + ENV.set('DEBUG', false); gpgpu.dispose(); }); @@ -131,6 +133,7 @@ describeWithFlags( afterEach(() => { gpgpu.deleteMatrixTexture(output); gpgpu.deleteProgram(program); + ENV.set('DEBUG', false); gpgpu.dispose(); }); @@ -156,6 +159,7 @@ describeWithFlags('GPGPUContext', DOWNLOAD_FLOAT_ENVS, () => { }); afterEach(() => { + ENV.set('DEBUG', false); gpgpu.dispose(); }); From add8aed70b3d57ffd85092b915aff14c1b20b8b2 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Wed, 24 Jul 2019 16:10:52 -0700 Subject: [PATCH 25/28] backout resets --- src/backends/webgl/gpgpu_context_test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index 95804b06d9..0bdd3ec63b 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -44,7 +44,6 @@ describeWithFlags( afterEach(() => { gpgpu.deleteMatrixTexture(texture); - ENV.set('DEBUG', false); gpgpu.dispose(); }); @@ -81,7 +80,6 @@ describeWithFlags( if (texture != null) { gpgpu.deleteMatrixTexture(texture); } - ENV.set('DEBUG', false); gpgpu.dispose(); }); @@ -133,7 +131,6 @@ describeWithFlags( afterEach(() => { gpgpu.deleteMatrixTexture(output); gpgpu.deleteProgram(program); - ENV.set('DEBUG', false); gpgpu.dispose(); }); @@ -159,7 +156,6 @@ describeWithFlags('GPGPUContext', DOWNLOAD_FLOAT_ENVS, () => { }); afterEach(() => { - ENV.set('DEBUG', false); gpgpu.dispose(); }); From dcb400027b8850c05f54ccf4e08cd42270eb2992 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Fri, 26 Jul 2019 09:16:43 -0700 Subject: [PATCH 26/28] save --- src/backends/webgl/webgl_context_manager.ts | 3 +++ src/backends/webgl/webgl_util.ts | 2 +- src/debug_mode_test.ts | 2 +- src/webgl.ts | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index ffb35ef566..4a8be25e17 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -17,6 +17,7 @@ import {ENV} from '../../environment'; import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; +import {checkWebGLError} from './webgl_util'; const contexts: {[key: string]: WebGLRenderingContext} = {}; let contextFactory: (version: number) => WebGLRenderingContext = null; @@ -68,6 +69,8 @@ export function getContextByVersion(version: number): WebGLRenderingContext { contexts[version] = contextFactory(version); bootstrapWebGLContext(contexts[version]); } + + checkWebGLError(contexts[version]); const gl = contexts[version]; if (gl.isContextLost()) { disposeWebGLContext(version); diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index 2ec3c26a5f..1a7bab3bee 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -28,7 +28,7 @@ export function callAndCheck( return returnValue; } -function checkWebGLError(gl: WebGLRenderingContext) { +export function checkWebGLError(gl: WebGLRenderingContext) { const error = gl.getError(); if (error !== gl.NO_ERROR) { throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); diff --git a/src/debug_mode_test.ts b/src/debug_mode_test.ts index d79eca55c0..4d0e3bffc9 100644 --- a/src/debug_mode_test.ts +++ b/src/debug_mode_test.ts @@ -20,7 +20,7 @@ import {ALL_ENVS, describeWithFlags, SYNC_BACKEND_ENVS} from './jasmine_util'; import {convertToTensor} from './tensor_util_env'; import {expectArraysClose} from './test_util'; -describeWithFlags('debug on', SYNC_BACKEND_ENVS, () => { +describeWithFlags('KREEGER debug on', SYNC_BACKEND_ENVS, () => { beforeAll(() => { tf.ENV.set('DEBUG', true); }); diff --git a/src/webgl.ts b/src/webgl.ts index e21c38a236..54386b4429 100644 --- a/src/webgl.ts +++ b/src/webgl.ts @@ -21,5 +21,6 @@ import * as webgl_util from './backends/webgl/webgl_util'; export {MathBackendWebGL, WebGLMemoryInfo, WebGLTimingInfo} from './backends/webgl/backend_webgl'; export {GPGPUContext} from './backends/webgl/gpgpu_context'; export {GPGPUProgram} from './backends/webgl/gpgpu_math'; +export {getActiveContext, getContextByVersion, setContextCleanup, setContextFactory} from './backends/webgl/webgl_context_manager'; // WebGL specific utils. export {gpgpu_util, webgl_util}; From a370b9adcfd0c35c5ecaa618e18606d377b7f969 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Fri, 26 Jul 2019 19:10:36 -0700 Subject: [PATCH 27/28] save --- src/backends/webgl/webgl_context_manager.ts | 30 ++++++++++++++++++--- src/backends/webgl/webgl_util.ts | 7 +++++ src/debug_mode_test.ts | 4 +-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index 4a8be25e17..f843284329 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -17,7 +17,7 @@ import {ENV} from '../../environment'; import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; -import {checkWebGLError} from './webgl_util'; +import {callAndCheck, checkWebGLError} from './webgl_util'; const contexts: {[key: string]: WebGLRenderingContext} = {}; let contextFactory: (version: number) => WebGLRenderingContext = null; @@ -66,11 +66,11 @@ export function getContextByVersion(version: number): WebGLRenderingContext { } if (!(version in contexts)) { - contexts[version] = contextFactory(version); + contexts[version] = traceGLCalls(contextFactory(version)); bootstrapWebGLContext(contexts[version]); + checkWebGLError(contexts[version]); } - checkWebGLError(contexts[version]); const gl = contexts[version]; if (gl.isContextLost()) { disposeWebGLContext(version); @@ -95,7 +95,9 @@ function disposeWebGLContext(version: number) { } function bootstrapWebGLContext(gl: WebGLRenderingContext) { - gl.disable(gl.DEPTH_TEST); + // TODO - check GL calls here too. + callAndCheck(gl, ENV.getBool('DEBUG'), () => gl.disable(gl.DEPTH_TEST)); + // gl.disable(gl.DEPTH_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.BLEND); gl.disable(gl.DITHER); @@ -105,3 +107,23 @@ function bootstrapWebGLContext(gl: WebGLRenderingContext) { gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); } + +function traceGLCalls(ctx: WebGLRenderingContext) { + const handler = { + // tslint:disable-next-line:no-any + get(target: any, prop: PropertyKey, receiver: any): any { + const propValue = target[prop]; + + if (typeof (propValue) === 'function') { + console.log( + ' gl.' + prop.toString() + ' = ' + target.constructor.name); + // tslint:disable-next-line:only-arrow-functions + return function() { + return propValue.apply(target, arguments); + }; + } + return propValue; + }, + }; + return new Proxy(ctx, handler); +} diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index 1a7bab3bee..ae8035eb25 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -607,6 +607,13 @@ function createFloatTextureAndBindToFramebuffer( gl.bindTexture(gl.TEXTURE_2D, texture); + // TODO - fix this + // webgl_util.callAndCheck( + // gl, debug, + // () => gl.texImage2D( + // tex2d, 0, internalFormat, width, height, 0, textureFormat, + // textureType, null)); + // tslint:disable-next-line:no-any const internalFormat = webGLVersion === 2 ? (gl as any).RGBA32F : gl.RGBA; gl.texImage2D( diff --git a/src/debug_mode_test.ts b/src/debug_mode_test.ts index 4d0e3bffc9..8da3b80790 100644 --- a/src/debug_mode_test.ts +++ b/src/debug_mode_test.ts @@ -20,7 +20,7 @@ import {ALL_ENVS, describeWithFlags, SYNC_BACKEND_ENVS} from './jasmine_util'; import {convertToTensor} from './tensor_util_env'; import {expectArraysClose} from './test_util'; -describeWithFlags('KREEGER debug on', SYNC_BACKEND_ENVS, () => { +describeWithFlags('debug on', SYNC_BACKEND_ENVS, () => { beforeAll(() => { tf.ENV.set('DEBUG', true); }); @@ -29,7 +29,7 @@ describeWithFlags('KREEGER debug on', SYNC_BACKEND_ENVS, () => { tf.ENV.set('DEBUG', false); }); - it('debug mode does not error when no nans', async () => { + it('KREEGER debug mode does not error when no nans', async () => { const a = tf.tensor1d([2, -1, 0, 3]); const res = tf.relu(a); expectArraysClose(await res.data(), [2, 0, 0, 3]); From 5da2f71f0cd8bb13e57a0325b072b0119f6ed990 Mon Sep 17 00:00:00 2001 From: Nick Kreeger Date: Mon, 29 Jul 2019 16:23:04 -0700 Subject: [PATCH 28/28] save debug --- src/backends/webgl/gpgpu_context.ts | 21 ++++---- src/backends/webgl/webgl_context_manager.ts | 10 ++-- src/backends/webgl/webgl_util.ts | 53 ++++++++++++--------- src/debug_mode_test.ts | 2 + 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/backends/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts index 7078caf1cd..cd560a3c4e 100644 --- a/src/backends/webgl/gpgpu_context.ts +++ b/src/backends/webgl/gpgpu_context.ts @@ -49,6 +49,7 @@ export class GPGPUContext { constructor() { const gl = getActiveContext(); + webgl_util.checkWebGLError(gl); // WebGL 2.0 enables texture floats without an extension. if (ENV.getNumber('WEBGL_VERSION') === 1) { @@ -97,20 +98,21 @@ export class GPGPUContext { 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + 'disposing.'); } + const debug = true; const gl = getActiveContext(); - webgl_util.callAndCheck(gl, this.debug, () => gl.finish()); + webgl_util.checkWebGLError(gl); + webgl_util.callAndCheck(gl, debug, () => gl.finish()); // TODO(kreeger): This bind framebuffer call can throw an INVALID_OPERATION // error on WebGL2 - fix this. webgl_util.callAndCheck( - gl, this.debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); + gl, debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); webgl_util.callAndCheck( - gl, this.debug, () => gl.deleteFramebuffer(this.framebuffer)); + gl, debug, () => gl.deleteFramebuffer(this.framebuffer)); webgl_util.callAndCheck( - gl, this.debug, () => gl.bindBuffer(gl.ARRAY_BUFFER, null)); + gl, debug, () => gl.bindBuffer(gl.ARRAY_BUFFER, null)); webgl_util.callAndCheck( - gl, this.debug, () => gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null)); - webgl_util.callAndCheck( - gl, this.debug, () => gl.deleteBuffer(this.indexBuffer)); + gl, debug, () => gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null)); + webgl_util.callAndCheck(gl, debug, () => gl.deleteBuffer(this.indexBuffer)); this.disposed = true; } @@ -162,7 +164,7 @@ export class GPGPUContext { WebGLTexture { this.throwIfDisposed(); return gpgpu_util.createPackedMatrixTexture( - getActiveContext(), this.debug, rows, columns, this.textureConfig); + getActiveContext(), true, rows, columns, this.textureConfig); } public deleteMatrixTexture(texture: WebGLTexture) { @@ -172,8 +174,9 @@ export class GPGPUContext { getActiveContext(), this.debug, this.framebuffer); this.outputTexture = null; } + console.log(' texture: ' + texture); webgl_util.callAndCheck( - getActiveContext(), this.debug, + getActiveContext(), true, () => getActiveContext().deleteTexture(texture)); } diff --git a/src/backends/webgl/webgl_context_manager.ts b/src/backends/webgl/webgl_context_manager.ts index f843284329..20fce05806 100644 --- a/src/backends/webgl/webgl_context_manager.ts +++ b/src/backends/webgl/webgl_context_manager.ts @@ -19,6 +19,7 @@ import {ENV} from '../../environment'; import {cleanupDOMCanvasWebGLRenderingContext, createDOMCanvasWebGLRenderingContext} from './canvas_util'; import {callAndCheck, checkWebGLError} from './webgl_util'; +let count = 0; const contexts: {[key: string]: WebGLRenderingContext} = {}; let contextFactory: (version: number) => WebGLRenderingContext = null; let contextCleanup: (context: WebGLRenderingContext) => void = null; @@ -66,16 +67,18 @@ export function getContextByVersion(version: number): WebGLRenderingContext { } if (!(version in contexts)) { - contexts[version] = traceGLCalls(contextFactory(version)); + contexts[version] = traceGLCalls(contextFactory(version), ++count); bootstrapWebGLContext(contexts[version]); checkWebGLError(contexts[version]); } const gl = contexts[version]; if (gl.isContextLost()) { + checkWebGLError(contexts[version]); disposeWebGLContext(version); return getContextByVersion(version); } + checkWebGLError(contexts[version]); return contexts[version]; } @@ -108,7 +111,7 @@ function bootstrapWebGLContext(gl: WebGLRenderingContext) { gl.cullFace(gl.BACK); } -function traceGLCalls(ctx: WebGLRenderingContext) { +function traceGLCalls(ctx: WebGLRenderingContext, idx: number) { const handler = { // tslint:disable-next-line:no-any get(target: any, prop: PropertyKey, receiver: any): any { @@ -116,7 +119,8 @@ function traceGLCalls(ctx: WebGLRenderingContext) { if (typeof (propValue) === 'function') { console.log( - ' gl.' + prop.toString() + ' = ' + target.constructor.name); + ' gl.' + prop.toString() + ' = ' + target.constructor.name + + ' ' + idx); // tslint:disable-next-line:only-arrow-functions return function() { return propValue.apply(target, arguments); diff --git a/src/backends/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts index ae8035eb25..b922c647d6 100644 --- a/src/backends/webgl/webgl_util.ts +++ b/src/backends/webgl/webgl_util.ts @@ -17,6 +17,7 @@ import {ENV} from '../../environment'; import * as util from '../../util'; + import {getContextByVersion} from './webgl_context_manager'; export function callAndCheck( @@ -219,8 +220,9 @@ export function validateTextureSize(width: number, height: number) { export function createFramebuffer( gl: WebGLRenderingContext, debug: boolean): WebGLFramebuffer { + console.log(' --- is debug: ' + debug); return throwIfNull( - gl, debug, () => gl.createFramebuffer(), + gl, true, () => gl.createFramebuffer(), 'Unable to create WebGLFramebuffer.'); } @@ -538,6 +540,12 @@ export function getWebGLDisjointQueryTimerVersion(webGLVersion: number): function hasExtension(gl: WebGLRenderingContext, extensionName: string) { const ext = gl.getExtension(extensionName); + try { + checkWebGLError(gl); + } catch (e) { + console.log('exception getting: ' + extensionName); + throw e; + } return ext != null; } @@ -602,34 +610,35 @@ export function isDownloadFloatTextureEnabled(webGLVersion: number): boolean { function createFloatTextureAndBindToFramebuffer( gl: WebGLRenderingContext, webGLVersion: number): boolean { - const frameBuffer = gl.createFramebuffer(); - const texture = gl.createTexture(); + const debug = ENV.getBool('DEBUG'); + const frameBuffer = callAndCheck(gl, debug, () => gl.createFramebuffer()); + const texture = callAndCheck(gl, debug, () => gl.createTexture()); - gl.bindTexture(gl.TEXTURE_2D, texture); - - // TODO - fix this - // webgl_util.callAndCheck( - // gl, debug, - // () => gl.texImage2D( - // tex2d, 0, internalFormat, width, height, 0, textureFormat, - // textureType, null)); + callAndCheck(gl, debug, () => gl.bindTexture(gl.TEXTURE_2D, texture)); // tslint:disable-next-line:no-any const internalFormat = webGLVersion === 2 ? (gl as any).RGBA32F : gl.RGBA; - gl.texImage2D( - gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + callAndCheck( + gl, debug, + () => gl.texImage2D( + gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null)); - gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); - gl.framebufferTexture2D( - gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + callAndCheck( + gl, debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer)); + callAndCheck( + gl, debug, + () => gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)); - const isFrameBufferComplete = - gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE; + const isFrameBufferComplete = callAndCheck( + gl, debug, + () => gl.checkFramebufferStatus(gl.FRAMEBUFFER) === + gl.FRAMEBUFFER_COMPLETE); - gl.bindTexture(gl.TEXTURE_2D, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - gl.deleteTexture(texture); - gl.deleteFramebuffer(frameBuffer); + callAndCheck(gl, debug, () => gl.bindTexture(gl.TEXTURE_2D, null)); + callAndCheck(gl, debug, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); + callAndCheck(gl, debug, () => gl.deleteTexture(texture)); + callAndCheck(gl, debug, () => gl.deleteFramebuffer(frameBuffer)); return isFrameBufferComplete; } diff --git a/src/debug_mode_test.ts b/src/debug_mode_test.ts index 8da3b80790..41a931d861 100644 --- a/src/debug_mode_test.ts +++ b/src/debug_mode_test.ts @@ -22,10 +22,12 @@ import {expectArraysClose} from './test_util'; describeWithFlags('debug on', SYNC_BACKEND_ENVS, () => { beforeAll(() => { + console.log('--- setting debug to TRUE'); tf.ENV.set('DEBUG', true); }); afterAll(() => { + console.log('--- setting debug to FALSE'); tf.ENV.set('DEBUG', false); });