oktaeder/src/resources/IndexBuffer.ts

136 lines
3.8 KiB
TypeScript

/*!
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Renderer } from "../oktaeder";
export interface IndexBufferProps {
readonly name?: string;
readonly indexFormat: "uint16" | "uint32";
readonly indexCount: number;
}
export interface IndexBufferResizeProps {
readonly indexFormat?: "uint16" | "uint32";
readonly indexCount?: number;
}
export class IndexBuffer {
readonly type!: "IndexBuffer";
_renderer: Renderer;
_name: string;
_buffer: GPUBuffer;
_indexFormat: "uint16" | "uint32";
constructor(renderer: Renderer, {
name = "",
indexFormat,
indexCount,
}: IndexBufferProps) {
this._renderer = renderer;
this._name = name;
this._buffer = renderer._device.createBuffer({
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
size: indexCount * indexSize(indexFormat),
label: name,
});
this._indexFormat = indexFormat;
}
/**
* Destroys owned GPU resources. The index buffer should not be used after
* calling this method.
* @returns `this` for chaining
*/
dispose(): IndexBuffer {
this._buffer.destroy();
return this;
}
get indexCount(): number {
return this._buffer.size / indexSize(this._indexFormat) | 0;
}
writeArray(offset: number, indices: readonly number[]): IndexBuffer {
const array = this._indexFormat === "uint16" ? new Uint16Array(indices) : new Uint32Array(indices);
return this.writeTypedArray(offset, array);
}
writeTypedArray(offset: number, indices: Uint16Array | Uint32Array): IndexBuffer {
if (
this._indexFormat === "uint16" && !(indices instanceof Uint16Array)
|| this._indexFormat === "uint32" && !(indices instanceof Uint32Array)
) {
throw new Error(`Cannot write typed array to a mismatched index type. Typed array is of type ${indices.constructor.name}. Index buffer [${this._name}] uses format ${this._indexFormat}`);
}
this._renderer._device.queue.writeBuffer(this._buffer, offset * indexSize(this._indexFormat) | 0, indices);
return this;
}
/**
* Resize the index buffer, discarding currently stored data.
* @param props Desired buffer properties. Any unspecified property will
* stay unchanged.
* @returns `this` for chaining
*/
resizeDiscard({
indexFormat = this._indexFormat,
indexCount = this.indexCount,
}: IndexBufferResizeProps): IndexBuffer {
this._buffer.destroy();
this._buffer = this._renderer._device.createBuffer({
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
size: indexCount * indexSize(indexFormat),
label: this._name,
});
this._indexFormat = indexFormat;
return this;
}
/**
* Resize the index buffer if it can't hold provided number of indices or
* its index format is smaller than provided, potentially discarding
* currently stored data.
* @param props Desired buffer properties. Any unspecified property will
* be ignored.
* @returns `this` for chaining
*/
ensureSizeDiscard({
indexFormat = this._indexFormat,
indexCount = this.indexCount,
}: IndexBufferResizeProps): IndexBuffer {
if (this.indexCount >= indexCount && indexSize(this._indexFormat) >= indexSize(indexFormat)) {
return this;
}
return this.resizeDiscard({
indexFormat,
indexCount,
});
}
get indexFormat(): "uint16" | "uint32" { return this._indexFormat; }
get indexSize(): number { return indexSize(this._indexFormat); }
}
Object.defineProperty(IndexBuffer.prototype, "type", { value: "IndexBuffer" });
export function isIndexBuffer(value: unknown): value is IndexBuffer {
return Boolean(value) && (value as IndexBuffer).type === "IndexBuffer";
}
function indexSize(indexFormat: "uint16" | "uint32"): number {
switch (indexFormat) {
case "uint16": return 2;
case "uint32": return 4;
}
}