136 lines
3.8 KiB
TypeScript
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;
|
|
}
|
|
}
|