From 8310f9a88b39f01b6b1dd0a8041febd2165624c5 Mon Sep 17 00:00:00 2001 From: Ib Green Date: Sun, 28 Apr 2024 09:54:49 -0400 Subject: [PATCH] feat(core): Break out Adapter class from Device --- docs/api-guide/gpu/gpu-initialization.md | 36 ++---- docs/api-reference/core/adapter.md | 6 + docs/api-reference/core/luma.md | 96 +++++++-------- examples/api/animation/index.html | 4 +- examples/api/cubemap/index.html | 4 +- examples/api/texture-3d/index.html | 4 +- .../webgl/transform-feedback/index.html | 4 +- examples/showcase/instancing/index.html | 6 +- examples/showcase/persistence/index.html | 4 +- examples/tutorials/hello-cube/index.html | 6 +- examples/tutorials/hello-gltf/index.html | 6 +- .../hello-instanced-cubes/index.html | 6 +- .../tutorials/hello-instancing/index.html | 4 +- .../hello-triangle-geometry/index.html | 6 +- examples/tutorials/hello-triangle/index.html | 6 +- examples/tutorials/hello-two-cubes/index.html | 6 +- examples/tutorials/lighting/index.html | 4 +- examples/tutorials/shader-hooks/index.html | 4 +- examples/tutorials/shader-modules/index.html | 4 +- examples/webgpu/textured-cube/index.html | 6 +- modules/core/src/adapter/adapter.ts | 15 +++ modules/core/src/adapter/device.ts | 11 -- modules/core/src/adapter/luma.ts | 76 +++++++----- modules/core/src/index.ts | 2 + modules/core/test/adapter/luma.spec.ts | 10 +- modules/test-utils/src/create-test-device.ts | 12 +- modules/test-utils/src/index.ts | 3 + .../src/null-device/null-adapter.ts | 38 ++++++ .../test-utils/src/null-device/null-device.ts | 17 +-- modules/test-utils/src/register-devices.ts | 6 +- modules/webgl/src/adapter/webgl-adapter.ts | 115 ++++++++++++++++++ modules/webgl/src/adapter/webgl-device.ts | 107 +--------------- modules/webgl/src/index.ts | 4 + .../webgl/test/adapter/webgl-device.spec.ts | 4 +- modules/webgpu/src/adapter/webgpu-adapter.ts | 91 ++++++++++++++ modules/webgpu/src/adapter/webgpu-device.ts | 73 +---------- modules/webgpu/src/index.ts | 4 +- website/src/react-luma/store/device-store.tsx | 6 +- 38 files changed, 442 insertions(+), 374 deletions(-) create mode 100644 docs/api-reference/core/adapter.md create mode 100644 modules/core/src/adapter/adapter.ts create mode 100644 modules/test-utils/src/null-device/null-adapter.ts create mode 100644 modules/webgl/src/adapter/webgl-adapter.ts create mode 100644 modules/webgpu/src/adapter/webgpu-adapter.ts diff --git a/docs/api-guide/gpu/gpu-initialization.md b/docs/api-guide/gpu/gpu-initialization.md index 0407228c94..9c5e17889e 100644 --- a/docs/api-guide/gpu/gpu-initialization.md +++ b/docs/api-guide/gpu/gpu-initialization.md @@ -1,5 +1,9 @@ # GPU Initialization +## Adapter + +An `Adapter` is a factory for `Device` instances for a specific backend (e.g. WebGPU or WebGL). + ## Device The [`Device`](/docs/api-reference/core/device) class provides luma.gl applications with access to the GPU. @@ -22,31 +26,7 @@ A `CanvasContext` takes care of: - canvas resizing - device pixel ratio calculations -## Usage - -Create a `Device` using the best available registered backend, -specifying an existing canvas to initialize the default `CanvasContext`. - -```typescript -import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -import {WebGPUDevice} from '@luma.gl/webgpu'; - -luma.registerDevices([WebGLDevice]); -const device = luma.createDevice({canvas: ...}); -``` - -Create a WebGL 2 device, auto creating a canvas. - -```typescript -import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; - -luma.registerDevices([WebGLDevice]); -const webglDevice = luma.createDevice({type: 'webgl'}); -``` - -## Registering Device Backends +## Registering Backend Adapters The `@luma.gl/core` module defines abstract API interfaces such as `Device`, `Buffer` etc and is not usable on its own. @@ -62,9 +42,9 @@ yarn add @luma.gl/webgpu ```typescript import {luma} from '@luma.gl/core'; -import {WebGPUDevice} from '@luma.gl/webgpu'; +import {webgpuAdapter} from '@luma.gl/webgpu'; -luma.registerDevices([WebGPUDevice]); +luma.registerAdapters([webgpuAdapter]); const device = await luma.createDevice({type: 'webgpu', canvas: ...}); ``` @@ -83,7 +63,7 @@ import {luma} from '@luma.gl/core'; import {WebGLDevice} from '@luma.gl/webgl'; import {WebGPUDevice} from '@luma.gl/webgpu'; -luma.registerDevices([WebGLDevice, WebGPUDevice]); +luma.registerAdapters([WebGLDevice, WebGPUDevice]); const webgpuDevice = luma.createDevice({type: 'best-available', canvas: ...}); ``` diff --git a/docs/api-reference/core/adapter.md b/docs/api-reference/core/adapter.md new file mode 100644 index 0000000000..95ce058ab9 --- /dev/null +++ b/docs/api-reference/core/adapter.md @@ -0,0 +1,6 @@ +# Adapter + +An `Adapter` is a factory for `Device` instances for a specific backend (e.g. WebGPU or WebGL). + +Adapters are normally not used directly, they are imported and passed to +methods like [`luma.createDevice`]. \ No newline at end of file diff --git a/docs/api-reference/core/luma.md b/docs/api-reference/core/luma.md index 760bc53e92..08d55c2f71 100644 --- a/docs/api-reference/core/luma.md +++ b/docs/api-reference/core/luma.md @@ -1,11 +1,10 @@ # luma - The [`luma`](/docs/api-reference/core/luma) namespace provides luma.gl applications -with the ability to register GPU Device backends and create `Device` class instances -using the registered backends. +with the ability to create `Device` class instances against GPU `Adapters` that bring +support for different GPU backends such as WebGPU and WebGL. -The returned [`Device`](/docs/api-reference/core/device) instances provides +The returned [`Device`](/docs/api-reference/core/device) instances provide luma.gl applications with a complete GPU API. ## Device Registration @@ -17,47 +16,48 @@ GPU API backend module (`@luma.gl/webgl` and/or `@luma.gl/webgpu`) and then regi ## Usage -To register a device backend, import the corresponding device backend module and then call `luma.registerDevices()` +Create a WebGL 2 context (throws if WebGL2 not supported) ```typescript import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -luma.registerDevices([WebGLDevice]); -``` +import {webgl2Adapter} from '@luma.gl/webgl'; -It is possible to register more than one device to create an application -that can work in both WebGL and WebGPU environments. +const webgpuDevice = luma.createDevice({type: 'webgl', adapters: [webgl2Adapter], canvas: ...}); +``` ```typescript -import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -import {WebGPUDevice} from '@luma.gl/webgl'; -luma.registerDevices([WebGLDevice, WebGPUDevice]); +const webgpuDevice = luma.createDevice({ + type: 'best-available', + canvas: ..., + adapters: [webgl2Adapter, WebGPUDevice] +}); ``` +To pre-register a device backend, import the corresponding device backend module and then call `luma.registerAdapters()` + Register the WebGL backend, then create a WebGL2 context, auto creating a canvas ```typescript import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; - -luma.registerDevices([WebGLDevice]); +import {webgl2Adapter} from '@luma.gl/webgl'; +luma.registerAdapters([webgl2Adapter]); const webglDevice = luma.createDevice({type: 'webgl', canvas: ...}); ``` -Create a WebGL 2 context (throws if WebGL2 not supported) +It is possible to register more than one device to create an application +that can work in both WebGL and WebGPU environments. ```typescript import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; - -luma.registerDevices([WebGLDevice]); -const webgpuDevice = luma.createDevice({type: 'webgl', canvas: ...}); +import {webgl2Adapter} from '@luma.gl/webgl'; +import {webgpuDevice} from '@luma.gl/webgl'; +luma.registerAdapters([webgl2Adapter, webgpuDevice]); +const device = luma.createDevice({type: 'best-available', canvas: ...}); ``` -## Registering Device Backends +## Registering Adapters -Install device modules +Install device modules and register adapters to access backends ```sh yarn add @luma.gl/core @@ -69,9 +69,9 @@ To create a WebGPU device: ```typescript import {luma} from '@luma.gl/core'; -import {WebGPUDevice} from '@luma.gl/webgpu'; +import {webgpuAdapter} from '@luma.gl/webgpu'; -luma.registerDevices([WebGPUDevice]); +luma.registerAdapters([webgpuAdapter]); const device = await luma.createDevice({type: 'webgpu', canvas: ...}); ``` @@ -79,23 +79,13 @@ Pre-register devices ```typescript import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -import {WebGPUDevice} from '@luma.gl/webgpu'; +import {webgl2Adapter} from '@luma.gl/webgl'; +import {webgpuAdapter} from '@luma.gl/webgpu'; -luma.registerDevices([WebGLDevice, WebGPUDevice]); +luma.registerAdapters([webgl2Adapter, webgpuAdapter]); const webgpuDevice = luma.createDevice({type: 'best-available', canvas: ...}); ``` -Provide devices to createDevice - -```typescript -const webgpuDevice = luma.createDevice({ - type: 'best-available', - canvas: ..., - devices: [WebGLDevice, WebGPUDevice] -}); -``` - ## Types ### `CreateDeviceProps` @@ -107,7 +97,7 @@ type CreateDeviceProps = DeviceProps & { /** Selects the type of device. `best-available` uses webgpu if available, then webgl. */ type?: 'webgl' | 'webgpu' | 'unknown' | 'best-available'; /** List of device types. Will also search any pre-registered device backends */ - devices?: DeviceFactory[]; + adapters?: Adapter[]; } ``` @@ -120,7 +110,7 @@ export type AttachDeviceProps = DeviceProps & { /** Externally created WebGL context or WebGPU device */ handle: WebGL2RenderingContext | GPUDevice | null; /** List of device types. Will also search any pre-registered device backends */ - devices?: DeviceFactory[]; + adapters?: Adapter[]; }; ``` @@ -129,16 +119,16 @@ export type AttachDeviceProps = DeviceProps & { ### `luma.createDevice()` ```typescript -luma.createDevice({type, devices, ...deviceProps}: CreateDeviceProps); +luma.createDevice({type, adapters, ...deviceProps}: CreateDeviceProps); ``` To create a Device instance, the application calls `luma.createDevice()`. - `type`: `'webgl' \| 'webgpu' \| 'best-available'` -- `devices`: list of `Device` backend classes. Can be omitted if `luma.registerDevices()` has been called. +- `adapters`: list of `Device` backend classes. Can be omitted if `luma.registerAdapters()` has been called. Unless a device `type` is specified a `Device` will be created using the `'best-available'` adapter. -luma.gl favors WebGPU over WebGL devices, whenever WebGPU is available. +luma.gl favors WebGPU over WebGL adapters, whenever WebGPU is available. Note: A device type is available if: 1. The backend module has been registered @@ -147,29 +137,29 @@ Note: A device type is available if: ### `luma.attachDevice()` ```ts -luma.attachDevice({handle: WebGL2RenderingContext | GPUDevice, devices, ...}: AttachDeviceProps); +luma.attachDevice({handle: WebGL2RenderingContext | GPUDevice, adapters, ...}: AttachDeviceProps); ``` A luma.gl Device can be attached to an externally created `WebGL2RenderingContext` or `GPUDevice`. This allows applications to use the luma.gl API to "interleave" rendering with other GPU libraries. - `handle` - The externally created `WebGL2RenderingContext` or `GPUDevice` that should be attached to a luma `Device`. -- `devices` - list of `Device` backend classes. Can be omitted if `luma.registerDevices()` has been called. +- `adapters` - list of `Device` backend classes. Can be omitted if `luma.registerAdapters()` has been called. Note that while you cannot directly attach a luma.gl `Device` to a WebGL 1 `WebGLRenderingContext`, you may be able to work around it using `luma.enforceWebGL2()`. -### `luma.registerDevices()` +### `luma.registerAdapters()` ```typescript -luma.registerDevices(devices?: (typeof Device)[]): void; +luma.registerAdapters(adapters?: (typeof Device)[]): void; ``` -Registers one or more devices (device constructors) so that they can be used -to create `Device` instances against that GPU backend. The registered device types +Pre-registers one or more adapters so that they can be used +to create `Device` instances against those GPU backends. The registered adapters types will be available to `luma.createDevice()` and `luma.attachDevice()` calls. -`luma.registerDevices()` enables separation of the application code that -registers GPU backends from the application code that creates devices, +`luma.registerAdapters()` enables separation of the application code that +registers GPU backends from the application code that creates adapters, so that device types do not have to be provided at `Device` create or attach time. ### `luma.enforceWebGL2()` @@ -191,4 +181,4 @@ Since WebGL2 is a essentially a superset of WebGL1, a library written for WebGL ## Remarks -- At least one backend must be imported and registered with `luma.registerDevices()` for `luma.createDevice()` or `luma.attachDevice()` calls to succeed (unless `Device` implementations are supplied to those calls). +- At least one backend must be imported and registered with `luma.registerAdapters()` for `luma.createDevice()` or `luma.attachDevice()` calls to succeed (unless `Device` implementations are supplied to those calls). diff --git a/examples/api/animation/index.html b/examples/api/animation/index.html index 3c61153cb9..ceb97d576a 100644 --- a/examples/api/animation/index.html +++ b/examples/api/animation/index.html @@ -1,8 +1,8 @@ diff --git a/examples/api/texture-3d/index.html b/examples/api/texture-3d/index.html index dded8a8c3b..091cbd3bc0 100644 --- a/examples/api/texture-3d/index.html +++ b/examples/api/texture-3d/index.html @@ -1,12 +1,12 @@ diff --git a/examples/script/webgl/transform-feedback/index.html b/examples/script/webgl/transform-feedback/index.html index dded8a8c3b..091cbd3bc0 100644 --- a/examples/script/webgl/transform-feedback/index.html +++ b/examples/script/webgl/transform-feedback/index.html @@ -1,12 +1,12 @@ diff --git a/examples/showcase/instancing/index.html b/examples/showcase/instancing/index.html index 6818e4f060..8749d5fe5c 100644 --- a/examples/showcase/instancing/index.html +++ b/examples/showcase/instancing/index.html @@ -2,10 +2,10 @@ diff --git a/examples/showcase/persistence/index.html b/examples/showcase/persistence/index.html index 33897f3734..aaf6bf7e5b 100644 --- a/examples/showcase/persistence/index.html +++ b/examples/showcase/persistence/index.html @@ -1,10 +1,10 @@ diff --git a/examples/tutorials/hello-cube/index.html b/examples/tutorials/hello-cube/index.html index 7c7cc9bb3a..c1cd5e4d28 100644 --- a/examples/tutorials/hello-cube/index.html +++ b/examples/tutorials/hello-cube/index.html @@ -1,12 +1,12 @@ diff --git a/examples/tutorials/hello-gltf/index.html b/examples/tutorials/hello-gltf/index.html index 54cf4d56f0..89f63e5216 100644 --- a/examples/tutorials/hello-gltf/index.html +++ b/examples/tutorials/hello-gltf/index.html @@ -21,13 +21,13 @@ diff --git a/examples/tutorials/hello-instancing/index.html b/examples/tutorials/hello-instancing/index.html index dded8a8c3b..091cbd3bc0 100644 --- a/examples/tutorials/hello-instancing/index.html +++ b/examples/tutorials/hello-instancing/index.html @@ -1,12 +1,12 @@ diff --git a/examples/tutorials/hello-triangle-geometry/index.html b/examples/tutorials/hello-triangle-geometry/index.html index 6741cf2fc3..eb09bbba7b 100644 --- a/examples/tutorials/hello-triangle-geometry/index.html +++ b/examples/tutorials/hello-triangle-geometry/index.html @@ -1,13 +1,13 @@ diff --git a/examples/tutorials/hello-triangle/index.html b/examples/tutorials/hello-triangle/index.html index 91cb545e7f..3a875e7a0e 100644 --- a/examples/tutorials/hello-triangle/index.html +++ b/examples/tutorials/hello-triangle/index.html @@ -1,12 +1,12 @@ diff --git a/examples/tutorials/hello-two-cubes/index.html b/examples/tutorials/hello-two-cubes/index.html index 105bc89f69..d66f4189c8 100644 --- a/examples/tutorials/hello-two-cubes/index.html +++ b/examples/tutorials/hello-two-cubes/index.html @@ -1,12 +1,12 @@ diff --git a/examples/tutorials/lighting/index.html b/examples/tutorials/lighting/index.html index dded8a8c3b..091cbd3bc0 100644 --- a/examples/tutorials/lighting/index.html +++ b/examples/tutorials/lighting/index.html @@ -1,12 +1,12 @@ diff --git a/examples/tutorials/shader-hooks/index.html b/examples/tutorials/shader-hooks/index.html index dded8a8c3b..091cbd3bc0 100644 --- a/examples/tutorials/shader-hooks/index.html +++ b/examples/tutorials/shader-hooks/index.html @@ -1,12 +1,12 @@ diff --git a/examples/tutorials/shader-modules/index.html b/examples/tutorials/shader-modules/index.html index dded8a8c3b..091cbd3bc0 100644 --- a/examples/tutorials/shader-modules/index.html +++ b/examples/tutorials/shader-modules/index.html @@ -1,12 +1,12 @@ diff --git a/examples/webgpu/textured-cube/index.html b/examples/webgpu/textured-cube/index.html index 105bc89f69..d66f4189c8 100644 --- a/examples/webgpu/textured-cube/index.html +++ b/examples/webgpu/textured-cube/index.html @@ -1,12 +1,12 @@ diff --git a/modules/core/src/adapter/adapter.ts b/modules/core/src/adapter/adapter.ts new file mode 100644 index 0000000000..ac0cabe832 --- /dev/null +++ b/modules/core/src/adapter/adapter.ts @@ -0,0 +1,15 @@ +// luma.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import {Device, DeviceProps} from './device'; +/** + * Create and attach devices for a specific backend. Currently static methods on each device + */ +export abstract class Adapter { + // new (props: DeviceProps): Device; Constructor isn't used + abstract type: string; + abstract isSupported(): boolean; + abstract create(props: DeviceProps): Promise; + abstract attach?(handle: unknown): Device; +} diff --git a/modules/core/src/adapter/device.ts b/modules/core/src/adapter/device.ts index f54b764f95..5d5eeba902 100644 --- a/modules/core/src/adapter/device.ts +++ b/modules/core/src/adapter/device.ts @@ -241,17 +241,6 @@ export type DeviceProps = { _factoryDestroyPolicy?: 'unused' | 'never'; }; -/** - * Create and attach devices for a specific backend. Currently static methods on each device - */ -export interface DeviceFactory { - // new (props: DeviceProps): Device; Constructor isn't used - type: string; - isSupported(): boolean; - create(props: DeviceProps): Promise; - attach?(handle: unknown): Device; -} - /** * WebGPU Device/WebGL context abstraction */ diff --git a/modules/core/src/adapter/luma.ts b/modules/core/src/adapter/luma.ts index b48ce66a55..eaa53f2c29 100644 --- a/modules/core/src/adapter/luma.ts +++ b/modules/core/src/adapter/luma.ts @@ -4,7 +4,8 @@ import type {Log} from '@probe.gl/log'; import type {DeviceProps} from './device'; -import {Device, DeviceFactory} from './device'; +import {Device} from './device'; +import {Adapter} from './adapter'; import {StatsManager} from '../utils/stats-manager'; import {lumaStats} from '../utils/stats-manager'; import {log} from '../utils/log'; @@ -17,7 +18,7 @@ export type CreateDeviceProps = DeviceProps & { /** Selects the type of device. `best-available` uses webgpu if available, then webgl. */ type?: 'webgl' | 'webgpu' | 'unknown' | 'best-available'; /** List of device types. Will also search any pre-registered device types */ - devices?: DeviceFactory[]; + adapters?: Adapter[]; }; /** Properties for attaching an existing WebGL context or WebGPU device to a new luma Device */ @@ -25,7 +26,7 @@ export type AttachDeviceProps = DeviceProps & { /** Externally created WebGL context or WebGPU device */ handle: unknown; // WebGL2RenderingContext | GPUDevice | null; /** List of device types. Will also search any pre-registered device types */ - devices?: DeviceFactory[]; + adapters?: Adapter[]; }; /** @@ -37,7 +38,7 @@ export class Luma { static defaultProps: Required = { ...Device.defaultProps, type: 'best-available', - devices: undefined! + adapters: undefined! }; /** Global stats for all devices */ @@ -46,30 +47,32 @@ export class Luma { /** Global log */ readonly log: Log = log; - protected deviceMap = new Map(); + /** Map of pre-registered adapters */ + protected preregisteredAdapters = new Map(); - registerDevices(deviceClasses: DeviceFactory[]): void { - for (const deviceClass of deviceClasses) { - this.deviceMap.set(deviceClass.type, deviceClass); + /** Optionally pre-register adapters so that devices can be created and attached */ + registerAdapters(adapters: Adapter[]): void { + for (const adapter of adapters) { + this.preregisteredAdapters.set(adapter.type, adapter); } } /** Get type strings for supported Devices */ - getSupportedDeviceTypes(devices: DeviceFactory[] = []): string[] { - const deviceMap = this.getDeviceMap(devices); - return Array.from(deviceMap) - .map(([, Device]) => Device) - .filter(Device => Device.isSupported?.()) - .map(Device => Device.type); + getSupportedAdapters(adapters: Adapter[] = []): string[] { + const adapterMap = this.getAdapterMap(adapters); + return Array.from(adapterMap) + .map(([, adapter]) => adapter) + .filter(adapter => adapter.isSupported?.()) + .map(adapter => adapter.type); } /** Get type strings for best available Device */ - getBestAvailableDeviceType(devices: DeviceFactory[] = []): 'webgpu' | 'webgl' | null { - const deviceMap = this.getDeviceMap(devices); - if (deviceMap.get('webgpu')?.isSupported?.()) { + getBestAvailableAdapter(adapters: Adapter[] = []): 'webgpu' | 'webgl' | null { + const adapterMap = this.getAdapterMap(adapters); + if (adapterMap.get('webgpu')?.isSupported?.()) { return 'webgpu'; } - if (deviceMap.get('webgl')?.isSupported?.()) { + if (adapterMap.get('webgl')?.isSupported?.()) { return 'webgl'; } return null; @@ -88,16 +91,16 @@ export class Luma { // props.type = 'webgl'; // } - const deviceMap = this.getDeviceMap(props.devices); + const adapterMap = this.getAdapterMap(props.adapters); let type: string = props.type || ''; if (type === 'best-available') { - type = this.getBestAvailableDeviceType(props.devices) || type; + type = this.getBestAvailableAdapter(props.adapters) || type; } - const Device = deviceMap.get(type); - if (Device) { - return await Device.create(props); + const adapter = adapterMap.get(type); + if (adapter) { + return await adapter.create(props); } throw new Error(ERROR_MESSAGE); @@ -105,7 +108,7 @@ export class Luma { /** Attach to an existing GPU API handle (WebGL2RenderingContext or GPUDevice). */ async attachDevice(props: AttachDeviceProps): Promise { - const deviceMap = this.getDeviceMap(props.devices); + const adapterMap = this.getAdapterMap(props.adapters); let deviceType; @@ -128,8 +131,8 @@ export class Luma { } if (deviceType) { - const Device = deviceMap.get(deviceType); - const device = Device?.attach?.(props.handle); + const adapter = adapterMap.get(deviceType); + const device = adapter?.attach?.(props.handle); if (device) { return device; } @@ -168,13 +171,26 @@ export class Luma { } /** Convert a list of devices to a map */ - protected getDeviceMap(deviceClasses: DeviceFactory[] = []): Map { - const map = new Map(this.deviceMap); - for (const deviceClass of deviceClasses) { - map.set(deviceClass.type, deviceClass); + protected getAdapterMap(adapters: Adapter[] = []): Map { + const map = new Map(this.preregisteredAdapters); + for (const adapter of adapters) { + map.set(adapter.type, adapter); } return map; } + + // DEPRECATED + + /** @deprecated Use registerAdapters */ + registerDevices(deviceClasses: any[]): void { + log.warn('luma.registerDevices() is deprecated, use luma.registerAdapters() instead'); + for (const deviceClass of deviceClasses) { + const adapter = deviceClass.adapter as Adapter; + if (adapter) { + this.preregisteredAdapters.set(adapter.type, adapter); + } + } + } } /** Singleton */ diff --git a/modules/core/src/index.ts b/modules/core/src/index.ts index 94edd113af..7cb9a7b93f 100644 --- a/modules/core/src/index.ts +++ b/modules/core/src/index.ts @@ -8,6 +8,8 @@ export {VERSION} from './init'; export {luma} from './adapter/luma'; // ADAPTER (DEVICE AND GPU RESOURCE INTERFACES) +export {Adapter} from './adapter/adapter'; + export type {DeviceProps, DeviceInfo, DeviceFeature} from './adapter/device'; export {Device, DeviceFeatures, DeviceLimits} from './adapter/device'; diff --git a/modules/core/test/adapter/luma.spec.ts b/modules/core/test/adapter/luma.spec.ts index b8384a218e..85aa6e26c7 100644 --- a/modules/core/test/adapter/luma.spec.ts +++ b/modules/core/test/adapter/luma.spec.ts @@ -3,11 +3,11 @@ // Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; -import {NullDevice} from '@luma.gl/test-utils'; +import {nullAdapter, NullDevice} from '@luma.gl/test-utils'; import {luma} from '@luma.gl/core'; test('luma#attachDevice', async t => { - const device = await luma.attachDevice({handle: null, devices: [NullDevice]}); + const device = await luma.attachDevice({handle: null, adapters: [nullAdapter]}); t.equal(device.type, 'unknown', 'info.vendor ok'); t.equal(device.info.vendor, 'no one', 'info.vendor ok'); t.equal(device.info.renderer, 'none', 'info.renderer ok'); @@ -15,7 +15,7 @@ test('luma#attachDevice', async t => { }); test('luma#createDevice', async t => { - const device = await luma.createDevice({type: 'unknown', devices: [NullDevice]}); + const device = await luma.createDevice({type: 'unknown', adapters: [nullAdapter]}); t.equal(device.type, 'unknown', 'info.vendor ok'); t.equal(device.info.vendor, 'no one', 'info.vendor ok'); t.equal(device.info.renderer, 'none', 'info.renderer ok'); @@ -33,7 +33,7 @@ test('luma#registerDevices', async t => { test('luma#getSupportedDevices', async t => { luma.registerDevices([NullDevice]); - const types = luma.getSupportedDeviceTypes(); + const types = luma.getSupportedAdapters(); t.ok(types.includes('unknown'), 'null device is supported'); }); @@ -41,7 +41,7 @@ test('luma#getBestAvailableDeviceType', async t => { luma.registerDevices([NullDevice]); // Somewhat dummy test, as tests rely on test utils registering webgl and webgpu devices // But they might not be supported on all devices. - const types = luma.getBestAvailableDeviceType(); + const types = luma.getBestAvailableAdapter(); t.ok(typeof types === 'string', 'doesnt crash'); }); diff --git a/modules/test-utils/src/create-test-device.ts b/modules/test-utils/src/create-test-device.ts index e7baf3f0ad..b41af460dc 100644 --- a/modules/test-utils/src/create-test-device.ts +++ b/modules/test-utils/src/create-test-device.ts @@ -4,8 +4,8 @@ import type {Device, DeviceProps} from '@luma.gl/core'; import {luma, log} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -import {WebGPUDevice} from '@luma.gl/webgpu'; +import {webgl2Adapter, WebGLDevice} from '@luma.gl/webgl'; +import {webgpuAdapter, WebGPUDevice} from '@luma.gl/webgpu'; const CONTEXT_DEFAULTS: Partial = { width: 1, @@ -24,7 +24,7 @@ export function createTestDevice(props: DeviceProps = {}): WebGLDevice | null { try { props = {...CONTEXT_DEFAULTS, ...props, debug: true}; // TODO - We dont use luma.createDevice since this function currently expect the context to be created synchronously - return WebGLDevice.createSync(props); + return webgl2Adapter.createSync(props); } catch (error) { // eslint-disable-next-line no-console console.error(`Failed to created device '${props.id}': ${(error as Error).message}`); @@ -50,7 +50,8 @@ export async function getTestDevices(type?: 'webgl' | 'webgpu'): Promise { + // Wait for page to load: if canvas is a string we need to query the DOM for the canvas element. + // We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary. + if (typeof props.canvas === 'string') { + await CanvasContext.pageLoaded; + } + + return new NullDevice(props); + } +} + +export const nullAdapter = new NullAdapter(); diff --git a/modules/test-utils/src/null-device/null-device.ts b/modules/test-utils/src/null-device/null-device.ts index db2cd7f305..139acad560 100644 --- a/modules/test-utils/src/null-device/null-device.ts +++ b/modules/test-utils/src/null-device/null-device.ts @@ -9,7 +9,7 @@ import type { VertexArray, VertexArrayProps } from '@luma.gl/core'; -import {Device, CanvasContext, DeviceFeatures} from '@luma.gl/core'; +import {Device, DeviceFeatures} from '@luma.gl/core'; import type { BufferProps, @@ -50,7 +50,6 @@ export class NullDevice extends Device { static isSupported(): boolean { return true; } - static type: string = 'unknown'; readonly type = 'unknown'; features: DeviceFeatures = new DeviceFeatures([], this.props.disabledFeatures); limits: NullDeviceLimits = new NullDeviceLimits(); @@ -59,20 +58,6 @@ export class NullDevice extends Device { readonly canvasContext: NullCanvasContext; readonly lost: Promise<{reason: 'destroyed'; message: string}>; - static attach(handle: null): NullDevice { - return new NullDevice({}); - } - - static async create(props: DeviceProps = {}): Promise { - // Wait for page to load: if canvas is a string we need to query the DOM for the canvas element. - // We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary. - if (typeof props.canvas === 'string') { - await CanvasContext.pageLoaded; - } - - return new NullDevice(props); - } - constructor(props: DeviceProps) { super({...props, id: props.id || 'null-device'}); diff --git a/modules/test-utils/src/register-devices.ts b/modules/test-utils/src/register-devices.ts index 6e4492cb87..8d1526a47e 100644 --- a/modules/test-utils/src/register-devices.ts +++ b/modules/test-utils/src/register-devices.ts @@ -3,7 +3,7 @@ // Copyright (c) vis.gl contributors import {luma} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -import {WebGPUDevice} from '@luma.gl/webgpu'; +import {webgl2Adapter} from '@luma.gl/webgl'; +import {webgpuAdapter} from '@luma.gl/webgpu'; -luma.registerDevices([WebGLDevice, WebGPUDevice]); +luma.registerAdapters([webgl2Adapter, webgpuAdapter]); diff --git a/modules/webgl/src/adapter/webgl-adapter.ts b/modules/webgl/src/adapter/webgl-adapter.ts new file mode 100644 index 0000000000..fcf1dfdf3b --- /dev/null +++ b/modules/webgl/src/adapter/webgl-adapter.ts @@ -0,0 +1,115 @@ +// luma.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import {Adapter, Device, DeviceProps, CanvasContext, log} from '@luma.gl/core'; +import {WebGLDevice} from './webgl-device'; +import {loadSpectorJS} from '../context/debug/spector'; +import {loadWebGLDeveloperTools} from '../context/debug/webgl-developer-tools'; + +const LOG_LEVEL = 1; + +export class WebGLAdapter extends Adapter { + /** type of device's created by this adapter */ + readonly type: Device['type'] = 'webgl'; + + constructor() { + super(); + // @ts-ignore DEPRECATED For backwards compatibility luma.registerDevices + WebGLDevice.adapter = this; + } + + /** Check if WebGL 2 is available */ + isSupported(): boolean { + return typeof WebGL2RenderingContext !== 'undefined'; + } + + /** + * Get a device instance from a GL context + * Creates and instruments the device if not already created + * @param gl + * @returns + */ + attach(gl: Device | WebGL2RenderingContext): WebGLDevice { + if (gl instanceof WebGLDevice) { + return gl; + } + // @ts-expect-error + if (gl?.device instanceof Device) { + // @ts-expect-error + return gl.device as WebGLDevice; + } + if (!isWebGL(gl)) { + throw new Error('Invalid WebGL2RenderingContext'); + } + return new WebGLDevice({gl: gl as WebGL2RenderingContext}); + } + + async create(props: DeviceProps = {}): Promise { + log.groupCollapsed(LOG_LEVEL, 'WebGLDevice created')(); + + const promises: Promise[] = []; + + // Load webgl and spector debug scripts from CDN if requested + if (props.debug) { + promises.push(loadWebGLDeveloperTools()); + } + + if (props.spector) { + promises.push(loadSpectorJS()); + } + + // Wait for page to load: if canvas is a string we need to query the DOM for the canvas element. + // We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary. + if (typeof props.canvas === 'string') { + promises.push(CanvasContext.pageLoaded); + } + + // Wait for all the loads to settle before creating the context. + // The Device.create() functions are async, so in contrast to the constructor, we can `await` here. + const results = await Promise.allSettled(promises); + for (const result of results) { + if (result.status === 'rejected') { + log.error(`Failed to initialize debug libraries ${result.reason}`)(); + } + } + + log.probe(LOG_LEVEL + 1, 'DOM is loaded')(); + + const device = this.createSync(props); + log.groupEnd(LOG_LEVEL)(); + + return device; + } + + /** Create or attach device synchronously. Not supported for all devices. */ + createSync(props: DeviceProps = {}): WebGLDevice { + // @ts-expect-error + if (props.gl?.device) { + log.warn('reattaching existing device')(); + return this.attach(props.gl); + } + + const device = new WebGLDevice(props); + + // Log some debug info about the newly created context + const message = `\ +Created ${device.type}${device.debug ? ' debug' : ''} context: \ +${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContext.id}`; + log.probe(LOG_LEVEL, message)(); + log.table(LOG_LEVEL, device.info)(); + + return device; + } +} + +/** Check if supplied parameter is a WebGL2RenderingContext */ +function isWebGL(gl: any): boolean { + if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) { + return true; + } + // Look for debug contexts, headless gl etc + return Boolean(gl && Number.isFinite(gl._version)); +} + +export const webgl2Adapter = new WebGLAdapter(); diff --git a/modules/webgl/src/adapter/webgl-device.ts b/modules/webgl/src/adapter/webgl-device.ts index 1abeb7d0c9..0fbcca1f78 100644 --- a/modules/webgl/src/adapter/webgl-device.ts +++ b/modules/webgl/src/adapter/webgl-device.ts @@ -17,8 +17,8 @@ import {getDeviceInfo} from './device-helpers/webgl-device-info'; import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features'; import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits'; import {WebGLCanvasContext} from './webgl-canvas-context'; -import {loadSpectorJS, initializeSpectorJS} from '../context/debug/spector'; -import {loadWebGLDeveloperTools, makeDebugContext} from '../context/debug/webgl-developer-tools'; +import {initializeSpectorJS} from '../context/debug/spector'; +import {makeDebugContext} from '../context/debug/webgl-developer-tools'; import { isTextureFormatSupported, isTextureFormatRenderable, @@ -71,17 +71,12 @@ import {withGLParameters} from '../context/state-tracker/with-parameters'; import {clear} from '../classic/clear'; import {getWebGLExtension} from '../context/helpers/webgl-extensions'; -const LOG_LEVEL = 1; - /** WebGPU style Device API for a WebGL context */ export class WebGLDevice extends Device { // // Public `Device` API // - /** type of this device */ - static readonly type: string = 'webgl'; - /** type of this device */ readonly type = 'webgl'; @@ -97,98 +92,11 @@ export class WebGLDevice extends Device { private _resolveContextLost?: (value: {reason: 'destroyed'; message: string}) => void; - // - // Static methods, expected to be present by `luma.createDevice()` - // - - /** Check if WebGL 2 is available */ - static isSupported(): boolean { - return typeof WebGL2RenderingContext !== 'undefined'; - } - - /** - * Get a device instance from a GL context - * Creates and instruments the device if not already created - * @param gl - * @returns - */ - static attach(gl: Device | WebGL2RenderingContext): WebGLDevice { - if (gl instanceof WebGLDevice) { - return gl; - } - // @ts-expect-error - if (gl?.device instanceof Device) { - // @ts-expect-error - return gl.device as WebGLDevice; - } - if (!isWebGL(gl)) { - throw new Error('Invalid WebGL2RenderingContext'); - } - return new WebGLDevice({gl: gl as WebGL2RenderingContext}); - } - - static async create(props: DeviceProps = {}): Promise { - log.groupCollapsed(LOG_LEVEL, 'WebGLDevice created')(); - - const promises: Promise[] = []; - - // Load webgl and spector debug scripts from CDN if requested - if (props.debug) { - promises.push(loadWebGLDeveloperTools()); - } - - if (props.spector) { - promises.push(loadSpectorJS()); - } - - // Wait for page to load: if canvas is a string we need to query the DOM for the canvas element. - // We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary. - if (typeof props.canvas === 'string') { - promises.push(CanvasContext.pageLoaded); - } - - // Wait for all the loads to settle before creating the context. - // The Device.create() functions are async, so in contrast to the constructor, we can `await` here. - const results = await Promise.allSettled(promises); - for (const result of results) { - if (result.status === 'rejected') { - log.error(`Failed to initialize debug libraries ${result.reason}`)(); - } - } - - log.probe(LOG_LEVEL + 1, 'DOM is loaded')(); - - const device = WebGLDevice.createSync(props); - log.groupEnd(LOG_LEVEL)(); - - return device; - } - - /** Create or attach device synchronously. Not supported for all devices. */ - static createSync(props: DeviceProps = {}): WebGLDevice { - // @ts-expect-error - if (props.gl?.device) { - log.warn('reattaching existing device')(); - return WebGLDevice.attach(props.gl); - } - - const device = new WebGLDevice(props); - - // Log some debug info about the newly created context - const message = `\ -Created ${device.type}${device.debug ? ' debug' : ''} context: \ -${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContext.id}`; - log.probe(LOG_LEVEL, message)(); - log.table(LOG_LEVEL, device.info)(); - - return device; - } - // // Public API // - protected constructor(props: DeviceProps) { + constructor(props: DeviceProps) { super({...props, id: props.id || 'webgl-device'}); // If attaching to an already attached context, return the attached device @@ -547,15 +455,6 @@ ${device.info.vendor}, ${device.info.renderer} for canvas: ${device.canvasContex } } -/** Check if supplied parameter is a WebGL2RenderingContext */ -function isWebGL(gl: any): boolean { - if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) { - return true; - } - // Look for debug contexts, headless gl etc - return Boolean(gl && Number.isFinite(gl._version)); -} - /** Set constant float array attribute */ function setConstantFloatArray(device: WebGLDevice, location: number, array: Float32Array): void { switch (array.length) { diff --git a/modules/webgl/src/index.ts b/modules/webgl/src/index.ts index d9f452f02f..edda89efca 100644 --- a/modules/webgl/src/index.ts +++ b/modules/webgl/src/index.ts @@ -12,6 +12,10 @@ export type {WebGLDeviceLimits} from './adapter/device-helpers/webgl-device-limits'; // WebGL adapter classes +export {webgl2Adapter} from './adapter/webgl-adapter'; +export type {WebGLAdapter} from './adapter/webgl-adapter'; + +// WebGL Device classes export {WebGLDevice} from './adapter/webgl-device'; export {WebGLCanvasContext} from './adapter/webgl-canvas-context'; diff --git a/modules/webgl/test/adapter/webgl-device.spec.ts b/modules/webgl/test/adapter/webgl-device.spec.ts index 235a483ac1..f433c431e7 100644 --- a/modules/webgl/test/adapter/webgl-device.spec.ts +++ b/modules/webgl/test/adapter/webgl-device.spec.ts @@ -4,10 +4,10 @@ import test from 'tape-promise/tape'; import {webglDevice} from '@luma.gl/test-utils'; -import {WebGLDevice} from '@luma.gl/webgl'; +import {webgl2Adapter} from '@luma.gl/webgl'; test('WebGLDevice#lost (Promise)', async t => { - const device = await WebGLDevice.create(); + const device = await webgl2Adapter.create(); // Wrap in a promise to make sure tape waits for us await new Promise(async resolve => { diff --git a/modules/webgpu/src/adapter/webgpu-adapter.ts b/modules/webgpu/src/adapter/webgpu-adapter.ts new file mode 100644 index 0000000000..ae090b8c34 --- /dev/null +++ b/modules/webgpu/src/adapter/webgpu-adapter.ts @@ -0,0 +1,91 @@ +// luma.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import {Adapter, DeviceProps, CanvasContext, log} from '@luma.gl/core'; +import {WebGPUDevice} from './webgpu-device'; + +export class WebGPUAdapter extends Adapter { + /** type of device's created by this adapter */ + readonly type: WebGPUDevice['type'] = 'webgpu'; + + constructor() { + super(); + // @ts-ignore For backwards compatibility luma.registerDevices + WebGPUDevice.adapter = this; + } + + /** Check if WebGPU is available */ + isSupported(): boolean { + return Boolean(typeof navigator !== 'undefined' && navigator.gpu); + } + + async create(props: DeviceProps): Promise { + if (!navigator.gpu) { + throw new Error( + 'WebGPU not available. Open in Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu' + ); + } + log.groupCollapsed(1, 'WebGPUDevice created')(); + const adapter = await navigator.gpu.requestAdapter({ + powerPreference: 'high-performance' + // forceSoftware: false + }); + + if (!adapter) { + throw new Error('Failed to request WebGPU adapter'); + } + + const adapterInfo = await adapter.requestAdapterInfo(); + log.probe(2, 'Adapter available', adapterInfo)(); + + const requiredFeatures: GPUFeatureName[] = []; + const requiredLimits: Record = {}; + + if (props.requestMaxLimits) { + // Require all features + requiredFeatures.push(...(Array.from(adapter.features) as GPUFeatureName[])); + + // Require all limits + // Filter out chrome specific keys (avoid crash) + const limits = Object.keys(adapter.limits).filter( + key => !['minSubgroupSize', 'maxSubgroupSize'].includes(key) + ); + for (const key of limits) { + const limit = key as keyof GPUSupportedLimits; + const value = adapter.limits[limit]; + if (typeof value === 'number') { + requiredLimits[limit] = value; + } + } + } + + const gpuDevice = await adapter.requestDevice({ + requiredFeatures, + requiredLimits + }); + + log.probe(1, 'GPUDevice available')(); + + if (typeof props.canvas === 'string') { + await CanvasContext.pageLoaded; + log.probe(1, 'DOM is loaded')(); + } + + const device = new WebGPUDevice(props, gpuDevice, adapter, adapterInfo); + + log.probe( + 1, + 'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features' + )(); + log.table(1, device.info)(); + log.groupEnd(1)(); + return device; + } + + attach(handle: GPUDevice): WebGPUDevice { + throw new Error('WebGPUAdapter.attach() not implemented'); + } +} + +export const webgpuAdapter = new WebGPUAdapter(); diff --git a/modules/webgpu/src/adapter/webgpu-device.ts b/modules/webgpu/src/adapter/webgpu-device.ts index 2e3de46499..58ac3201e6 100644 --- a/modules/webgpu/src/adapter/webgpu-device.ts +++ b/modules/webgpu/src/adapter/webgpu-device.ts @@ -30,7 +30,7 @@ import type { QuerySet, QuerySetProps } from '@luma.gl/core'; -import {Device, DeviceFeatures, CanvasContext, log} from '@luma.gl/core'; +import {Device, DeviceFeatures} from '@luma.gl/core'; import {WebGPUBuffer} from './resources/webgpu-buffer'; import {WebGPUTexture} from './resources/webgpu-texture'; import {WebGPUExternalTexture} from './resources/webgpu-external-texture'; @@ -49,8 +49,6 @@ import {WebGPUQuerySet} from './resources/webgpu-query-set'; /** WebGPU Device implementation */ export class WebGPUDevice extends Device { - static type: string = 'webgpu'; - /** type of this device */ readonly type = 'webgpu'; @@ -72,74 +70,7 @@ export class WebGPUDevice extends Device { commandEncoder: GPUCommandEncoder | null = null; renderPass: WebGPURenderPass | null = null; - /** Check if WebGPU is available */ - static isSupported(): boolean { - return Boolean(typeof navigator !== 'undefined' && navigator.gpu); - } - - static async create(props: DeviceProps): Promise { - if (!navigator.gpu) { - throw new Error( - 'WebGPU not available. Open in Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu' - ); - } - log.groupCollapsed(1, 'WebGPUDevice created')(); - const adapter = await navigator.gpu.requestAdapter({ - powerPreference: 'high-performance' - // forceSoftware: false - }); - if (!adapter) { - throw new Error('Failed to request WebGPU adapter'); - } - - const adapterInfo = await adapter.requestAdapterInfo(); - log.probe(2, 'Adapter available', adapterInfo)(); - - const requiredFeatures: GPUFeatureName[] = []; - const requiredLimits: Record = {}; - - if (props.requestMaxLimits) { - // Require all features - requiredFeatures.push(...(Array.from(adapter.features) as GPUFeatureName[])); - - // Require all limits - // Filter out chrome specific keys (avoid crash) - const limits = Object.keys(adapter.limits).filter( - key => !['minSubgroupSize', 'maxSubgroupSize'].includes(key) - ); - for (const key of limits) { - const limit = key as keyof GPUSupportedLimits; - const value = adapter.limits[limit]; - if (typeof value === 'number') { - requiredLimits[limit] = value; - } - } - } - - const gpuDevice = await adapter.requestDevice({ - requiredFeatures, - requiredLimits - }); - - log.probe(1, 'GPUDevice available')(); - - if (typeof props.canvas === 'string') { - await CanvasContext.pageLoaded; - log.probe(1, 'DOM is loaded')(); - } - - const device = new WebGPUDevice(props, gpuDevice, adapter, adapterInfo); - - log.probe( - 1, - 'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features' - )(); - log.table(1, device.info)(); - log.groupEnd(1)(); - return device; - } - - protected constructor( + constructor( props: DeviceProps, device: GPUDevice, adapter: GPUAdapter, diff --git a/modules/webgpu/src/index.ts b/modules/webgpu/src/index.ts index af00ecb03b..957f45a3d7 100644 --- a/modules/webgpu/src/index.ts +++ b/modules/webgpu/src/index.ts @@ -3,9 +3,11 @@ // Copyright (c) vis.gl contributors // WEBGPU ADAPTER -export {WebGPUDevice} from './adapter/webgpu-device'; +export type {WebGPUAdapter} from './adapter/webgpu-adapter'; +export {webgpuAdapter} from './adapter/webgpu-adapter'; // WEBGPU CLASSES (typically not accessed directly) +export {WebGPUDevice} from './adapter/webgpu-device'; export {WebGPUBuffer} from './adapter/resources/webgpu-buffer'; export {WebGPUTexture} from './adapter/resources/webgpu-texture'; export {WebGPUSampler} from './adapter/resources/webgpu-sampler'; diff --git a/website/src/react-luma/store/device-store.tsx b/website/src/react-luma/store/device-store.tsx index 6496fcde00..6a6bb390f8 100644 --- a/website/src/react-luma/store/device-store.tsx +++ b/website/src/react-luma/store/device-store.tsx @@ -1,10 +1,10 @@ import {create} from 'zustand'; import {luma, Device} from '@luma.gl/core'; -import {WebGLDevice} from '@luma.gl/webgl'; -import {WebGPUDevice} from '@luma.gl/webgpu'; +import {webgl2Adapter} from '@luma.gl/webgl'; +import {webgpuAdapter} from '@luma.gl/webgpu'; -luma.registerDevices([WebGLDevice, WebGPUDevice]); +luma.registerAdapters([webgl2Adapter, webgpuAdapter]); export type Store = { device?: Device;