Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(core) new device props api #2100

Merged
merged 24 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions docs/api-reference/core/device.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,44 @@ console.error(message);

| Parameter | Default | Description |
| ------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `type` | `'best-available'` | `'webgpu'`, `'webgl'`, `'best-available'` |
| `canvas` | N/A | A _string_ `id` of an existing HTML element or a _DOMElement_. If not provided, a new canvas will be created. |
| priority. |
| `debug?: boolean` | `false` | WebGL API calls will be logged to the console and WebGL errors will generate JavaScript exceptions. |
| `break?: string[]` | `[]` | Insert a break point (`debugger`) if one of the listed gl functions is called. |
| `id?: string` | `null` | |
| `canvas?: HTMLCanvasElement | OffscreenCanvas | string` | N/A | A _string_ `id` or object of an existing canvas element. If not provided, a new canvas will be created.|
| `container?: HTMLElement | string` | N/A | If new canvas is created, it will be created in the specified container, otherwise appended to body. |
| `width?: number` | `800` | Sets the canvas width. Only used when creating a new canvas internally. |
| `height?: number` | `600` | Sets the canvas height. Only used when creating a new canvas internally. |
| `onError?: (error: Error) => unknown` | `log.error` | Error handling. |
| `initalizeFeatures?: boolean` | `true` | Initialize all features on startup. |
| `disabledFeatures?: Partial<Record<DeviceFeature, boolean>>` | `{ 'compilation-status-async-webgl': true }` | Disable specific features. |
| `_factoryDestroyPolicy?: 'unused' | 'never'` | `true` | Never destroy cached shaders and pipelines. |

### WebGLDeviceProps

Extends `DeviceProps`.

| WebGL Parameter | Default | Description |
| ------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `alpha?: boolean` | `true` | Default render target has an alpha buffer. |
| `depth?: boolean` | `true` | Default render target has a depth buffer of at least `16` bits. |
| `stencil?` | `false` | Default render target has a stencil buffer of at least `8` bits. |
| `stencil?: boolean` | `false` | Default render target has a stencil buffer of at least `8` bits. |
| `desynchronized?: boolean` | `false` | Hints the user agent to reduce the latency by desynchronizing the canvas paint cycle from the event loop. |
| `antialias?` | `true` | Boolean that indicates whether or not to perform anti-aliasing. |
| `failIfMajorPerformanceCaveat?` | `false` | Do not create if the system performance is low. |
| `powerPreference` | `'high-performance'`| `'default'`, `'high-performance'`, `'low-power'` |
| `premultipliedAlpha?` | `true` | Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha. |
| `preserveDrawingBuffer?` | `false` | Default render target buffers will preserve their values until cleared or overwritten. Useful for screen capture. |
| `failIfMajorPerformanceCaveat?` | `false` | Do not create if the system performance is low. |
| `onContextLost` | `(event: Event) => void` | `webglcontextlost` event. |
| `onContextRestored` | `(event: Event) => void` | `webglcontextrestored` event. |
| `debug?: boolean` | `false` | WebGL API calls will be logged to the console and WebGL errors will generate JavaScript exceptions. |
| `break?: string[]` | `[]` | Insert a break point (`debugger`) if one of the listed gl functions is called. |
| `manageState?: boolean` | `true` | Set to false to disable WebGL state management instrumentation. |

### WebGPUDeviceProps

Extends `DeviceProps`.

| WebGPU Parameter | Default | Description |
| ------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `requestMaxLimits` | `true` | Request a Device with the highest limits supported by platform. WebGPU: devices can be created with minimal limits. |

## Fields

Expand Down
4 changes: 2 additions & 2 deletions docs/api-reference/core/luma.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const webgpuDevice = luma.createDevice({type: 'best-available', canvas: ...});

### `CreateDeviceProps`

Properties for creating a new device
Properties for creating a new device.

```ts
type CreateDeviceProps = DeviceProps & {
Expand Down Expand Up @@ -137,7 +137,7 @@ Note: A specific device type is available and supported if both of the following
### `luma.attachDevice()`

```ts
luma.attachDevice({handle: WebGL2RenderingContext | GPUDevice, adapters, ...}: AttachDeviceProps);
luma.attachDevice({handle: WebGL2RenderingContext | GPUDevice, adapters, ...deviceProps}: AttachDeviceProps);
```

A luma.gl Device can be attached to an externally created `WebGL2RenderingContext` or `GPUDevice`.
Expand Down
8 changes: 4 additions & 4 deletions docs/api-reference/webgl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
This module contains the WebGL adapter for the "abstract" luma.gl API (`@luma.gl/core`).

Simply importing `@luma.gl/webgl` installs the adapter and enables WebGL devices to
be created using `luma.createDevice(...)`:
be created using `luma.createDevice(props)`. See [`WebGLDeviceProps`](../core/device#webgldeviceprops) for prop options.

```typescript
import {luma} from '@luma.gl/core';
Expand All @@ -18,20 +18,20 @@ const buffer = device.createBuffer(...);
```

To use a luma.gl WebGL `Device` with raw WebGL calls, the application needs to access
the `WebGLRenderingContext`. The context is available on the `WebGLDevice` subclass:
the `WebGL2RenderingContext`. The context is available on the `WebGLDevice` subclass:

```typescript
// @ts-expect-error
const gl = device.gl;
```

With a bit more work, typescript users can retrieve the `WebGLRenderingContext`
With a bit more work, typescript users can retrieve the `WebGL2RenderingContext`
without ignoring type errors:

```typescript
import {cast} from '@luma.gl/core';
import {WebGLDevice} from '@luma.gl/webgl'; // Installs the WebGLDevice adapter

const webglDevice = cast<WebGPUDevice>(device);
const webglDevice = cast<WebGLDevice>(device);
const gl = webglDevice.gl;
```
2 changes: 1 addition & 1 deletion docs/api-reference/webgpu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
This module contains the WebGPU adapter for the "abstract" luma.gl API (`@luma.gl/core`).

Simply importing `@luma.gl/webgpu` installs the adapter and enables WebGPU devices to
be created using `luma.createDevice(...)`:
be created using `luma.createDevice(props)`: See [`WebGPUDeviceProps`](../core/device#webgpudeviceprops) for prop options.

```typescript
import {luma} from '@luma.gl/core';
Expand Down
97 changes: 63 additions & 34 deletions modules/core/src/adapter/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ type WebGLCompressedTextureFeatures =
| 'texture-compression-atc-webgl';

/** Device properties */
export type DeviceProps = {
type _DeviceProps = {
id?: string;

// Common parameters
Expand All @@ -203,21 +203,19 @@ export type DeviceProps = {
width?: number /** width is only used when creating a new canvas */;
height?: number /** height is only used when creating a new canvas */;

/** Request a Device with the highest limits supported by platform. WebGPU: devices can be created with minimal limits. */
requestMaxLimits?: boolean;

// WebGLContext PARAMETERS - Can only be set on context creation...
// alpha?: boolean; // Default render target has an alpha buffer.
// depth?: boolean; // Default render target has a depth buffer of at least 16 bits.
// stencil?: boolean; // Default render target has a stencil buffer of at least 8 bits.
// antialias?: boolean; // Boolean that indicates whether or not to perform anti-aliasing.
// premultipliedAlpha?: boolean; // Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
// preserveDrawingBuffer?: boolean; // Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
// failIfMajorPerformanceCaveat?: boolean; // Do not create if the system performance is low.

/** Error handling */
onError?: (error: Error) => unknown;

// EXPERIMENTAL SETTINGS
/** Initialize all features on startup */
initalizeFeatures?: boolean;
/** Disable specific features */
disabledFeatures?: Partial<Record<DeviceFeature, boolean>>;
/** Never destroy cached shaders and pipelines */
_factoryDestroyPolicy?: 'unused' | 'never';
};

export type WebGLDeviceProps = _DeviceProps & {
// @deprecated Attach to existing context. Rename to handle? Use Device.attach?
gl?: WebGL2RenderingContext | null;

Expand All @@ -232,17 +230,31 @@ export type DeviceProps = {
/** SpectorJS URL. Override if CDN is down or different SpectorJS version is desired */
spectorUrl?: string;

// ContextProps
onContextLost?: (event: Event) => void;
onContextRestored?: (event: Event) => void;
chrisgervang marked this conversation as resolved.
Show resolved Hide resolved
alpha?: boolean; // indicates if the canvas contains an alpha buffer.
depth?: boolean; // indicates that the drawing buffer has a depth buffer of at least 16 bits.
stencil?: boolean; // Default render target has a stencil buffer of at least `8` bits.
desynchronized?: boolean; // hints the user agent to reduce the latency by desynchronizing the canvas paint cycle from the event loop
antialias?: boolean; // indicates whether or not to perform anti-aliasing.
failIfMajorPerformanceCaveat?: boolean; // indicates if a context will be created if the system performance is low or if no hardware GPU is available.
powerPreference?: 'default' | 'high-performance' | 'low-power';
premultipliedAlpha?: boolean; // page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
preserveDrawingBuffer?: boolean; // buffers will not be cleared and will preserve their values until cleared or overwritten by the author.

// EXPERIMENTAL SETTINGS
/** Set to false to disable WebGL state management instrumentation: TODO- Unclear if still supported / useful */
manageState?: boolean;
/** Initialize all features on startup */
initalizeFeatures?: boolean;
/** Disable specific features */
disabledFeatures?: Partial<Record<DeviceFeature, boolean>>;
/** Never destroy cached shaders and pipelines */
_factoryDestroyPolicy?: 'unused' | 'never';
};

export type WebGPUDeviceProps = _DeviceProps & {
/** Request a Device with the highest limits supported by platform. WebGPU: devices can be created with minimal limits. */
requestMaxLimits?: boolean;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intended to be WebGPU only? It isn't used by WebGL

};

export type DeviceProps = WebGLDeviceProps & WebGPUDeviceProps;

/**
* Create and attach devices for a specific backend. Currently static methods on each device
*/
Expand All @@ -262,23 +274,38 @@ export abstract class Device {
id: null!,
canvas: null,
container: null,
manageState: true,
width: 800, // width are height are only used by headless gl
height: 600,
requestMaxLimits: true,

// Callbacks
onError: (error: Error) => log.error(error.message),

// TODO - Change these after confirming things work as expected
initalizeFeatures: true,
disabledFeatures: {
'compilation-status-async-webgl': true
},
_factoryDestroyPolicy: 'unused',

/**
* WebGL
*/

gl: null,

// alpha: undefined,
// depth: undefined,
// stencil: undefined,
// antialias: undefined,
// premultipliedAlpha: undefined,
// preserveDrawingBuffer: undefined,
// failIfMajorPerformanceCaveat: undefined
// WebGL context aattributes
alpha: true,
depth: true,
stencil: false,
desynchronized: false,
antialias: true,
failIfMajorPerformanceCaveat: false,
powerPreference: 'high-performance',
premultipliedAlpha: true,
preserveDrawingBuffer: false,
chrisgervang marked this conversation as resolved.
Show resolved Hide resolved

onContextLost: undefined!,
onContextRestored: undefined!,

debug: Boolean(log.get('debug')), // Instrument context (at the expense of performance)
break: (log.get('break') as string[]) || [],
Expand All @@ -287,12 +314,14 @@ export abstract class Device {
debugWithSpectorJS: undefined!,
spectorUrl: undefined!,

// TODO - Change these after confirming things work as expected
initalizeFeatures: true,
disabledFeatures: {
'compilation-status-async-webgl': true
},
_factoryDestroyPolicy: 'unused'
// Experimental WebGL
manageState: true,

/**
* WebGPU
*/

requestMaxLimits: true
};

get [Symbol.toStringTag](): string {
Expand Down
21 changes: 14 additions & 7 deletions modules/core/src/adapter/luma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Copyright (c) vis.gl contributors

import type {Log} from '@probe.gl/log';
import type {DeviceProps} from './device';
import type {DeviceProps, WebGLDeviceProps, WebGPUDeviceProps} from './device';
import {Device} from './device';
import {Adapter} from './adapter';
import {StatsManager} from '../utils/stats-manager';
Expand All @@ -21,19 +21,26 @@ const ERROR_MESSAGE =
'No matching device found. Ensure `@luma.gl/webgl` and/or `@luma.gl/webgpu` modules are imported.';

/** Properties for creating a new device */
export type CreateDeviceProps = DeviceProps & {
/** Selects the type of device. `best-available` uses webgpu if available, then webgl. */
type?: 'webgl' | 'webgpu' | 'unknown' | 'best-available';
export type CreateDeviceProps = {
chrisgervang marked this conversation as resolved.
Show resolved Hide resolved
/** List of adapters. Will also search any pre-registered adapterss */
adapters?: Adapter[];
};
} /** Selects the type of device. `best-available` uses webgpu if available, then webgl. */ & (
| ({type: 'webgl'} & WebGLDeviceProps)
| ({type: 'webgpu'} & WebGPUDeviceProps)
| ({type?: 'unknown' | 'best-available'} & DeviceProps)
);

/** Properties for attaching an existing WebGL context or WebGPU device to a new luma Device */
export type AttachDeviceProps = DeviceProps & {
export type AttachDeviceProps = {
/** Externally created WebGL context or WebGPU device */
handle: unknown; // WebGL2RenderingContext | GPUDevice | null;
ibgreen marked this conversation as resolved.
Show resolved Hide resolved
/** List of adapters. Will also search any pre-registered adapterss */
adapters?: Adapter[];
};
} & (
| ({type: 'webgl'} & WebGLDeviceProps)
| ({type: 'webgpu'} & WebGPUDeviceProps)
| ({type?: 'unknown' | 'best-available'} & DeviceProps)
);

/**
* Entry point to the luma.gl GPU abstraction
Expand Down
9 changes: 8 additions & 1 deletion modules/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
// Copyright (c) vis.gl contributors

// MAIN API ACCESS POINT
export type {CreateDeviceProps} from './adapter/luma';
chrisgervang marked this conversation as resolved.
Show resolved Hide resolved
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 type {
DeviceProps,
DeviceInfo,
DeviceFeature,
WebGLDeviceProps,
WebGPUDeviceProps
} from './adapter/device';
export {Device, DeviceFeatures, DeviceLimits} from './adapter/device';

export type {CanvasContextProps} from './adapter/canvas-context';
Expand Down
10 changes: 6 additions & 4 deletions modules/webgl/src/adapter/webgl-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Copyright (c) vis.gl contributors

import type {TypedArray} from '@math.gl/types';
import type {DeviceProps, DeviceInfo, CanvasContextProps, TextureFormat} from '@luma.gl/core';
import type {WebGLDeviceProps, DeviceInfo, CanvasContextProps, TextureFormat} from '@luma.gl/core';
import type {Buffer, Texture, Framebuffer, VertexArray, VertexArrayProps} from '@luma.gl/core';
import {Device, CanvasContext, log} from '@luma.gl/core';
import type {GLExtensions} from '@luma.gl/constants';
Expand Down Expand Up @@ -108,7 +108,7 @@ export class WebGLDevice extends Device {
// Public API
//

constructor(props: DeviceProps) {
constructor(props: WebGLDeviceProps) {
super({...props, id: props.id || uid('webgl-device')});

// If attaching to an already attached context, return the attached device
Expand All @@ -128,11 +128,13 @@ export class WebGLDevice extends Device {

this.handle = createBrowserContext(this.canvasContext.canvas, {
...props,
onContextLost: (event: Event) =>
onContextLost: (event: Event) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these changes? Some editor auto correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calls the users onContextLost, if provided

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calls the users onContextLost, if provided

I see. Adding a one line bug fix in to a bigger API change is a bit risky, can slip through the review, and can get lost if API change is not approved...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to split this out. I didn't mean to put anything risky in here. The reason I included this is get clarity on the intended API. It's unclear to me what the intent is.

If users shouldn't be able to provide onContextLost by design, then the API types need to be adjusted a bit. If we can agree the user should be able to provide onContextLost, then this is a bug and I'll pull that into another PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you shine a light on it, I think we should completely drop support for onContextLost. Such callback code tends to be surprisingly messy to keep working across devices and it doesn't add value.

We don't talk about contexts anymore, just devices. and we have the replacement in the WebGPU style promise API: device.lost

App can register its onContextLost callback with device.lost.then(onContextLost).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. device.lost sounds better since it the same API for both devices, making this redundant.

Is there a device equivalent for onContextRestore as well?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, context restore is not supported by luma.gl v9.

The problems is that properly handling context restore in non-trivial apps is very tricky, since all resources are lost and have to be recreated, and the data that was used to create buffers and textures may not have been saved.

\Showing a message asking the user to just refresh the page is usually best way out.

WebGPU doesn't support restore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the onContextRestore code in the WebGL device be removed then?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I would vote for that. It is not impossible for an app to register the callback even if luma.gl API doesn't support it, they will just need to get the context and add an event handler themselves.

this._resolveContextLost?.({
reason: 'destroyed',
message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
})
});
props.onContextLost?.(event);
}
});
this.gl = this.handle;

Expand Down
6 changes: 3 additions & 3 deletions modules/webgpu/src/adapter/webgpu-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// / <reference types="@webgpu/types" />

import type {
DeviceProps,
DeviceInfo,
DeviceLimits,
DeviceFeature,
Expand All @@ -28,7 +27,8 @@ import type {
TransformFeedback,
TransformFeedbackProps,
QuerySet,
QuerySetProps
QuerySetProps,
WebGPUDeviceProps
} from '@luma.gl/core';
import {Device, DeviceFeatures} from '@luma.gl/core';
import {WebGPUBuffer} from './resources/webgpu-buffer';
Expand Down Expand Up @@ -71,7 +71,7 @@ export class WebGPUDevice extends Device {
renderPass: WebGPURenderPass | null = null;

constructor(
props: DeviceProps,
props: WebGPUDeviceProps,
device: GPUDevice,
adapter: GPUAdapter,
adapterInfo: GPUAdapterInfo
Expand Down
Loading