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

feat(webgpu): Ability to override render targets on RenderPipeline #2245

Merged
merged 2 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion docs/api-reference/core/texture-formats.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Note that even though a GPU supports creating and sampling textures of a certain
| `rgb9e5ufloat` | <Ft f="rgb9e5ufloat" /> | <L f="rgb9e5ufloat" /> | <R f="rgb9e5ufloat" /> | `GL.RGB9_E5` |
| `rg11b10ufloat` | <Ft f="rg11b10ufloat" /> | <L f="rg11b10ufloat" /> | <R f="rg11b10ufloat" /> | `GL.R11F_G11F_B10F` |
| `rgb10a2unorm` | <Ft f="rgb10a2unorm" /> | <L f="rgb10a2unorm" /> | <R f="rgb10a2unorm" /> | `GL.RGB10_A2` |
| `rgb10a2uint-webgl` | <Ft f="rgb10a2uint-webgl" /> | <L f="rgb10a2uint-webgl" /> | <R f="rgb10a2uint-webgl" /> | `GL.RGB10_A2UI` |
| `rgb10a2uint` | <Ft f="rgb10a2uint" /> | <L f="rgb10a2uint" /> | <R f="rgb10a2uint" /> | `GL.RGB10_A2UI` |
| **6 bytes per pixel formats** | | | |
| `rgb16unorm-webgl` | <Ft f="rgb16unorm-webgl" /> | <L f="rgb16unorm-webgl" /> | <R f="rgb16unorm-webgl" /> | |
| `rgb16snorm-webgl` | <Ft f="rgb16snorm-webgl" /> | <L f="rgb16snorm-webgl" /> | <R f="rgb16snorm-webgl" /> | |
Expand Down
11 changes: 4 additions & 7 deletions modules/core/src/adapter/canvas-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {Device} from './device';
import type {Framebuffer} from './resources/framebuffer';
import {log} from '../utils/log';
import {uid} from '../utils/uid';
import type {TextureFormat, DepthStencilTextureFormat} from '../gpu-type-utils/texture-formats';
import type {DepthStencilTextureFormat} from '../gpu-type-utils/texture-formats';

/** Properties for a CanvasContext */
export type CanvasContextProps = {
Expand Down Expand Up @@ -44,7 +44,7 @@ export abstract class CanvasContext {
static defaultProps: Required<CanvasContextProps> = {
id: undefined!,
canvas: null,
width: 800, // width are height are only used by headless gl
width: 800,
height: 600,
useDevicePixels: true,
autoResize: true,
Expand All @@ -55,18 +55,15 @@ export abstract class CanvasContext {
};

abstract readonly device: Device;
abstract readonly handle: unknown;
readonly id: string;

readonly props: Required<CanvasContextProps>;
readonly canvas: HTMLCanvasElement | OffscreenCanvas;
readonly htmlCanvas?: HTMLCanvasElement;
readonly offscreenCanvas?: OffscreenCanvas;
readonly type: 'html-canvas' | 'offscreen-canvas' | 'node';

/** Format of returned textures: "bgra8unorm", "rgba8unorm" */
abstract readonly format: TextureFormat;
/** Default stencil format for depth textures */
abstract readonly depthStencilFormat: TextureFormat;

protected _initializedResolvers = withResolvers<void>();

/** Promise that resolved once the resize observer has updated the pixel size */
Expand Down
9 changes: 8 additions & 1 deletion modules/core/src/adapter/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ export abstract class Device {
// Callbacks
onError: (error: Error) => log.error(error.message)(),
onResize: (context: CanvasContext, info: {oldPixelSize: [number, number]}) => {
const [prevWidth, prevHeight] = info.oldPixelSize;
const [width, height] = context.getPixelSize();
const [prevWidth, prevHeight] = info.oldPixelSize;
log.log(1, `${context} Resized ${prevWidth}x${prevHeight} => ${width}x${height}px`)();
},
onVisibilityChange: (context: CanvasContext) =>
Expand Down Expand Up @@ -361,6 +361,8 @@ export abstract class Device {
readonly id: string;
/** type of this device */
abstract readonly type: 'webgl' | 'webgpu' | 'unknown';
abstract readonly handle: unknown;

/** A copy of the device props */
readonly props: Required<DeviceProps>;
/** Available for the application to store data on the device */
Expand All @@ -384,6 +386,11 @@ export abstract class Device {
/** WebGPU style device limits */
abstract get limits(): DeviceLimits;

/** Optimal TextureFormat for displaying 8-bit depth, standard dynamic range content on this system. */
abstract preferredColorFormat: 'rgba8unorm' | 'bgra8unorm';
/** Default depth format used on this system */
abstract preferredDepthFormat: 'depth16' | 'depth24plus' | 'depth32float';

/** Determines what operations are supported on a texture format, checking against supported device features */
getTextureFormatCapabilities(format: TextureFormat): DeviceTextureFormatCapabilities {
const genericCapabilities = getTextureFormatCapabilities(format);
Expand Down
30 changes: 18 additions & 12 deletions modules/core/src/adapter/resources/render-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import type {UniformValue} from '../types/uniforms';
import type {PrimitiveTopology, RenderPipelineParameters} from '../types/parameters';
import type {ShaderLayout, Binding} from '../types/shader-layout';
import type {BufferLayout} from '../types/buffer-layout';
// import {normalizeAttributeMap} from '../helpers/attribute-bindings';
import {Resource, ResourceProps} from './resource';
import type {
ColorTextureFormat,
DepthStencilTextureFormat
} from '@luma.gl/core/gpu-type-utils/texture-formats';
import type {Shader} from './shader';
import type {RenderPass} from './render-pass';
import {Resource, ResourceProps} from './resource';
import {VertexArray} from './vertex-array';
import {TransformFeedback} from './transform-feedback';

Expand All @@ -37,15 +40,18 @@ export type RenderPipelineProps = ResourceProps & {

/** Determines how vertices are read from the 'vertex' attributes */
topology?: PrimitiveTopology;

// color attachment information (needed on WebGPU)

/** Color attachments expected by this pipeline. Defaults to [device.preferredColorFormat]. Array needs not be contiguous. */
colorAttachmentFormats?: (ColorTextureFormat | null)[];
/** Depth attachment expected by this pipeline (if depth parameters are specified). Defaults to device.preferredDepthFormat */
depthStencilAttachmentFormat?: DepthStencilTextureFormat;

/** Parameters that are controlled by pipeline */
parameters?: RenderPipelineParameters;

// /** Use instanced rendering? */
// isInstanced?: boolean;
// /** Number of instances */
// instanceCount?: number;
// /** Number of vertices */
// vertexCount?: number;
// Dynamic bindings (TODO - pipelines should be immutable, move to RenderPass)

/** Buffers, Textures, Samplers for the shader bindings */
bindings?: Record<string, Binding>;
Expand All @@ -71,11 +77,11 @@ export abstract class RenderPipeline extends Resource<RenderPipelineProps> {
shaderLayout: null,
bufferLayout: [],
topology: 'triangle-list',
parameters: {},

// isInstanced: false,
// instanceCount: 0,
// vertexCount: 0,
colorAttachmentFormats: undefined!,
depthStencilAttachmentFormat: undefined!,

parameters: {},

bindings: {},
uniforms: {}
Expand Down
2 changes: 1 addition & 1 deletion modules/core/src/gpu-type-utils/texture-format-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const TEXTURE_FORMAT_TABLE: Readonly<Record<TextureFormat, TextureFormatDefiniti
'rgb9e5ufloat': {channels: 'rgb', packed: true, render: rgb9e5ufloat_renderable}, // , filter: true},
'rg11b10ufloat': {channels: 'rgb', bitsPerChannel: [11, 11, 10, 0], packed: true, p: 1,render: float32_renderable},
'rgb10a2unorm': {channels: 'rgba', bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1},
'rgb10a2uint-webgl': {channels: 'rgba', bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1, wgpu: false},
'rgb10a2uint': {channels: 'rgba', bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1},

// 48-bit formats
'rgb16unorm-webgl': {f: norm16_renderable}, // rgb not renderable
Expand Down
3 changes: 2 additions & 1 deletion modules/core/src/gpu-type-utils/texture-formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type WebGPUColorTextureFormat =
// Packed 32-bit formats
| 'rgb9e5ufloat'
| 'rgb10a2unorm'
| 'rgb10a2uint'
| 'rg11b10ufloat'

// 64-bit formats
Expand Down Expand Up @@ -143,7 +144,7 @@ export type WebGL2ColorTextureFormat =
| 'rgb8snorm-webgl'
| 'rg16unorm-webgl'
| 'rg16snorm-webgl'
| 'rgb10a2uint-webgl'
| 'rgb10a2uint'
| 'rgb16unorm-webgl'
| 'rgb16snorm-webgl'
| 'rgba16unorm-webgl'
Expand Down
5 changes: 2 additions & 3 deletions modules/test-utils/src/null-device/null-canvas-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import type {CanvasContextProps, TextureFormat} from '@luma.gl/core';
import type {CanvasContextProps} from '@luma.gl/core';
import {CanvasContext} from '@luma.gl/core';
import type {NullDevice} from './null-device';
import {NullFramebuffer} from './resources/null-framebuffer';
Expand All @@ -12,8 +12,7 @@ import {NullFramebuffer} from './resources/null-framebuffer';
*/
export class NullCanvasContext extends CanvasContext {
readonly device: NullDevice;
readonly format: TextureFormat = 'rgba8unorm';
readonly depthStencilFormat: TextureFormat = 'depth24plus';
readonly handle = null;

presentationSize: [number, number];
private _framebuffer: NullFramebuffer | null = null;
Expand Down
5 changes: 5 additions & 0 deletions modules/test-utils/src/null-device/null-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export class NullDevice extends Device {
return true;
}
readonly type = 'unknown';
readonly handle = null;

readonly preferredColorFormat = 'rgba8unorm';
readonly preferredDepthFormat = 'depth24plus';

features: DeviceFeatures = new DeviceFeatures([], this.props._disabledFeatures);
limits: NullDeviceLimits = new NullDeviceLimits();
readonly info = NullDeviceInfo;
Expand Down
4 changes: 2 additions & 2 deletions modules/webgl/src/adapter/converters/webgl-texture-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ export const WEBGL_TEXTURE_FORMATS: Record<TextureFormat, WebGLFormatInfo> = {
'rgb9e5ufloat': {gl: GL.RGB9_E5}, // , filter: true},
'rg11b10ufloat': {gl: GL.R11F_G11F_B10F, rb: true},
'rgb10a2unorm': {gl: GL.RGB10_A2, rb: true},
'rgb10a2uint-webgl': {gl: GL.RGB10_A2UI, rb: true},

'rgb10a2uint': {gl: GL.RGB10_A2UI, rb: true},
// 48-bit formats
'rgb16unorm-webgl': {gl: GL.RGB16_EXT}, // rgb not renderable
'rgb16snorm-webgl': {gl: GL.RGB16_SNORM_EXT}, // rgb not renderable
Expand Down
6 changes: 2 additions & 4 deletions modules/webgl/src/adapter/webgl-canvas-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import type {CanvasContextProps, TextureFormat} from '@luma.gl/core';
import type {CanvasContextProps} from '@luma.gl/core';
import {CanvasContext} from '@luma.gl/core';
import {WebGLDevice} from './webgl-device';
import {WEBGLFramebuffer} from './resources/webgl-framebuffer';
Expand All @@ -12,10 +12,8 @@ import {WEBGLFramebuffer} from './resources/webgl-framebuffer';
*/
export class WebGLCanvasContext extends CanvasContext {
readonly device: WebGLDevice;
readonly format: TextureFormat = 'rgba8unorm';
readonly depthStencilFormat: TextureFormat = 'depth24plus';
readonly handle: unknown = null;

presentationSize: [number, number];
private _framebuffer: WEBGLFramebuffer | null = null;

get [Symbol.toStringTag](): string {
Expand Down
6 changes: 3 additions & 3 deletions modules/webgl/src/adapter/webgl-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ import {getWebGLExtension} from '../context/helpers/webgl-extensions';

/** WebGPU style Device API for a WebGL context */
export class WebGLDevice extends Device {
//
// Public `Device` API
//

/** type of this device */
readonly type = 'webgl';
Expand All @@ -83,10 +81,12 @@ export class WebGLDevice extends Device {
readonly handle: WebGL2RenderingContext;
features: WebGLDeviceFeatures;
limits: WebGLDeviceLimits;

readonly info: DeviceInfo;
readonly canvasContext: WebGLCanvasContext;

readonly preferredColorFormat = 'rgba8unorm';
readonly preferredDepthFormat = 'depth24plus';

readonly lost: Promise<{reason: 'destroyed'; message: string}>;

private _resolveContextLost?: (value: {reason: 'destroyed'; message: string}) => void;
Expand Down
33 changes: 21 additions & 12 deletions modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,34 +166,43 @@ export class WebGPURenderPipeline extends RenderPipeline {
buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout)
};

// Populate color targets
// TODO - at the moment blend and write mask are only set on the first target
const targets: (GPUColorTargetState | null)[] = [];
if (this.props.colorAttachmentFormats) {
for (const format of this.props.colorAttachmentFormats) {
targets.push(format ? {format: getWebGPUTextureFormat(format)} : null);
}
} else {
targets.push({format: getWebGPUTextureFormat(this.device.preferredColorFormat)});
}

// Set up the fragment stage
const fragment: GPUFragmentState = {
module: (this.props.fs as WebGPUShader).handle,
entryPoint: this.props.fragmentEntryPoint || 'main',
targets: [
{
format: getWebGPUTextureFormat(this.device.getDefaultCanvasContext().format)
}
]
targets
};

const depthStencil: GPUDepthStencilState | undefined = this.props.parameters.depthWriteEnabled
? {
format: getWebGPUTextureFormat(this.device.getDefaultCanvasContext().depthStencilFormat)
}
: undefined;

// Create a partially populated descriptor
const descriptor: GPURenderPipelineDescriptor = {
vertex,
fragment,
depthStencil,
primitive: {
topology: this.props.topology
},
layout: 'auto'
};

// Set depth format if required, defaulting to the preferred depth format
const depthFormat = this.props.depthStencilAttachmentFormat || this.device.preferredDepthFormat;

if (this.props.parameters.depthWriteEnabled) {
descriptor.depthStencil = {
format: getWebGPUTextureFormat(depthFormat)
};
}

// Set parameters on the descriptor
applyParametersToRenderPipelineDescriptor(descriptor, this.props.parameters);

Expand Down
25 changes: 7 additions & 18 deletions modules/webgpu/src/adapter/webgpu-canvas-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

// / <reference types="@webgpu/types" />

import type {TextureFormat, DepthStencilTextureFormat, CanvasContextProps} from '@luma.gl/core';
import type {DepthStencilTextureFormat, CanvasContextProps} from '@luma.gl/core';
import {CanvasContext, Texture, log} from '@luma.gl/core';
import {getWebGPUTextureFormat} from './helpers/convert-texture-format';
import {WebGPUDevice} from './webgpu-device';
import {WebGPUFramebuffer} from './resources/webgpu-framebuffer';
import {WebGPUTexture} from './resources/webgpu-texture';
Expand All @@ -18,11 +17,7 @@ import {WebGPUTexture} from './resources/webgpu-texture';
*/
export class WebGPUCanvasContext extends CanvasContext {
readonly device: WebGPUDevice;
readonly gpuCanvasContext: GPUCanvasContext;
/** Format of returned textures: "bgra8unorm", "rgba8unorm", "rgba16float". */
readonly format: TextureFormat = navigator.gpu.getPreferredCanvasFormat() as TextureFormat;
/** Default stencil format for depth textures */
readonly depthStencilFormat: TextureFormat = 'depth24plus';
readonly handle: GPUCanvasContext;

private depthStencilAttachment: WebGPUTexture | null = null;

Expand All @@ -34,20 +29,14 @@ export class WebGPUCanvasContext extends CanvasContext {
super(props);
this.device = device;

// @ts-ignore TODO - we don't handle OffscreenRenderingContext.
this.gpuCanvasContext = this.canvas.getContext('webgpu');
// TODO this has been replaced
// this.format = this.gpuCanvasContext.getPreferredFormat(adapter);
this.format = 'bgra8unorm';

// Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
this.updateSize([this.drawingBufferWidth, this.drawingBufferHeight]);
}

/** Destroy any textures produced while configured and remove the context configuration. */
destroy(): void {
this.gpuCanvasContext.unconfigure();
this.handle.unconfigure();
}

/** Update framebuffer with properly resized "swap chain" texture views */
Expand Down Expand Up @@ -92,9 +81,9 @@ export class WebGPUCanvasContext extends CanvasContext {

// Reconfigure the canvas size.
// https://www.w3.org/TR/webgpu/#canvas-configuration
this.gpuCanvasContext.configure({
this.handle.configure({
device: this.device.handle,
format: getWebGPUTextureFormat(this.format),
format: this.device.preferredColorFormat,
// Can be used to define e.g. -srgb views
// viewFormats: [...]
colorSpace: this.props.colorSpace,
Expand All @@ -121,8 +110,8 @@ export class WebGPUCanvasContext extends CanvasContext {
getCurrentTexture(): WebGPUTexture {
return this.device.createTexture({
id: `${this.id}#color-texture`,
handle: this.gpuCanvasContext.getCurrentTexture(),
format: this.format
handle: this.handle.getCurrentTexture(),
format: this.device.preferredColorFormat
});
}

Expand Down
Loading
Loading