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: Consolidate texture implementations, move setters to AsyncTexture (v9.2) #2247

Merged
merged 1 commit into from
Sep 7, 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
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
Loading