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

WebGPU: Add labels to buffers + support non float vertex buffers #14397

Merged
merged 5 commits into from Oct 6, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/dev/core/src/Animations/animationGroup.ts
Expand Up @@ -1007,7 +1007,7 @@ export class AnimationGroup implements IDisposable {
/**
* Convert the keyframes for all animations belonging to the group to be relative to a given reference frame.
* @param sourceAnimationGroup defines the AnimationGroup containing animations to convert
* @param options defines the options to use when converting ey keyframes
* @param options defines the options to use when converting keyframes
* @returns a new AnimationGroup if options.cloneOriginalAnimationGroup is true or the original AnimationGroup if options.cloneOriginalAnimationGroup is false
*/
public static MakeAnimationAdditive(sourceAnimationGroup: AnimationGroup, options?: IMakeAnimationGroupAdditiveOptions): AnimationGroup;
Expand Down
116 changes: 111 additions & 5 deletions packages/dev/core/src/Buffers/buffer.ts
Expand Up @@ -16,6 +16,7 @@ export class Buffer {
private _divisor: number;
private _isAlreadyOwned = false;
private _isDisposed = false;
private _label?: string;

/**
* Gets a boolean indicating if the Buffer is disposed
Expand All @@ -39,6 +40,7 @@ export class Buffer {
* @param instanced whether the buffer is instanced (optional)
* @param useBytes set to true if the stride in in bytes (optional)
* @param divisor sets an optional divisor for instances (1 by default)
* @param label defines the label of the buffer (for debug purpose)
*/
constructor(
engine: ThinEngine,
Expand All @@ -48,7 +50,8 @@ export class Buffer {
postponeInternalCreation = !engine,
instanced = false,
useBytes = false,
divisor?: number
divisor?: number,
label?: string
) {
if (engine && (engine as unknown as Mesh).getScene) {
// old versions of VertexBuffer accepted 'mesh' instead of 'engine'
Expand All @@ -60,6 +63,7 @@ export class Buffer {
this._updatable = updatable;
this._instanced = instanced;
this._divisor = divisor || 1;
this._label = label;

if (data instanceof DataBuffer) {
this._data = null;
Expand Down Expand Up @@ -167,10 +171,10 @@ export class Buffer {
if (!this._buffer) {
// create buffer
if (this._updatable) {
this._buffer = this._engine.createDynamicVertexBuffer(data);
this._buffer = this._engine.createDynamicVertexBuffer(data, this._label);
this._data = data;
} else {
this._buffer = this._engine.createVertexBuffer(data);
this._buffer = this._engine.createVertexBuffer(data, undefined, this._label);
}
} else if (this._updatable) {
// update buffer
Expand Down Expand Up @@ -254,6 +258,60 @@ export class Buffer {
}
}

/**
* Options to be used when creating a vertex buffer
*/
export interface IVertexBufferOptions {
/**
* whether the data is updatable (default: false)
*/
updatable?: boolean;
/**
* whether to postpone creating the internal WebGL buffer (default: false)
*/
postponeInternalCreation?: boolean;
/**
* the stride (will be automatically computed from the kind parameter if not specified)
*/
stride?: number;
/**
* whether the buffer is instanced (default: false)
*/
instanced?: boolean;
/**
* the offset of the data (default: 0)
*/
offset?: number;
/**
* the number of components (will be automatically computed from the kind parameter if not specified)
*/
size?: number;
/**
* the type of the component (will be deduce from the data parameter if not specified)
*/
type?: number;
/**
* whether the data contains normalized data (default: false)
*/
normalized?: boolean;
/**
* set to true if stride and offset are in bytes (default: false)
*/
useBytes?: boolean;
/**
* defines the instance divisor to use (default: 1, only used if instanced is true)
*/
divisor?: number;
/**
* defines if the buffer should be released when the vertex buffer is disposed (default: false)
*/
takeBufferOwnership?: boolean;
/**
* label to use for this vertex buffer (debugging purpose)
*/
label?: string;
}

/**
* Specialized buffer used to store vertex data
*/
Expand Down Expand Up @@ -389,23 +447,70 @@ export class VertexBuffer {
offset?: number,
size?: number,
type?: number,
normalized?: boolean,
useBytes?: boolean,
divisor?: number,
takeBufferOwnership?: boolean
);

/**
* Constructor
* @param engine the engine
* @param data the data to use for this vertex buffer
* @param kind the vertex buffer kind
* @param options defines the rest of the options used to create the buffer
*/
constructor(engine: ThinEngine, data: DataArray | Buffer | DataBuffer, kind: string, options?: IVertexBufferOptions);

/** @internal */
constructor(
engine: ThinEngine,
data: DataArray | Buffer | DataBuffer,
kind: string,
updatableOrOptions?: boolean | IVertexBufferOptions,
postponeInternalCreation?: boolean,
stride?: number,
instanced?: boolean,
offset?: number,
size?: number,
type?: number,
normalized = false,
useBytes = false,
divisor = 1,
takeBufferOwnership = false
) {
let updatable = false;
let label: string | undefined;

if (typeof updatableOrOptions === "object" && updatableOrOptions !== null) {
updatable = updatableOrOptions.updatable ?? false;
postponeInternalCreation = updatableOrOptions.postponeInternalCreation;
stride = updatableOrOptions.stride;
instanced = updatableOrOptions.instanced;
offset = updatableOrOptions.offset;
size = updatableOrOptions.size;
type = updatableOrOptions.type;
normalized = updatableOrOptions.normalized ?? false;
useBytes = updatableOrOptions.useBytes ?? false;
divisor = updatableOrOptions.divisor ?? 1;
takeBufferOwnership = updatableOrOptions.takeBufferOwnership ?? false;
label = updatableOrOptions.label;
} else {
updatable = !!updatableOrOptions;
}

if (data instanceof Buffer) {
this._buffer = data;
this._ownsBuffer = takeBufferOwnership;
} else {
this._buffer = new Buffer(engine, data, updatable, stride, postponeInternalCreation, instanced, useBytes);
this._buffer = new Buffer(engine, data, updatable, stride, postponeInternalCreation, instanced, useBytes, divisor, label);
this._ownsBuffer = true;
}

this.uniqueId = VertexBuffer._Counter++;
this._kind = kind;

if (type == undefined) {
if (type === undefined) {
const vertexData = this.getData();
this.type = vertexData ? VertexBuffer.GetDataType(vertexData) : VertexBuffer.FLOAT;
} else {
Expand Down Expand Up @@ -675,6 +780,7 @@ export class VertexBuffer {
case VertexBuffer.PositionKind:
return 3;
case VertexBuffer.ColorKind:
case VertexBuffer.ColorInstanceKind:
case VertexBuffer.MatricesIndicesKind:
case VertexBuffer.MatricesIndicesExtraKind:
case VertexBuffer.MatricesWeightsKind:
Expand Down
7 changes: 5 additions & 2 deletions packages/dev/core/src/Buffers/storageBuffer.ts
Expand Up @@ -11,23 +11,26 @@ export class StorageBuffer {
private _buffer: DataBuffer;
private _bufferSize: number;
private _creationFlags: number;
private _label?: string;

/**
* Creates a new storage buffer instance
* @param engine The engine the buffer will be created inside
* @param size The size of the buffer in bytes
* @param creationFlags flags to use when creating the buffer (see Constants.BUFFER_CREATIONFLAG_XXX). The BUFFER_CREATIONFLAG_STORAGE flag will be automatically added.
* @param label defines the label of the buffer (for debug purpose)
*/
constructor(engine: ThinEngine, size: number, creationFlags = Constants.BUFFER_CREATIONFLAG_READWRITE) {
constructor(engine: ThinEngine, size: number, creationFlags = Constants.BUFFER_CREATIONFLAG_READWRITE, label?: string) {
this._engine = engine;
this._label = label;
this._engine._storageBuffers.push(this);
this._create(size, creationFlags);
}

private _create(size: number, creationFlags: number): void {
this._bufferSize = size;
this._creationFlags = creationFlags;
this._buffer = this._engine.createStorageBuffer(size, creationFlags);
this._buffer = this._engine.createStorageBuffer(size, creationFlags, this._label);
}

/** @internal */
Expand Down
Expand Up @@ -10,9 +10,10 @@ declare module "../../Engines/thinEngine" {
* Creates a storage buffer
* @param data the data for the storage buffer or the size of the buffer
* @param creationFlags flags to use when creating the buffer (see Constants.BUFFER_CREATIONFLAG_XXX). The BUFFER_CREATIONFLAG_STORAGE flag will be automatically added
* @param label defines the label of the buffer (for debug purpose)
* @returns the new buffer
*/
createStorageBuffer(data: DataArray | number, creationFlags: number): DataBuffer;
createStorageBuffer(data: DataArray | number, creationFlags: number, label?: string): DataBuffer;

/**
* Updates a storage buffer
Expand Down
10 changes: 6 additions & 4 deletions packages/dev/core/src/Engines/Extensions/engine.uniformBuffer.ts
Expand Up @@ -11,17 +11,19 @@ declare module "../../Engines/thinEngine" {
* Create an uniform buffer
* @see https://doc.babylonjs.com/setup/support/webGL2#uniform-buffer-objets
* @param elements defines the content of the uniform buffer
* @param label defines a name for the buffer (for debugging purpose)
* @returns the webGL uniform buffer
*/
createUniformBuffer(elements: FloatArray): DataBuffer;
createUniformBuffer(elements: FloatArray, label?: string): DataBuffer;

/**
* Create a dynamic uniform buffer
* @see https://doc.babylonjs.com/setup/support/webGL2#uniform-buffer-objets
* @param elements defines the content of the uniform buffer
* @param label defines a name for the buffer (for debugging purpose)
* @returns the webGL uniform buffer
*/
createDynamicUniformBuffer(elements: FloatArray): DataBuffer;
createDynamicUniformBuffer(elements: FloatArray, label?: string): DataBuffer;

/**
* Update an existing uniform buffer
Expand Down Expand Up @@ -57,7 +59,7 @@ declare module "../../Engines/thinEngine" {
}
}

ThinEngine.prototype.createUniformBuffer = function (elements: FloatArray): DataBuffer {
ThinEngine.prototype.createUniformBuffer = function (elements: FloatArray, _label?: string): DataBuffer {
const ubo = this._gl.createBuffer();

if (!ubo) {
Expand All @@ -79,7 +81,7 @@ ThinEngine.prototype.createUniformBuffer = function (elements: FloatArray): Data
return result;
};

ThinEngine.prototype.createDynamicUniformBuffer = function (elements: FloatArray): DataBuffer {
ThinEngine.prototype.createDynamicUniformBuffer = function (elements: FloatArray, _label?: string): DataBuffer {
const ubo = this._gl.createBuffer();

if (!ubo) {
Expand Down
Expand Up @@ -14,17 +14,18 @@ declare module "../../../Materials/effect" {
* Sets a storage buffer on the engine to be used in the shader.
* @param name Name of the storage buffer variable.
* @param buffer Storage buffer to set.
* @param label defines the label of the buffer (for debug purpose)
*/
setStorageBuffer(name: string, buffer: Nullable<StorageBuffer>): void;
setStorageBuffer(name: string, buffer: Nullable<StorageBuffer>, label?: string): void;
}
}

Effect.prototype.setStorageBuffer = function (name: string, buffer: Nullable<StorageBuffer>): void {
this._engine.setStorageBuffer(name, buffer);
};

WebGPUEngine.prototype.createStorageBuffer = function (data: DataArray | number, creationFlags: number): DataBuffer {
return this._createBuffer(data, creationFlags | Constants.BUFFER_CREATIONFLAG_STORAGE);
WebGPUEngine.prototype.createStorageBuffer = function (data: DataArray | number, creationFlags: number, label?: string): DataBuffer {
return this._createBuffer(data, creationFlags | Constants.BUFFER_CREATIONFLAG_STORAGE, label);
};

WebGPUEngine.prototype.updateStorageBuffer = function (buffer: DataBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
Expand Down Expand Up @@ -59,7 +60,7 @@ WebGPUEngine.prototype.updateStorageBuffer = function (buffer: DataBuffer, data:
WebGPUEngine.prototype.readFromStorageBuffer = function (storageBuffer: DataBuffer, offset?: number, size?: number, buffer?: ArrayBufferView): Promise<ArrayBufferView> {
size = size || storageBuffer.capacity;

const gpuBuffer = this._bufferManager.createRawBuffer(size, WebGPUConstants.BufferUsage.MapRead | WebGPUConstants.BufferUsage.CopyDst);
const gpuBuffer = this._bufferManager.createRawBuffer(size, WebGPUConstants.BufferUsage.MapRead | WebGPUConstants.BufferUsage.CopyDst, undefined, "TempReadFromStorageBuffer");

this._renderTargetEncoder.copyBufferToBuffer(storageBuffer.underlyingResource, offset ?? 0, gpuBuffer, 0, size);

Expand Down
Expand Up @@ -4,20 +4,20 @@ import type { FloatArray } from "../../../types";
import { WebGPUEngine } from "../../webgpuEngine";
import * as WebGPUConstants from "../webgpuConstants";

WebGPUEngine.prototype.createUniformBuffer = function (elements: FloatArray): DataBuffer {
WebGPUEngine.prototype.createUniformBuffer = function (elements: FloatArray, label?: string): DataBuffer {
let view: Float32Array;
if (elements instanceof Array) {
view = new Float32Array(elements);
} else {
view = elements;
}

const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst);
const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst, label);
return dataBuffer;
};

WebGPUEngine.prototype.createDynamicUniformBuffer = function (elements: FloatArray): DataBuffer {
return this.createUniformBuffer(elements);
WebGPUEngine.prototype.createDynamicUniformBuffer = function (elements: FloatArray, label?: string): DataBuffer {
return this.createUniformBuffer(elements, label);
};

WebGPUEngine.prototype.updateUniformBuffer = function (uniformBuffer: DataBuffer, elements: FloatArray, offset?: number, count?: number): void {
Expand Down Expand Up @@ -45,7 +45,7 @@ WebGPUEngine.prototype.updateUniformBuffer = function (uniformBuffer: DataBuffer
this._bufferManager.setSubData(dataBuffer, offset, view, 0, count);
};

WebGPUEngine.prototype.bindUniformBufferBase = function (buffer: DataBuffer, location: number, name: string): void {
WebGPUEngine.prototype.bindUniformBufferBase = function (buffer: DataBuffer, _location: number, name: string): void {
this._currentDrawContext.setBuffer(name, buffer as WebGPUDataBuffer);
};

Expand Down
22 changes: 19 additions & 3 deletions packages/dev/core/src/Engines/WebGPU/webgpuBufferManager.ts
Expand Up @@ -15,13 +15,29 @@ export class WebGPUBufferManager {
return (buffer as DataBuffer).underlyingResource === undefined;
}

private static _FlagsToString(flags: GPUBufferUsageFlags, suffix = "") {
let result = suffix;

for (let i = 0; i <= 9; ++i) {
if (flags & (1 << i)) {
if (result) {
result += "_";
}
result += WebGPUConstants.BufferUsage[1 << i];
}
}

return result;
}

constructor(device: GPUDevice) {
this._device = device;
}

public createRawBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags, mappedAtCreation = false): GPUBuffer {
public createRawBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags, mappedAtCreation = false, label?: string): GPUBuffer {
const alignedLength = (viewOrSize as ArrayBufferView).byteLength !== undefined ? ((viewOrSize as ArrayBufferView).byteLength + 3) & ~3 : ((viewOrSize as number) + 3) & ~3; // 4 bytes alignments (because of the upload which requires this)
const verticesBufferDescriptor = {
label: WebGPUBufferManager._FlagsToString(flags, label ?? "Buffer") + "_size" + alignedLength,
mappedAtCreation,
size: alignedLength,
usage: flags,
Expand All @@ -30,9 +46,9 @@ export class WebGPUBufferManager {
return this._device.createBuffer(verticesBufferDescriptor);
}

public createBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags): WebGPUDataBuffer {
public createBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags, label?: string): WebGPUDataBuffer {
const isView = (viewOrSize as ArrayBufferView).byteLength !== undefined;
const buffer = this.createRawBuffer(viewOrSize, flags);
const buffer = this.createRawBuffer(viewOrSize, flags, undefined, label);
const dataBuffer = new WebGPUDataBuffer(buffer);
dataBuffer.references = 1;
dataBuffer.capacity = isView ? (viewOrSize as ArrayBufferView).byteLength : (viewOrSize as number);
Expand Down