Skip to content

Commit

Permalink
chore: Consolidate texture implementations, move specialized setters …
Browse files Browse the repository at this point in the history
…to AsyncTexture (#2247)
  • Loading branch information
ibgreen committed Sep 18, 2024
1 parent 5f4aacd commit 68d8878
Show file tree
Hide file tree
Showing 39 changed files with 1,577 additions and 1,122 deletions.
112 changes: 60 additions & 52 deletions modules/core/src/adapter/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {StatsManager, lumaStats} from '../utils/stats-manager';
import {log} from '../utils/log';
import {uid} from '../utils/uid';
import type {TextureFormat} from '../gpu-type-utils/texture-formats';
import type {TextureFormatCapabilities} from '../gpu-type-utils/texture-format-capabilities';
import type {CanvasContext, CanvasContextProps} from './canvas-context';
import type {BufferProps} from './resources/buffer';
import {Buffer} from './resources/buffer';
Expand All @@ -24,6 +23,8 @@ import type {VertexArray, VertexArrayProps} from './resources/vertex-array';
import type {TransformFeedback, TransformFeedbackProps} from './resources/transform-feedback';
import type {QuerySet, QuerySetProps} from './resources/query-set';

import type {ExternalImage} from '../image-utils/image-types';
import {isExternalImage, getExternalImageSize} from '../image-utils/image-types';
import {isTextureFormatCompressed} from '../gpu-type-utils/decode-texture-format';
import {getTextureFormatCapabilities} from '../gpu-type-utils/texture-format-capabilities';

Expand Down Expand Up @@ -197,15 +198,15 @@ type WebGLCompressedTextureFeatures =
/** Texture format capabilities that have been checked against a specific device */
export type DeviceTextureFormatCapabilities = {
format: TextureFormat;
/** Can the format be created */
/** Can the format be created and sampled?*/
create: boolean;
/** If a feature string, the specified device feature determines if format is renderable. */
/** Is the format renderable. */
render: boolean;
/** If a feature string, the specified device feature determines if format is filterable. */
/** Is the format filterable. */
filter: boolean;
/** If a feature string, the specified device feature determines if format is blendable. */
/** Is the format blendable. */
blend: boolean;
/** If a feature string, the specified device feature determines if format is storeable. */
/** Is the format storeable. */
store: boolean;
};

Expand Down Expand Up @@ -386,40 +387,30 @@ export abstract class Device {
/** WebGPU style device limits */
abstract get limits(): DeviceLimits;

// Texture helpers

/** 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);

// Check standard features
const checkFeature = (featureOrBoolean: DeviceFeature | boolean | undefined) =>
(typeof featureOrBoolean === 'string'
? this.features.has(featureOrBoolean)
: featureOrBoolean) ?? true;

const supported = checkFeature(genericCapabilities.create);
/** Calculates the number of mip levels for a texture of width and height */
getMipLevelCount(width: number, height: number): number {
return Math.floor(Math.log2(Math.max(width, height))) + 1;
}

const deviceCapabilities: DeviceTextureFormatCapabilities = {
format,
create: supported,
render: supported && checkFeature(genericCapabilities.render),
filter: supported && checkFeature(genericCapabilities.filter),
blend: supported && checkFeature(genericCapabilities.blend),
store: supported && checkFeature(genericCapabilities.store)
};
/** Check if data is an external image */
isExternalImage(data: unknown): data is ExternalImage {
return isExternalImage(data);
}

return this._getDeviceSpecificTextureFormatCapabilities(deviceCapabilities);
/** Get the size of an external image */
getExternalImageSize(data: ExternalImage): {width: number; height: number} {
return getExternalImageSize(data);
}

/** Check if device supports a specific texture format (creation and `nearest` sampling) */
isTextureFormatSupported(
format: TextureFormat,
capabilities: Partial<TextureFormatCapabilities>
): boolean {
isTextureFormatSupported(format: TextureFormat): boolean {
return this.getTextureFormatCapabilities(format).create;
}

Expand All @@ -438,6 +429,12 @@ export abstract class Device {
return isTextureFormatCompressed(format);
}

/** Determines what operations are supported on a texture format, checking against supported device features */
getTextureFormatCapabilities(format: TextureFormat): DeviceTextureFormatCapabilities {
const capabilities = this._getDeviceTextureFormatCapabilities(format);
return this._getDeviceSpecificTextureFormatCapabilities(capabilities);
}

// Device loss

/** `true` if device is already lost */
Expand All @@ -455,6 +452,11 @@ export abstract class Device {
return false;
}

/** A monotonic counter for tracking buffer and texture updates */
incrementTimestamp(): number {
return this.timestamp++;
}

/** Report error (normally called for unhandled device errors) */
reportError(error: Error): void {
this.props.onError(error);
Expand Down Expand Up @@ -514,26 +516,20 @@ export abstract class Device {
/** Create a ComputePass */
abstract beginComputePass(props?: ComputePassProps): ComputePass;

abstract createCommandEncoder(props?: CommandEncoderProps): CommandEncoder;

/** Create a transform feedback (immutable set of output buffer bindings). WebGL only. */
abstract createTransformFeedback(props: TransformFeedbackProps): TransformFeedback;

abstract createQuerySet(props: QuerySetProps): QuerySet;

createCommandEncoder(props: CommandEncoderProps = {}): CommandEncoder {
throw new Error('not implemented');
}

/** A monotonic counter for tracking buffer and texture updates */
incrementTimestamp(): number {
return this.timestamp++;
}

// Error Handling

/** Report unhandled device errors */
onError(error: Error) {
this.props.onError(error);
}
/**
* Determines what operations are supported on a texture format, checking against supported device features
* Subclasses override to apply additional checks
*/
protected abstract _getDeviceSpecificTextureFormatCapabilities(
format: DeviceTextureFormatCapabilities
): DeviceTextureFormatCapabilities;

// DEPRECATED METHODS

Expand Down Expand Up @@ -608,13 +604,25 @@ export abstract class Device {

// IMPLEMENTATION

/**
* Determines what operations are supported on a texture format, checking against supported device features
* Subclasses override to apply additional checks
*/
protected abstract _getDeviceSpecificTextureFormatCapabilities(
format: DeviceTextureFormatCapabilities
): DeviceTextureFormatCapabilities;
protected _getDeviceTextureFormatCapabilities(
format: TextureFormat
): DeviceTextureFormatCapabilities {
const genericCapabilities = getTextureFormatCapabilities(format);

// Check standard features
const checkFeature = (feature: DeviceFeature | boolean | undefined) =>
(typeof feature === 'string' ? this.features.has(feature) : feature) ?? true;

const supported = checkFeature(genericCapabilities.create);
return {
format,
create: supported,
render: supported && checkFeature(genericCapabilities.render),
filter: supported && checkFeature(genericCapabilities.filter),
blend: supported && checkFeature(genericCapabilities.blend),
store: supported && checkFeature(genericCapabilities.store)
} as const satisfies DeviceTextureFormatCapabilities;
}

/** Subclasses use this to support .createBuffer() overloads */
protected _normalizeBufferProps(props: BufferProps | ArrayBuffer | ArrayBufferView): BufferProps {
Expand Down
31 changes: 16 additions & 15 deletions modules/core/src/adapter/resources/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,6 @@ export type BufferProps = ResourceProps & {

/** Abstract GPU buffer */
export abstract class Buffer extends Resource<BufferProps> {
static override defaultProps: Required<BufferProps> = {
...Resource.defaultProps,
usage: 0, // Buffer.COPY_DST | Buffer.COPY_SRC
byteLength: 0,
byteOffset: 0,
data: null,
indexType: 'uint16',
mappedAtCreation: false
};

// Usage Flags
static MAP_READ = 0x01;
static MAP_WRITE = 0x02;
static COPY_SRC = 0x0004;
static COPY_DST = 0x0008;
/** Index buffer */
static INDEX = 0x0010;
/** Vertex buffer */
Expand All @@ -51,6 +36,12 @@ export abstract class Buffer extends Resource<BufferProps> {
static INDIRECT = 0x0100;
static QUERY_RESOLVE = 0x0200;

// Usage Flags
static MAP_READ = 0x01;
static MAP_WRITE = 0x02;
static COPY_SRC = 0x0004;
static COPY_DST = 0x0008;

override get [Symbol.toStringTag](): string {
return 'Buffer';
}
Expand Down Expand Up @@ -134,4 +125,14 @@ export abstract class Buffer extends Resource<BufferProps> {
this.debugData = arrayBuffer.slice(byteOffset, byteOffset + debugDataLength);
}
}

static override defaultProps: Required<BufferProps> = {
...Resource.defaultProps,
usage: 0, // Buffer.COPY_DST | Buffer.COPY_SRC
byteLength: 0,
byteOffset: 0,
data: null,
indexType: 'uint16',
mappedAtCreation: false
};
}
8 changes: 4 additions & 4 deletions modules/core/src/adapter/resources/command-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ export type CommandBufferProps = ResourceProps & {};
* Encodes commands to queue that can be executed later
*/
export abstract class CommandBuffer extends Resource<CommandBufferProps> {
static override defaultProps: Required<CommandBufferProps> = {
...Resource.defaultProps
};

override get [Symbol.toStringTag](): string {
return 'CommandBuffer';
}

constructor(device: Device, props: CommandBufferProps) {
super(device, props, CommandBuffer.defaultProps);
}

static override defaultProps: Required<CommandBufferProps> = {
...Resource.defaultProps
};
}
10 changes: 5 additions & 5 deletions modules/core/src/adapter/resources/command-encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,6 @@ export type CommandEncoderProps = ResourceProps & {
* Encodes commands to queue that can be executed later
*/
export abstract class CommandEncoder extends Resource<CommandEncoderProps> {
static override defaultProps: Required<CommandEncoderProps> = {
...Resource.defaultProps,
measureExecutionTime: undefined!
};

override get [Symbol.toStringTag](): string {
return 'CommandEncoder';
}
Expand Down Expand Up @@ -185,4 +180,9 @@ export abstract class CommandEncoder extends Resource<CommandEncoderProps> {
// TODO - luma.gl has these on the device, should we align with WebGPU API?
// beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
// beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;

static override defaultProps: Required<CommandEncoderProps> = {
...Resource.defaultProps,
measureExecutionTime: undefined!
};
}
22 changes: 11 additions & 11 deletions modules/core/src/adapter/resources/compute-pass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@ export type ComputePassProps = ResourceProps & {
};

export abstract class ComputePass extends Resource<ComputePassProps> {
static override defaultProps: Required<ComputePassProps> = {
...Resource.defaultProps,
timestampQuerySet: undefined!,
beginTimestampIndex: undefined!,
endTimestampIndex: undefined!
};

override get [Symbol.toStringTag](): string {
return 'ComputePass';
}

constructor(device: Device, props: ComputePassProps) {
super(device, props, ComputePass.defaultProps);
}
Expand Down Expand Up @@ -63,4 +52,15 @@ export abstract class ComputePass extends Resource<ComputePassProps> {
abstract popDebugGroup(): void;
/** Marks a point in a stream of commands with a label */
abstract insertDebugMarker(markerLabel: string): void;

static override defaultProps: Required<ComputePassProps> = {
...Resource.defaultProps,
timestampQuerySet: undefined!,
beginTimestampIndex: undefined!,
endTimestampIndex: undefined!
};

override get [Symbol.toStringTag](): string {
return 'ComputePass';
}
}
16 changes: 8 additions & 8 deletions modules/core/src/adapter/resources/compute-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ export type ComputePipelineProps = ResourceProps & {
* A compiled and linked shader program for compute
*/
export abstract class ComputePipeline extends Resource<ComputePipelineProps> {
static override defaultProps: Required<ComputePipelineProps> = {
...Resource.defaultProps,
shader: undefined!,
entryPoint: undefined!,
constants: {},
shaderLayout: undefined!
};

override get [Symbol.toStringTag](): string {
return 'ComputePipeline';
}
Expand All @@ -52,4 +44,12 @@ export abstract class ComputePipeline extends Resource<ComputePipelineProps> {
* @todo Do we want to expose BindGroups in the API and remove this?
*/
abstract setBindings(bindings: Record<string, Binding>): void;

static override defaultProps: Required<ComputePipelineProps> = {
...Resource.defaultProps,
shader: undefined!,
entryPoint: undefined!,
constants: {},
shaderLayout: undefined!
};
}
12 changes: 6 additions & 6 deletions modules/core/src/adapter/resources/external-texture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ export type ExternalTextureProps = ResourceProps & {
colorSpace?: 'srgb';
};
export abstract class ExternalTexture extends Resource<ExternalTextureProps> {
static override defaultProps: Required<ExternalTextureProps> = {
...Resource.defaultProps,
source: undefined!,
colorSpace: 'srgb'
};

override get [Symbol.toStringTag](): string {
return 'ExternalTexture';
}

constructor(device: Device, props: ExternalTextureProps) {
super(device, props, ExternalTexture.defaultProps);
}

static override defaultProps: Required<ExternalTextureProps> = {
...Resource.defaultProps,
source: undefined!,
colorSpace: 'srgb'
};
}
16 changes: 8 additions & 8 deletions modules/core/src/adapter/resources/framebuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ export type FramebufferProps = ResourceProps & {
* @note resize() destroys existing textures (if size has changed).
*/
export abstract class Framebuffer extends Resource<FramebufferProps> {
static override defaultProps: Required<FramebufferProps> = {
...Resource.defaultProps,
width: 1,
height: 1,
colorAttachments: [], // ['rgba8unorm'],
depthStencilAttachment: null // 'depth24plus-stencil8'
};

override get [Symbol.toStringTag](): string {
return 'Framebuffer';
}
Expand Down Expand Up @@ -182,4 +174,12 @@ export abstract class Framebuffer extends Resource<FramebufferProps> {

/** Implementation is expected to update any underlying binding (WebGL framebuffer attachment) */
protected abstract updateAttachments(): void;

static override defaultProps: Required<FramebufferProps> = {
...Resource.defaultProps,
width: 1,
height: 1,
colorAttachments: [], // ['rgba8unorm'],
depthStencilAttachment: null // 'depth24plus-stencil8'
};
}
Loading

0 comments on commit 68d8878

Please sign in to comment.