From 6ba603c1fc71086dd77bea48528d5d8c0d121226 Mon Sep 17 00:00:00 2001 From: Ib Green Date: Thu, 1 Dec 2022 16:36:04 -0500 Subject: [PATCH] feat: Start replacing `gl` with `device` --- .../src/effects/lighting/lighting-effect.ts | 4 +- modules/core/src/lib/deck.ts | 37 +++++++++++------ modules/core/src/lib/layer-manager.ts | 41 ++++++++----------- .../core/src/lib/resource/resource-manager.ts | 13 ++++-- modules/core/src/passes/screen-pass.ts | 4 +- modules/test-utils/src/utils/setup-gl.js | 4 +- .../core/effects/lighting-effect.spec.ts | 6 +-- test/modules/core/{index.js => index.ts} | 0 test/modules/core/lib/{index.js => index.ts} | 0 ...-manager.spec.js => layer-manager.spec.ts} | 0 .../lib/resource/resource-manager.spec.js | 12 +++--- 11 files changed, 65 insertions(+), 56 deletions(-) rename test/modules/core/{index.js => index.ts} (100%) rename test/modules/core/lib/{index.js => index.ts} (100%) rename test/modules/core/lib/{layer-manager.spec.js => layer-manager.spec.ts} (100%) diff --git a/modules/core/src/effects/lighting/lighting-effect.ts b/modules/core/src/effects/lighting/lighting-effect.ts index 14ce5b2f652..70978b6db0b 100644 --- a/modules/core/src/effects/lighting/lighting-effect.ts +++ b/modules/core/src/effects/lighting/lighting-effect.ts @@ -37,7 +37,7 @@ export default class LightingEffect implements Effect { private pointLights: PointLight[] = []; private shadowPasses: ShadowPass[] = []; private shadowMaps: Texture2D[] = []; - private dummyShadowMap?: Texture2D; + private dummyShadowMap: Texture2D | null = null; private pipelineFactory?: ProgramManager; private shadowMatrices?: Matrix4[]; @@ -158,7 +158,7 @@ export default class LightingEffect implements Effect { if (this.shadow && this.pipelineFactory) { this.pipelineFactory.removeDefaultModule(shadow); - this.pipelineFactory = undefined; + this.pipelineFactory = null!; } } diff --git a/modules/core/src/lib/deck.ts b/modules/core/src/lib/deck.ts index 0d5a31228b7..f823a6e6737 100644 --- a/modules/core/src/lib/deck.ts +++ b/modules/core/src/lib/deck.ts @@ -31,6 +31,8 @@ import typedArrayManager from '../utils/typed-array-manager'; import deckGlobal from './init'; import {getBrowser} from '@probe.gl/env'; +import {Device} from '@luma.gl/api'; +import {WebGLDevice} from '@luma.gl/webgl'; import GL from '@luma.gl/constants'; import { AnimationLoop, @@ -179,7 +181,9 @@ export type DeckProps = { /** (Experimental) Fine-tune attribute memory usage. See documentation for details. */ _typedArrayManagerProps?: TypedArrayManagerOptions; - /** Called once the WebGL context has been initiated. */ + /** Called once the GPU Device has been initiated. */ + onDeviceInitialized?: (device: Device) => void; + /** @deprecated Called once the WebGL context has been initiated. */ onWebGLInitialized?: (gl: WebGLRenderingContext) => void; /** Called when the canvas resizes. */ onResize?: (dimensions: {width: number; height: number}) => void; @@ -188,9 +192,9 @@ export type DeckProps = { /** Called when the user has interacted with the deck.gl canvas, e.g. using mouse, touch or keyboard. */ onInteractionStateChange?: (state: InteractionState) => void; /** Called just before the canvas rerenders. */ - onBeforeRender?: (context: {gl: WebGLRenderingContext}) => void; + onBeforeRender?: (context: {device: Device; gl: WebGLRenderingContext}) => void; /** Called right after the canvas rerenders. */ - onAfterRender?: (context: {gl: WebGLRenderingContext}) => void; + onAfterRender?: (context: {device: Device; gl: WebGLRenderingContext}) => void; /** Called once after gl context and all Deck components are created. */ onLoad?: () => void; /** Called if deck.gl encounters an error. @@ -252,6 +256,7 @@ const defaultProps = { _typedArrayManagerProps: {}, _customRender: null, + onDeviceInitialized: noop, onWebGLInitialized: noop, onResize: noop, onViewStateChange: noop, @@ -763,6 +768,7 @@ export default class Deck { onContextLost: () => this._onContextLost() }), onInitialize: context => this._setGLContext(context.gl), + onRender: this._onRenderFrame.bind(this), // onBeforeRender, // onAfterRender, @@ -875,7 +881,10 @@ export default class Deck { } } + /** @deprecated */ private _setGLContext(gl: WebGLRenderingContext) { + const device = WebGLDevice.attach(gl); + if (this.layerManager) { return; } @@ -897,6 +906,7 @@ export default class Deck { depthFunc: GL.LEQUAL }); + this.props.onDeviceInitialized(device); this.props.onWebGLInitialized(gl); // timeline for transitions @@ -933,7 +943,8 @@ export default class Deck { const viewport = this.viewManager.getViewports()[0]; // Note: avoid React setState due GL animation loop / setState timing issue - this.layerManager = new LayerManager(gl, { + this.layerManager = new LayerManager({ + device, deck: this, stats: this.stats, viewport, @@ -967,25 +978,25 @@ export default class Deck { clearCanvas?: boolean; } ) { - const {gl} = this.layerManager.context; + const {device, gl} = this.layerManager?.context; setParameters(gl, this.props.parameters); - this.props.onBeforeRender({gl}); + this.props.onBeforeRender({device, gl}); - this.deckRenderer.renderLayers({ + this.deckRenderer?.renderLayers({ target: this.props._framebuffer, - layers: this.layerManager.getLayers(), - viewports: this.viewManager.getViewports(), - onViewportActive: this.layerManager.activateViewport, - views: this.viewManager.getViews(), + layers: this.layerManager?.getLayers(), + viewports: this.viewManager?.getViewports(), + onViewportActive: this.layerManager?.activateViewport, + views: this.viewManager?.getViews(), pass: 'screen', redrawReason, - effects: this.effectManager.getEffects(), + effects: this.effectManager?.getEffects(), ...renderOptions }); - this.props.onAfterRender({gl}); + this.props.onAfterRender({device, gl}); } // Callbacks diff --git a/modules/core/src/lib/layer-manager.ts b/modules/core/src/lib/layer-manager.ts index 2a546fa25b6..fea41abe50d 100644 --- a/modules/core/src/lib/layer-manager.ts +++ b/modules/core/src/lib/layer-manager.ts @@ -20,7 +20,7 @@ import {Device} from '@luma.gl/api'; import {Timeline} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; +import type {ProgramManager} from '@luma.gl/core'; import {LIFECYCLE} from '../lifecycle/constants'; import log from '../utils/log'; import debug from '../debug'; @@ -34,7 +34,6 @@ import {createProgramManager} from '../shaderlib'; import type Layer from './layer'; import type CompositeLayer from './composite-layer'; import type Deck from './deck'; -import type {ProgramManager} from '@luma.gl/core'; const TRACE_SET_LAYERS = 'layerManager.setLayers'; const TRACE_ACTIVATE_VIEWPORT = 'layerManager.activateViewport'; @@ -44,7 +43,6 @@ export type LayerContext = { resourceManager: ResourceManager; deck?: Deck; device: Device; - gl: WebGLRenderingContext; pipelineFactory: ProgramManager; stats: Stats; viewport: Viewport; @@ -52,10 +50,20 @@ export type LayerContext = { mousePosition: {x: number; y: number} | null; userData: any; onError?: (error: Error, source: Layer) => void; + /** @deprecated Use context.device */ + gl: WebGLRenderingContext; }; export type LayersList = (Layer | undefined | false | null | LayersList)[]; +export type LayerManagerProps = { + // Apparently LayerManager is supposed to be instantiatable without gl context? + device: Device | null; + deck?: Deck; + stats?: Stats; + viewport?: Viewport; + timeline?: Timeline; +}; export default class LayerManager { layers: Layer[]; context: LayerContext; @@ -68,20 +76,7 @@ export default class LayerManager { private _debug: boolean = false; // eslint-disable-next-line - constructor( - gl, - { - deck, - stats, - viewport, - timeline - }: { - deck?: Deck; - stats?: Stats; - viewport?: Viewport; - timeline?: Timeline; - } = {} - ) { + constructor({device, deck, stats, viewport, timeline}: LayerManagerProps) { // Currently deck.gl expects the DeckGL.layers array to be different // whenever React rerenders. If the same layers array is used, the // LayerManager's diffing algorithm will generate a fatal error and @@ -92,20 +87,18 @@ export default class LayerManager { // If it's the same across two React render calls, the diffing logic // will be skipped. this.layers = []; - this.resourceManager = new ResourceManager({gl, protocol: 'deck://'}); - - // Apparently LayerManager is supposed to be instantiatable without gl context? - const device = gl && WebGLDevice.attach(gl)!; + this.resourceManager = new ResourceManager({device, protocol: 'deck://'}); this.context = { mousePosition: null, userData: {}, layerManager: this, - device, - gl, + device: device!, + // @ts-expect-error + gl: device && device.gl, deck, // Enabling luma.gl Program caching using private API (_cachePrograms) - pipelineFactory: gl && createProgramManager(device), + pipelineFactory: (device && createProgramManager(device))!, stats: stats || new Stats({id: 'deck.gl'}), // Make sure context.viewport is not empty on the first layer initialization viewport: viewport || new Viewport({id: 'DEFAULT-INITIAL-VIEWPORT'}), // Current viewport, exposed to layers for project* function diff --git a/modules/core/src/lib/resource/resource-manager.ts b/modules/core/src/lib/resource/resource-manager.ts index ec1058817ac..4d74b361960 100644 --- a/modules/core/src/lib/resource/resource-manager.ts +++ b/modules/core/src/lib/resource/resource-manager.ts @@ -1,10 +1,13 @@ /* global setTimeout */ +import {Device} from '@luma.gl/api'; import Resource from './resource'; import type {ResourceSubscriber} from './resource'; export type ResourceManagerContext = { - gl: WebGLRenderingContext; + device: Device; resourceManager: ResourceManager; + /** @deprecated */ + gl: WebGLRenderingContext; }; type Consumer = Record; @@ -17,11 +20,13 @@ export default class ResourceManager { private _consumers: Record; private _pruneRequest: number | null; - constructor({gl, protocol}) { - this.protocol = protocol || 'resource://'; + constructor(props: {device: Device; protocol?: string}) { + this.protocol = props.protocol || 'resource://'; this._context = { - gl, + device: props.device, + // @ts-expect-error + gl: props.device.gl, resourceManager: this }; this._resources = {}; diff --git a/modules/core/src/passes/screen-pass.ts b/modules/core/src/passes/screen-pass.ts index e353686c6d0..34c247cfdbc 100644 --- a/modules/core/src/passes/screen-pass.ts +++ b/modules/core/src/passes/screen-pass.ts @@ -42,8 +42,8 @@ export default class ScreenPass extends Pass { } delete() { - this.model.delete(); - this.model = null; + this.model.destroy(); + this.model = null!; } // Private methods diff --git a/modules/test-utils/src/utils/setup-gl.js b/modules/test-utils/src/utils/setup-gl.js index 2dd8aa13dc4..d6b3d300afe 100644 --- a/modules/test-utils/src/utils/setup-gl.js +++ b/modules/test-utils/src/utils/setup-gl.js @@ -1,4 +1,4 @@ -import {createTestContext} from '@luma.gl/test-utils'; +import {createTestContext, webgl1TestDevice} from '@luma.gl/test-utils'; globalThis.glContext = globalThis.glContext || @@ -15,4 +15,4 @@ globalThis.glContext = export default globalThis.glContext; -export const device = globalThis.glContext.device; +export const device = webgl1TestDevice; diff --git a/test/modules/core/effects/lighting-effect.spec.ts b/test/modules/core/effects/lighting-effect.spec.ts index 07ae864b2c2..83e0c57a809 100644 --- a/test/modules/core/effects/lighting-effect.spec.ts +++ b/test/modules/core/effects/lighting-effect.spec.ts @@ -115,7 +115,7 @@ test('LightingEffect#shadow module', t => { }); const lightingEffect = new LightingEffect({dirLight}); - const pipelineManager = ProgramManager.getDefaultProgramManager(device); + const pipelineFactory = ProgramManager.getDefaultProgramManager(device); lightingEffect.preRender(gl, { layers: [], viewports: [testViewport], @@ -124,13 +124,13 @@ test('LightingEffect#shadow module', t => { pixelRatio: 1 }); // @ts-expect-error private - let defaultModules = pipelineManager._defaultModules; + let defaultModules = pipelineFactory._defaultModules; let hasShadow = defaultModules.some(m => m.name === 'shadow'); t.equal(hasShadow, true, 'LightingEffect adds shadow module to default correctly'); lightingEffect.cleanup(); // @ts-expect-error private - defaultModules = pipelineManager._defaultModules; + defaultModules = pipelineFactory._defaultModules; hasShadow = defaultModules.some(m => m.name === 'shadow'); t.equal(hasShadow, false, 'LightingEffect removes shadow module to default correctly'); t.end(); diff --git a/test/modules/core/index.js b/test/modules/core/index.ts similarity index 100% rename from test/modules/core/index.js rename to test/modules/core/index.ts diff --git a/test/modules/core/lib/index.js b/test/modules/core/lib/index.ts similarity index 100% rename from test/modules/core/lib/index.js rename to test/modules/core/lib/index.ts diff --git a/test/modules/core/lib/layer-manager.spec.js b/test/modules/core/lib/layer-manager.spec.ts similarity index 100% rename from test/modules/core/lib/layer-manager.spec.js rename to test/modules/core/lib/layer-manager.spec.ts diff --git a/test/modules/core/lib/resource/resource-manager.spec.js b/test/modules/core/lib/resource/resource-manager.spec.js index f51592493df..d064697535a 100644 --- a/test/modules/core/lib/resource/resource-manager.spec.js +++ b/test/modules/core/lib/resource/resource-manager.spec.js @@ -1,14 +1,14 @@ -/* global setTimeout */ +/* deviceobal setTimeout */ import test from 'tape-promise/tape'; -import {gl} from '@deck.gl/test-utils'; +import {device} from '@deck/test-utils'; import ResourceManager from '@deck.gl/core/lib/resource/resource-manager'; test('ResourceManager#protocol', t => { - let dataManager = new ResourceManager({gl, onError: t.notOk}); + let dataManager = new ResourceManager({device, onError: t.notOk}); t.ok(dataManager.contains('resource://data-01'), 'checks protocol'); t.notOk(dataManager.contains('deck://data-01'), 'checks protocol'); - dataManager = new ResourceManager({gl, protocol: 'deck://', onError: t.notOk}); + dataManager = new ResourceManager({device, protocol: 'deck://', onError: t.notOk}); t.notOk(dataManager.contains('resource://data-01'), 'checks protocol'); t.ok(dataManager.contains('deck://data-01'), 'checks protocol'); @@ -16,7 +16,7 @@ test('ResourceManager#protocol', t => { }); test('ResourceManager#add,remove', t => { - const dataManager = new ResourceManager({gl, onError: t.notOk}); + const dataManager = new ResourceManager({device, onError: t.notOk}); t.notOk(dataManager.contains('data-01'), 'does not contain resource'); @@ -41,7 +41,7 @@ test('ResourceManager#add,remove', t => { // eslint-disable-next-line test('ResourceManager#subscribe, unsubscribe', t => { - const dataManager = new ResourceManager({gl, onError: t.notOk}); + const dataManager = new ResourceManager({device, onError: t.notOk}); let propA1Changed = 0; let propA2Changed = 0; let propBChanged = 0;