webgpu-pipeline-kit
Version:
A type-safe declarative way of creating WebGPU pipelines
1,543 lines (1,520 loc) • 92.1 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
factories: () => factories,
setLogLevel: () => setLogLevel
});
module.exports = __toCommonJS(src_exports);
// src/logging.ts
var import_loglevel = __toESM(require("loglevel"));
var prefix = "webgpu-pipeline-kit";
var getLogger = (namespace) => import_loglevel.default.getLogger(`${prefix}:${namespace}`);
var lazyWarn = (logger, warnMessageFunc) => {
if (logger.getLevel() <= logger.levels.WARN) {
logger.warn(warnMessageFunc());
}
};
var lazyInfo = (logger, infoMessageFunc) => {
if (logger.getLevel() <= logger.levels.INFO) {
logger.info(infoMessageFunc());
}
};
var lazyDebug = (logger, debugMessageFunc) => {
if (logger.getLevel() <= logger.levels.DEBUG) {
logger.debug(debugMessageFunc());
}
};
var lazyTrace = (logger, traceMessageFunc) => {
if (logger.getLevel() <= logger.levels.TRACE) {
logger.trace(traceMessageFunc());
}
};
var setLogLevel = (level, namespace) => {
if (namespace !== void 0) {
getLogger(namespace).setLevel(level);
} else {
const allLoggers = import_loglevel.default.getLoggers();
for (const [name, logger] of Object.entries(allLoggers)) {
if (name.startsWith(prefix)) {
logger.setLevel(level);
}
}
}
};
// src/utils/compare.ts
var equalityFactory = {
ofDoubleEquals: () => (a, b) => a == b,
ofTripleEquals: () => (a, b) => a === b
};
// src/utils/array.ts
var arrayFuncs = {
equals: (a, b, equality = equalityFactory.ofTripleEquals()) => {
if (a.length !== b.length) {
return false;
}
return a.every((val, i) => equality(val, b[i]));
},
shuffle: (array, random2) => {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = random2.intMinMax(0, i);
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
},
merge: (...arrays) => arrays.flatMap((arr) => arr ?? []),
toRecord: (array, toKey, toValue) => {
return array.reduce((acc, element) => {
const key = toKey(element);
const value = toValue(element);
acc[key] = value;
return acc;
}, {});
},
toMap: (array, toKey, toValue) => {
return array.reduce((acc, element, index) => {
const key = toKey(element, index);
const value = toValue(element, index);
acc.set(key, value);
return acc;
}, /* @__PURE__ */ new Map());
}
};
// src/utils/BidiMap.ts
var BidiMap = class {
constructor() {
this.keyValues = /* @__PURE__ */ new Map();
this.valueKeys = /* @__PURE__ */ new Map();
}
get size() {
return this.keyValues.size;
}
clear() {
this.keyValues.clear();
this.valueKeys.clear();
}
entries() {
return this.keyValues.entries();
}
forEach(callbackFn) {
for (const [key, value] of this.keyValues) {
callbackFn(value, key, this);
}
}
delete(key) {
const hasKey = this.keyValues.has(key);
if (hasKey) {
const value = this.keyValues.get(key);
this.keyValues.delete(key);
if (value !== void 0) {
this.valueKeys.delete(value);
}
}
return hasKey;
}
deleteValue(value) {
const hasValue = this.valueKeys.has(value);
if (hasValue) {
const key = this.valueKeys.get(value);
this.valueKeys.delete(value);
if (key !== void 0) {
this.keyValues.delete(key);
}
}
return hasValue;
}
get(key) {
return this.keyValues.get(key);
}
getKey(value) {
return this.valueKeys.get(value);
}
set(key, value, removeDuplicateValue) {
if (removeDuplicateValue || !this.valueKeys.has(value)) {
this.keyValues.set(key, value);
this.valueKeys.set(value, key);
}
return this;
}
has(key) {
return this.keyValues.has(key);
}
hasValue(value) {
return this.valueKeys.has(value);
}
keys() {
return this.keyValues.keys();
}
values() {
return this.valueKeys.keys();
}
};
// src/utils/Capacity.ts
var Capacity = class {
constructor(min, requiredMultiple, capacityMultiple) {
this.min = min;
this.requiredMultiple = requiredMultiple;
this.capacityMultiple = capacityMultiple;
this._capacity = 0;
this._capacity = min;
}
get capacity() {
return this._capacity;
}
set capacity(capacity) {
this._capacity = capacity;
}
ensureCapacity(required) {
const oldCapacity = this._capacity;
const hasIncreased = required > this._capacity;
if (hasIncreased) {
this._capacity = Math.round(
Math.max(
this.min,
required * this.requiredMultiple,
oldCapacity * this.capacityMultiple
)
);
}
return hasIncreased;
}
};
// src/utils/changeDetector.ts
var changeDetectorFactory = {
of: (first, equals) => {
let current = first;
return {
get() {
return current;
},
compareAndUpdate(next) {
const hasChanged = current === void 0 || !equals(current, next);
current = next;
return hasChanged;
}
};
},
ofDoubleEquals: (first) => {
return changeDetectorFactory.of(first, (a, b) => a == b);
},
ofTripleEquals: (first) => {
return changeDetectorFactory.of(first, (a, b) => a === b);
}
};
// src/utils/Color.ts
var colorFactory = {
BLACK: { r: 0, g: 0, b: 0, a: 1 },
RED: { r: 1, g: 0, b: 0, a: 1 },
GREEN: { r: 0, g: 1, b: 0, a: 1 },
BLUE: { r: 0, g: 0, b: 1, a: 1 },
WHITE: { r: 1, g: 1, b: 1, a: 1 },
of(r = 0, g = 0, b = 0, a = 1) {
return {
r,
g,
b,
a
};
}
};
// src/utils/floats.ts
var floatFuncs = {
float32ToFloat16: (val) => {
const floatView = new Float32Array(1);
const intView = new Uint32Array(floatView.buffer);
floatView[0] = val;
const x = intView[0];
const sign = x >>> 31 << 15;
const exponent = (x >> 23 & 255) - 127 + 15;
const mantissa = x & 8388607;
if (exponent <= 0) {
if (exponent < -10) {
return sign;
}
const m = (mantissa | 8388608) >> 1 - exponent;
return sign | m + 4096 >> 13;
} else if (exponent === 255 - 127 + 15) {
if (mantissa === 0) {
return sign | 31744;
}
return sign | 31744 | (mantissa >> 13 || 1);
} else if (exponent >= 31) {
return sign | 31744;
}
return sign | exponent << 10 | mantissa + 4096 >> 13;
}
};
// src/utils/functions.ts
var callCreatorOf = () => (methodName) => (target, ...args) => target[methodName](...args);
// src/utils/math.ts
var mathFuncs = {
HALF_PI: 0.5 * Math.PI,
TWO_PI: 2 * Math.PI,
clamp: (value, min, max) => {
return Math.max(min, Math.min(max, value));
},
gcd: (...numbers) => {
if (numbers.length === 0) {
throw new Error("Array must contain at least one number.");
}
const gcdAB = (a, b) => {
while (b !== 0) {
const temp = b;
b = a % b;
a = temp;
}
return Math.abs(a);
};
return numbers.reduce((acc, num) => gcdAB(acc, num));
},
geometricMean: (...elements) => {
const product = elements.reduce((total, value) => total * value, 1);
return Math.pow(product, 1 / elements.length);
},
harmonicMean: (...elements) => {
const sumReciprocals = elements.reduce((total, value) => total + 1 / value, 0);
return elements.length / sumReciprocals;
},
sumMapValues: (map) => Array.from(map.values()).reduce((sum, value) => sum + value, 0),
nextMultipleOf: (num, multiple) => Math.ceil(num / multiple) * multiple
};
// src/utils/random.ts
var import_random = require("@thi.ng/random");
var randomFactory = {
ofSeed: (seed) => {
const xoshiro128 = new import_random.Xoshiro128([seed.a, seed.b, seed.c, seed.d]);
return createRandomFromXoshiro128(xoshiro128);
},
ofString: (str) => {
const seed = createSeedFromString(str);
const xoshiro128 = new import_random.Xoshiro128([seed.a, seed.b, seed.c, seed.d]);
return createRandomFromXoshiro128(xoshiro128);
}
};
var createSeedFromString = (str) => {
const length2 = str.length;
if (length2 < 4) {
throw Error(`Seed must have length >= 4 but is ${length2}`);
}
const a = createSeedElement(str, 0, 0);
const b = createSeedElement(str, 0, Math.round(0.25 * length2));
const c = createSeedElement(str, 0, Math.round(0.5 * length2));
const d = createSeedElement(str, 0, Math.round(0.75 * length2));
return { a, b, c, d };
};
var createSeedElement = (seed, hashStart, index) => {
const hashMiddle = hashChars(seed, hashStart, index, seed.length);
const hashEnd = hashChars(seed, hashMiddle, 0, index);
return hashEnd >>> 0;
};
var hashChars = (seed, hash, startIndex, endIndex) => {
for (let i = startIndex; i < endIndex; i++) {
hash = Math.imul(hash ^ seed.charCodeAt(i), 1540483477);
hash = hash ^ hash >> 13;
}
return hash;
};
var createRandomFromXoshiro128 = (xoshiro128) => {
return {
getSeed() {
const buffer = xoshiro128.buffer;
return {
a: buffer[0],
b: buffer[1],
c: buffer[2],
d: buffer[3]
};
},
boolean() {
return this.true(0.5);
},
false(proportion) {
return this.true(1 - proportion);
},
true(proportion) {
return xoshiro128.float() < proportion;
},
int() {
return xoshiro128.int();
},
intRange(range) {
return this.intMinMax(range.min, range.max);
},
intMirrorZero(minMax) {
const abs = Math.abs(minMax);
return this.intMinMax(-abs, abs);
},
intMinMax(min, max) {
return xoshiro128.minmaxInt(min, max + 1);
},
float() {
return xoshiro128.float();
},
floatRange(range) {
return this.floatMinMax(range.min, range.max);
},
floatMirrorZero(minMax) {
const abs = Math.abs(minMax);
return this.floatMinMax(-abs, abs);
},
floatMinMax(min, max) {
return xoshiro128.minmax(min, max);
},
lowerCharCode() {
return this.intMinMax(97, 123);
},
upperCharCode() {
return this.intMinMax(65, 91);
},
uuidV4Like() {
const hex = [];
for (let i = 0; i < 4; i++) {
const r = this.int() >>> 0;
hex.push(r.toString(16).padStart(8, "0"));
}
return hex[0] + "-" + hex[1].slice(0, 4) + "-4" + hex[1].slice(5, 8) + "-" + (parseInt(hex[2][0], 16) & 3 | 8).toString(16) + hex[2].slice(1, 4) + "-" + hex[2].slice(4) + hex[3];
},
word() {
const min = this.intMinMax(2, 5);
const max = this.intMinMax(5, 8);
return this.wordMinMax(min, max);
},
wordRange(range) {
return this.wordMinMax(range.min, range.max);
},
wordMinMax(min, max) {
const charCodes = [];
charCodes.push(this.upperCharCode());
const subsequent = xoshiro128.minmaxUint(min, max);
for (let i = 1; i <= subsequent; i++) {
charCodes.push(this.lowerCharCode());
}
return String.fromCharCode(...charCodes);
}
};
};
// src/utils/record.ts
var recordFuncs = {
filter: (record, predicate) => {
return Object.fromEntries(
Object.entries(record).filter(
([key, value]) => predicate(value, key)
)
);
},
forEach: (record, apply) => {
Object.entries(record).map(([key, value]) => [key, apply(value, key)]);
},
mapRecord: (record, transform) => {
return Object.fromEntries(
Object.entries(record).map(([key, value]) => [key, transform(value, key)])
);
},
toMap: (record, transform) => {
return new Map(Object.entries(record).map(([key, value]) => [key, transform(value, key)]));
},
toArray: (record, transform) => {
return Object.entries(record).map(([key, value]) => transform(value, key));
}
};
// src/utils/slice.ts
var sliceFuncs = {
ofInts: (nums) => {
const slices = [];
if (nums.length === 0) {
return slices;
}
const sorted = nums.sort((a, b) => a - b);
let min = sorted[0];
let max = min;
for (let i = 1; i < sorted.length; i++) {
const num = sorted[i];
if (!Number.isInteger(num)) {
throw Error(`Cannot create slices with non-integer number ${num}`);
}
if (num === max || num === max + 1) {
max = num;
} else {
slices.push({ min, length: max + 1 - min });
min = max = num;
}
}
slices.push({ min, length: max + 1 - min });
return slices;
},
ofMap: (map) => {
const sortedEntries = Array.from(map.entries()).sort(([indexA], [indexB]) => indexA - indexB);
const values = sortedEntries.map(([, value]) => value);
const indexes = sortedEntries.map(([index]) => index);
const slices = sliceFuncs.ofInts(indexes);
const copySlices = Array.from({ length: slices.length });
let fromIndex = 0;
for (const [sliceIndex, slice] of slices.entries()) {
const { length: length2, min } = slice;
copySlices[sliceIndex] = {
min: fromIndex,
length: length2,
toIndex: min
};
fromIndex += length2;
}
return {
copySlices,
values
};
}
};
// src/utils/string.ts
var stringFuncs = {
canBePositiveInt: (str) => "0" === str || /^[1-9]\d*$/.test(str)
};
// src/utils/Vec3.ts
var vec3Funcs = {
ZERO: [0, 0, 0],
ONE: [1, 1, 1],
X: [1, 0, 0],
Y: [0, 1, 0],
Z: [0, 0, 1],
of(x = 0, y = 0, z = 0) {
return [x, y, z];
},
add(a, b) {
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
},
subtract(a, b) {
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
},
cross(a, b) {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
const b0 = b[0];
const b1 = b[1];
const b2 = b[2];
return [
a1 * b2 - a2 * b1,
a2 * b0 - a0 * b2,
a0 * b1 - a1 * b0
];
},
dot(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
},
distance(a, b) {
return vec3Funcs.length(vec3Funcs.subtract(a, b));
},
distanceSquared(a, b) {
return vec3Funcs.lengthSquared(vec3Funcs.subtract(a, b));
},
length(v) {
return Math.hypot(v[0], v[1], v[2]);
},
lengthSquared(v) {
return v[0] ** 2 + v[1] ** 2 + v[2] ** 2;
},
lerp(a, b, t) {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
return [
a0 + (b[0] - a0) * t,
a1 + (b[1] - a1) * t,
a2 + (b[2] - a2) * t
];
},
midpoint(a, b) {
return vec3Funcs.normalize([
(a[0] + b[0]) / 2,
(a[1] + b[1]) / 2,
(a[2] + b[2]) / 2
]);
},
negate(v) {
return [-v[0], -v[1], -v[2]];
},
normalize(v) {
const len = vec3Funcs.length(v);
return len === 0 ? [0, 0, 0] : vec3Funcs.scale(v, 1 / len);
},
scale(v, scalar) {
return [v[0] * scalar, v[1] * scalar, v[2] * scalar];
},
equals(a, b, epsilon = 1e-6) {
return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon && Math.abs(a[2] - b[2]) < epsilon;
},
toString(v) {
return `[${v[0].toFixed(3)}, ${v[1].toFixed(3)}, ${v[2].toFixed(3)}]`;
}
};
// src/cache.ts
var LOGGER = getLogger("cache");
var random = randomFactory.ofString((/* @__PURE__ */ new Date()).toISOString());
var cacheFactory = {
ofUniform: (_uniformFormat, initialUniform, mutable) => {
lazyDebug(LOGGER, () => `Creating uniform cache from format ${JSON.stringify(_uniformFormat)}`);
let previous = initialUniform;
let next = initialUniform;
const cache = {
isMutable: mutable,
isDirty: () => previous !== next,
get() {
previous = next;
return next;
}
};
if (mutable) {
lazyTrace(LOGGER, () => "Making uniform cache mutable");
const typedCached = cache;
typedCached.mutate = (uniform) => next = uniform;
}
return cache;
},
ofEntitiesFixedSize: (_entityFormat, mutable, ...elements) => {
lazyDebug(LOGGER, () => `Creating fixed size entity cache from format ${JSON.stringify(_entityFormat)}`);
const mutated = /* @__PURE__ */ new Map();
elements.forEach((element, index) => mutated.set(index, element));
const cache = {
isMutable: mutable,
isResizeable: false,
count: () => elements.length,
isDirty: () => mutated.size > 0,
calculateChanges() {
const slices = sliceFuncs.ofMap(mutated);
mutated.clear();
lazyTrace(LOGGER, () => `Calculated entity cache changes ${JSON.stringify(slices)}`);
return slices;
}
};
if (mutable) {
lazyTrace(LOGGER, () => "Making entity cache mutable");
const typedCache = cache;
typedCache.mutate = (index, instance) => {
lazyTrace(LOGGER, () => `Mutating instance with index ${index}`);
mutated.set(index, instance);
};
}
return cache;
},
ofEntitiesResizeable: (_entityFormat, mutable) => {
lazyDebug(LOGGER, () => `Creating resizeable entity cache from format ${JSON.stringify(_entityFormat)}`);
const idIndexes = new BidiMap();
const backing = /* @__PURE__ */ new Map();
const added = /* @__PURE__ */ new Map();
const mutated = /* @__PURE__ */ new Map();
const removed = /* @__PURE__ */ new Set();
const cache = {
isMutable: mutable,
isResizeable: true,
count: () => backing.size,
isDirty: () => added.size > 0 || mutated.size > 0 || removed.size > 0,
calculateChanges() {
added.forEach((instance, id) => backing.set(id, instance));
mutated.forEach((instance, id) => backing.set(id, instance));
removed.forEach((id) => backing.delete(id));
const stagingIdInstances = [
...added,
...mutated
];
const stagingIds = stagingIdInstances.map(([id]) => id);
const stagingInstances = stagingIdInstances.map(([, instance]) => instance);
const copySlices = [];
const previousUsed = idIndexes.size;
const newUsed = previousUsed + added.size - removed.size;
const oldIds = [
...mutated.keys(),
...removed.keys()
];
const oldIndexes = oldIds.map((id) => idIndexes.get(id)).filter((index) => index !== void 0);
oldIndexes.forEach((oldIndex) => idIndexes.deleteValue(oldIndex));
const overwrittenIndexes = oldIndexes.filter((index) => index < newUsed);
const overwrittenSlices = sliceFuncs.ofInts(overwrittenIndexes);
let stagingIndexOffset = 0;
for (const overwrittenSlice of overwrittenSlices) {
const { min, length: length2 } = overwrittenSlice;
const copySlice = {
min: stagingIndexOffset,
length: length2
};
copySlices.push({
...copySlice,
toIndex: min
});
for (let i = 0; i < length2; i++) {
const newIndex = min + i;
const stagingIndex = stagingIndexOffset + i;
const stagingId = stagingIds[stagingIndex];
idIndexes.set(stagingId, newIndex, true);
}
stagingIndexOffset += length2;
}
if (newUsed > previousUsed) {
const appendCount = newUsed - previousUsed;
const appendSlice = {
min: stagingIndexOffset,
length
};
copySlices.push({
...appendSlice,
toIndex: previousUsed
});
for (let i = 0; i < appendCount; i++) {
const newIndex = previousUsed + i;
const stagingIndex = stagingIndexOffset + i;
const stagingId = stagingIds[stagingIndex];
idIndexes.set(stagingId, newIndex, true);
}
} else if (newUsed < previousUsed) {
const replacedIndexes = oldIndexes.filter((index) => index >= newUsed);
let movedIndex = previousUsed;
for (const replacedIndex of replacedIndexes) {
movedIndex--;
while (movedIndex > newUsed && replacedIndexes.includes(movedIndex)) {
movedIndex--;
}
if (movedIndex > replacedIndex) {
const movedId = idIndexes.getKey(movedIndex);
if (movedId !== void 0) {
const movedSlice = {
min: movedIndex,
length: 1
};
copySlices.push({
...movedSlice,
toIndex: replacedIndex
});
idIndexes.set(movedId, replacedIndex, true);
}
}
}
}
const command = {
values: stagingInstances,
copySlices
};
added.clear();
removed.clear();
mutated.clear();
lazyTrace(LOGGER, () => `Calculated entity cache changes ${JSON.stringify(command)}`);
return command;
},
add(element) {
const id = random.uuidV4Like();
lazyTrace(LOGGER, () => `Add element with id ${id}`);
added.set(id, element);
return id;
},
remove(id) {
lazyTrace(LOGGER, () => `Remove element with id ${id}`);
if (backing.has(id)) {
added.delete(id);
mutated.delete(id);
removed.add(id);
}
},
indexOf(id) {
const index = idIndexes.get(id);
const validIndex = index === void 0 ? -1 : index;
lazyTrace(LOGGER, () => `Index of id ${id} is ${validIndex}`);
return validIndex;
}
};
if (mutable) {
lazyTrace(LOGGER, () => "Making entity cache mutable");
const typedCache = cache;
typedCache.mutate = (id, instance) => {
lazyTrace(LOGGER, () => `Mutating instance with id ${id}`);
if (!removed.has(id)) {
if (backing.has(id)) {
mutated.set(id, instance);
} else if (added.has(id)) {
added.set(id, instance);
}
}
};
}
return cache;
}
};
// src/mesh.ts
var LOGGER2 = getLogger("mesh");
var meshFuncs = {
UINT32_INDEX_COUNT: 1 << 16,
VERTICES_PER_TRIANGLE: 3,
cullMode: (mesh) => mesh.winding === "cw" ? "front" : "back",
indicesType: (mesh) => mesh.indices.length < meshFuncs.UINT32_INDEX_COUNT ? "uint16" : "uint32",
indicesCount: (mesh) => mesh.indices.length * 3,
indicesBytesLength: (mesh) => {
const indicesCount = meshFuncs.indicesCount(mesh);
return indicesCount < meshFuncs.UINT32_INDEX_COUNT ? indicesCount * 2 : indicesCount * 4;
},
toIndicesData: (mesh) => {
const indicesType = meshFuncs.indicesType(mesh);
const flatIndices = mesh.indices.flat();
const TypedArrayConstructor = indicesType === "uint16" ? Uint16Array : Uint32Array;
const typedArray = new TypedArrayConstructor(flatIndices);
const byteLength = typedArray.byteLength;
if (byteLength % 4 === 0) {
return typedArray.buffer;
}
const paddedByteLength = mathFuncs.nextMultipleOf(byteLength, 4);
const buffer = new ArrayBuffer(paddedByteLength);
const targetView = new TypedArrayConstructor(buffer);
targetView.set(typedArray);
return buffer;
},
toVerticesData: (mesh) => {
const flatVertices = mesh.vertices.flat();
const verticesArray = new Float32Array(flatVertices);
return verticesArray.buffer;
}
};
var meshFactory = {
of: (type, vertices, indices, winding) => {
return {
topology: type,
vertices,
indices,
winding
};
},
triangle: (topProportion, axis = vec3Funcs.Z) => {
lazyDebug(LOGGER2, () => `Creating triangle mesh with top proportion ${topProportion}`);
const axisNormalized = vec3Funcs.normalize(axis);
const up = Math.abs(axisNormalized[1]) < 0.99 ? [0, 1, 0] : [1, 0, 0];
const tangent = vec3Funcs.normalize(vec3Funcs.cross(up, axisNormalized));
const bitangent = vec3Funcs.cross(axisNormalized, tangent);
const baseY = -1;
const topY = 1;
const leftX = -1;
const rightX = 1;
const topX = leftX + (rightX - leftX) * topProportion;
const localPoints = [
[leftX, baseY],
[rightX, baseY],
[topX, topY]
];
const vertices = [];
for (const [x, y] of localPoints) {
const vx = tangent[0] * x + bitangent[0] * y;
const vy = tangent[1] * x + bitangent[1] * y;
const vz = tangent[2] * x + bitangent[2] * y;
vertices.push([vx, vy, vz]);
}
const indices = [[0, 1, 2]];
return {
topology: "triangle-list",
vertices,
indices,
winding: "cw"
};
},
cube: () => {
lazyDebug(LOGGER2, () => "Creating cube mesh");
const vertices = [
// Front face
[-1, -1, 1],
// front bottom left
[1, -1, 1],
// front bottom right
[1, 1, 1],
// front top right
[-1, 1, 1],
// front top left
// Back face
[-1, -1, -1],
// back bottom left
[-1, 1, -1],
// back top left
[1, 1, -1],
// back top right
[1, -1, -1]
// back bottom right
];
const indices = [
// Front
[0, 1, 2],
[0, 2, 3],
// Top
[3, 2, 6],
[3, 6, 5],
// Back
[5, 6, 7],
[5, 7, 4],
// Bottom
[4, 7, 1],
[4, 1, 0],
// Left
[4, 0, 3],
[4, 3, 5],
// Right
[1, 7, 6],
[1, 6, 2]
];
return {
topology: "triangle-list",
vertices,
indices,
winding: "cw"
};
},
sphere(subdivisions) {
lazyDebug(LOGGER2, () => `Creating sphere mesh with sub divisions ${subdivisions}`);
const phi = (1 + Math.sqrt(5)) / 2;
const unnormalizedVertices = [
[-1, phi, 0],
[1, phi, 0],
[-1, -phi, 0],
[1, -phi, 0],
[0, -1, phi],
[0, 1, phi],
[0, -1, -phi],
[0, 1, -phi],
[phi, 0, -1],
[phi, 0, 1],
[-phi, 0, -1],
[-phi, 0, 1]
];
const vertices = unnormalizedVertices.map(vec3Funcs.normalize);
const baseIndices = [
[0, 11, 5],
[0, 5, 1],
[0, 1, 7],
[0, 7, 10],
[0, 10, 11],
[1, 5, 9],
[5, 11, 4],
[11, 10, 2],
[10, 7, 6],
[7, 1, 8],
[3, 9, 4],
[3, 4, 2],
[3, 2, 6],
[3, 6, 8],
[3, 8, 9],
[4, 9, 5],
[2, 4, 11],
[6, 2, 10],
[8, 6, 7],
[9, 8, 1]
];
const indices = [];
const cache = /* @__PURE__ */ new Map();
const getOrCreateMidpoint = (a, b) => {
const key = [Math.min(a, b), Math.max(a, b)].join("|");
if (cache.has(key)) {
return cache.get(key);
}
const midpoint = vec3Funcs.midpoint(vertices[a], vertices[b]);
const index = vertices.length;
vertices.push(midpoint);
cache.set(key, index);
return index;
};
const subdivide = (a, b, c, depth) => {
if (depth === 0) {
indices.push([a, b, c]);
} else {
const ab = getOrCreateMidpoint(a, b);
const bc = getOrCreateMidpoint(b, c);
const ca = getOrCreateMidpoint(c, a);
subdivide(a, ab, ca, depth - 1);
subdivide(b, bc, ab, depth - 1);
subdivide(c, ca, bc, depth - 1);
subdivide(ab, bc, ca, depth - 1);
}
};
for (const [a, b, c] of baseIndices) {
subdivide(a, b, c, subdivisions);
}
return {
topology: "triangle-list",
vertices,
indices,
winding: "cw"
};
}
};
// src/buffer-factory.ts
var MINIMUM_BYTES_LENGTH = 16;
var VALID_BYTES_MULTIPLE = 4;
var LOGGER3 = getLogger("buffer");
var checkNotDestroyed = (state) => {
if (state === 3 /* Destroyed */) {
throw Error("Cannot use managed buffer after being destroyed");
}
};
var toValidSize = (label, bytesLength) => {
const clampedBytesLength = Math.max(MINIMUM_BYTES_LENGTH, bytesLength);
const validSize = mathFuncs.nextMultipleOf(clampedBytesLength, VALID_BYTES_MULTIPLE);
if (validSize > bytesLength) {
lazyDebug(LOGGER3, () => `Increasing desired length of buffer '${label}' from ${bytesLength} to valid length ${validSize}`);
}
return validSize;
};
var bufferFactory = {
ofData: (data, label, usage) => {
lazyDebug(LOGGER3, () => `Creating buffer '${label}' of usage ${usageToString(usage)} from data of byte length ${data.byteLength}`);
const size = toValidSize(label, data.byteLength);
if (size > data.byteLength) {
lazyTrace(LOGGER3, () => `Aligning buffer ${label} to new size ${size}`);
const alignedBuffer = new ArrayBuffer(size);
new Uint8Array(alignedBuffer).set(new Uint8Array(data));
data = alignedBuffer;
}
usage |= GPUBufferUsage.COPY_DST;
let state = 0 /* Initialized */;
let trackedBuffer;
return {
get(device, queue, _encoder) {
checkNotDestroyed(state);
if (trackedBuffer === void 0) {
lazyTrace(LOGGER3, () => `Creating new buffer ${label} of size ${size} and usage ${usageToString(usage)}`);
const buffer = device.createBuffer({
label,
size,
usage
});
lazyTrace(LOGGER3, () => `Writing data to new buffer ${label} with queue`);
queue.writeBuffer(buffer, 0, data);
state = 1 /* New */;
trackedBuffer = {
isNew: true,
buffer,
destroy() {
state = 3 /* Destroyed */;
buffer.destroy();
}
};
} else {
lazyTrace(LOGGER3, () => `Reusing buffer ${label}`);
if (state === 1 /* New */) {
state = 2 /* Reused */;
trackedBuffer = {
...trackedBuffer,
isNew: false
};
}
}
return trackedBuffer;
}
};
},
ofSize: (bytesLength, label, usage) => {
lazyDebug(LOGGER3, () => `Creating buffer '${label}' of usage ${usageToString(usage)} of byte length ${bytesLength}`);
const size = toValidSize(label, bytesLength);
let state = 0 /* Initialized */;
let trackedBuffer;
return {
get(device, _queue, _encoder) {
checkNotDestroyed(state);
if (trackedBuffer === void 0) {
lazyTrace(LOGGER3, () => `Creating new buffer ${label} of size ${size} and usage ${usageToString(usage)}`);
const buffer = device.createBuffer({
label,
size,
usage
});
state = 1 /* New */;
trackedBuffer = {
buffer,
isNew: true,
destroy() {
state = 3 /* Destroyed */;
buffer.destroy();
}
};
} else {
lazyTrace(LOGGER3, () => `Reusing buffer ${label}`);
if (state === 1 /* New */) {
state = 2 /* Reused */;
trackedBuffer = {
...trackedBuffer,
isNew: false
};
}
}
return trackedBuffer;
}
};
},
ofResizeable: (copyDataOnResize, label, usage) => {
lazyDebug(LOGGER3, () => `Creating resizeable buffer '${label}' of usage ${usageToString(usage)}`);
let previousBuffer;
let currentBuffer;
const capacity = new Capacity(MINIMUM_BYTES_LENGTH, 1.2, 1.5);
let previousBytesLength = capacity.capacity;
let desiredBytesLength = 0;
let state = 0 /* Initialized */;
let trackedBuffer;
if (copyDataOnResize) {
usage |= GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST;
}
return {
resize(bytesLength) {
lazyDebug(LOGGER3, () => `Resizing buffer ${label} to desired size ${desiredBytesLength}`);
desiredBytesLength = bytesLength;
},
get(device, queue, encoder) {
checkNotDestroyed(state);
if (previousBuffer !== void 0) {
lazyDebug(LOGGER3, () => `Destroying previous buffer ${label}`);
previousBuffer.destroy();
previousBuffer = void 0;
}
if (trackedBuffer === void 0 || desiredBytesLength > capacity.capacity) {
previousBytesLength = capacity.capacity;
capacity.ensureCapacity(desiredBytesLength);
capacity.capacity = toValidSize(label, capacity.capacity);
lazyTrace(LOGGER3, () => `Creating new buffer ${label} of size ${capacity.capacity} and usage ${usageToString(usage)}`);
previousBuffer = currentBuffer;
const newBuffer = currentBuffer = device.createBuffer({
label,
size: capacity.capacity,
usage
});
state = 1 /* New */;
trackedBuffer = {
buffer: currentBuffer,
isNew: true,
destroy() {
state = 3 /* Destroyed */;
newBuffer.destroy();
}
};
if (copyDataOnResize && previousBuffer !== void 0) {
const copySize = Math.min(previousBytesLength, capacity.capacity);
if (copySize > 0) {
lazyTrace(LOGGER3, () => `Copy ${copySize} bytes of data to buffer ${label} from previous`);
encoder.copyBufferToBuffer(previousBuffer, currentBuffer, copySize);
}
}
} else if (trackedBuffer !== void 0 && state === 1 /* New */) {
lazyTrace(LOGGER3, () => `Reusing buffer ${label}`);
state = 2 /* Reused */;
trackedBuffer = {
...trackedBuffer,
isNew: false
};
}
return trackedBuffer;
}
};
},
ofMutable: (bytesLength, label, usage) => {
lazyDebug(LOGGER3, () => `Creating mutable buffer '${label}' of usage ${usageToString(usage)} from data of byte length ${bytesLength}`);
const size = toValidSize(label, bytesLength);
let state = 0 /* Initialized */;
let trackedBuffer;
const mutatedDataArray = [];
return {
mutate(data, index) {
LOGGER3.info(`Mutating data for mutable buffer ${label}`);
mutatedDataArray.push({ data, index });
},
get(device, queue, _encoder) {
checkNotDestroyed(state);
if (trackedBuffer === void 0) {
lazyTrace(LOGGER3, () => `Creating new buffer ${label} of size ${size} and usage ${usageToString(usage)}`);
const buffer2 = device.createBuffer({
label,
size,
usage
});
state = 1 /* New */;
trackedBuffer = {
buffer: buffer2,
isNew: true,
destroy() {
state = 3 /* Destroyed */;
buffer2.destroy();
}
};
} else {
lazyTrace(LOGGER3, () => `Reusing buffer ${label}`);
if (state === 1 /* New */) {
state = 2 /* Reused */;
trackedBuffer = {
...trackedBuffer,
isNew: false
};
}
}
const { buffer } = trackedBuffer;
for (const { data, index } of mutatedDataArray) {
lazyTrace(LOGGER3, () => `Writing mutated data to buffer ${label} at index ${index} and length ${data.byteLength} using queue`);
queue.writeBuffer(buffer, index, data);
}
mutatedDataArray.length = 0;
return trackedBuffer;
}
};
},
ofStaged: (label, usage) => {
lazyDebug(LOGGER3, () => `Creating staged buffer '${label}' of usage ${usageToString(usage)}`);
const stagingLabel = `${label}-staging`;
const backingLabel = `${label}-backing`;
const staging = bufferFactory.ofResizeable(false, stagingLabel, GPUBufferUsage.COPY_SRC);
const backing = bufferFactory.ofResizeable(true, backingLabel, usage | GPUBufferUsage.COPY_DST);
let mutatedSlices = void 0;
return {
mutate(data, target) {
LOGGER3.info(`Mutating data for staged buffer ${label}`);
mutatedSlices = {
values: data,
copySlices: target
};
},
get(device, queue, encoder) {
const backingTrackedBuffer = backing.get(device, queue, encoder);
if (mutatedSlices !== void 0) {
lazyTrace(LOGGER3, () => `Staging data to buffer ${stagingLabel}`);
const { values, copySlices } = mutatedSlices;
const backingSizeRequired = copySlices.reduce((max, copySlice) => Math.max(max, copySlice.toIndex + copySlice.length), 0);
lazyTrace(LOGGER3, () => `Resizing buffer ${stagingLabel} to ${values.byteLength}`);
lazyTrace(LOGGER3, () => `Resizing buffer ${backingLabel} to ${backingSizeRequired}`);
staging.resize(values.byteLength);
backing.resize(backingSizeRequired);
const stagingBuffer = staging.get(device, queue, encoder).buffer;
const backingBuffer = backingTrackedBuffer.buffer;
lazyTrace(LOGGER3, () => `Writing staging data of length ${values.byteLength} to buffer ${stagingLabel} using queue`);
queue.writeBuffer(stagingBuffer, 0, values);
for (const copySlice of copySlices) {
const { length: length2, min, toIndex } = copySlice;
lazyTrace(LOGGER3, () => `Copy staging data from ${stagingLabel} at index ${min} to ${backingLabel} at index ${toIndex} of length ${length2} using encoder`);
encoder.copyBufferToBuffer(stagingBuffer, min, backingBuffer, toIndex, length2);
}
mutatedSlices = void 0;
}
return backingTrackedBuffer;
}
};
}
};
var usageToString = (usage) => {
const flagNames = [];
if ((usage & GPUBufferUsage.COPY_DST) === GPUBufferUsage.COPY_DST) {
flagNames.push("COPY_DST");
}
if ((usage & GPUBufferUsage.COPY_SRC) === GPUBufferUsage.COPY_SRC) {
flagNames.push("COPY_SRC");
}
if ((usage & GPUBufferUsage.INDEX) === GPUBufferUsage.INDEX) {
flagNames.push("INDEX");
}
if ((usage & GPUBufferUsage.INDIRECT) === GPUBufferUsage.INDIRECT) {
flagNames.push("INDIRECT");
}
if ((usage & GPUBufferUsage.MAP_READ) === GPUBufferUsage.MAP_READ) {
flagNames.push("MAP_READ");
}
if ((usage & GPUBufferUsage.MAP_WRITE) === GPUBufferUsage.MAP_WRITE) {
flagNames.push("MAP_WRITE");
}
if ((usage & GPUBufferUsage.QUERY_RESOLVE) === GPUBufferUsage.QUERY_RESOLVE) {
flagNames.push("QUERY_RESOLVE");
}
if ((usage & GPUBufferUsage.STORAGE) === GPUBufferUsage.STORAGE) {
flagNames.push("STORAGE");
}
if ((usage & GPUBufferUsage.UNIFORM) === GPUBufferUsage.UNIFORM) {
flagNames.push("UNIFORM");
}
if ((usage & GPUBufferUsage.VERTEX) === GPUBufferUsage.VERTEX) {
flagNames.push("VERTEX");
}
return `[${flagNames.join(", ")}]`;
};
// src/buffer-formats.ts
var isUserFormatScalar = (userFormat) => userFormat.scalar !== void 0;
var isUserFormatVec2 = (userFormat) => userFormat.vec2 !== void 0;
var isUserFormatVec3 = (userFormat) => userFormat.vec3 !== void 0;
var isUserFormatVec4 = (userFormat) => userFormat.vec4 !== void 0;
var isUserFormatEntityIndex = (userFormat) => userFormat.entityIndexFromResizeableEntityCache !== void 0;
// src/strides.ts
var strideFuncs = {
ofVertexFormat: (format) => {
switch (format) {
case "float16":
return 2;
case "float32":
return 4;
case "sint16":
return 2;
case "sint32":
return 4;
case "sint8":
return 1;
case "snorm16":
return 2;
case "snorm8":
return 1;
case "uint16":
return 2;
case "uint32":
return 4;
case "uint8":
return 1;
case "unorm16":
return 2;
case "unorm8":
return 1;
}
},
dimensionMultipleOfUserFormat: (userFormat) => {
if (isUserFormatScalar(userFormat)) {
return 1;
} else if (isUserFormatVec2(userFormat)) {
return 2;
} else if (isUserFormatVec3(userFormat)) {
return 3;
} else if (isUserFormatVec4(userFormat)) {
return 4;
}
throw Error(`Cannot find dimension of user format ${JSON.stringify(userFormat)}`);
},
dimensionMultipleOfLayout: (dimension) => {
switch (dimension) {
case "scalar":
return 1;
case "vec2":
return 2;
case "vec3":
return 3;
case "vec4":
return 4;
}
},
ofLayout: (layout) => strideFuncs.dimensionMultipleOfLayout(layout.dimension) * strideFuncs.ofVertexFormat(layout.datumType),
ofFormatLayout: (formatLayout) => {
return formatLayout.reduce((acc, layout) => acc + strideFuncs.ofLayout(layout), 0);
},
ofUserFormat: (userFormat) => {
const datumStride = strideFuncs.ofVertexFormat(userFormat.datumType);
const multiple = strideFuncs.dimensionMultipleOfUserFormat(userFormat);
return datumStride * multiple;
},
ofFormatMarshall: (format) => {
return format.reduce((acc, userFormat) => acc + strideFuncs.ofUserFormat(userFormat), 0);
}
};
// src/data-extractor.ts
var LOGGER4 = getLogger("data");
var createDatumSetters = () => {
const dataViewCallCreator = callCreatorOf();
const map = /* @__PURE__ */ new Map();
const setInt8 = dataViewCallCreator("setInt8");
const setUint8 = dataViewCallCreator("setUint8");
const setInt16 = dataViewCallCreator("setInt16");
const setUint16 = dataViewCallCreator("setUint16");
const setInt32 = dataViewCallCreator("setInt32");
const setUint32 = dataViewCallCreator("setUint32");
const setFloat32 = dataViewCallCreator("setFloat32");
map.set("sint8", setInt8);
map.set("snorm8", setInt8);
map.set("uint8", setUint8);
map.set("unorm8", setUint8);
map.set("sint16", setInt16);
map.set("snorm16", setInt16);
map.set("uint16", setUint16);
map.set("unorm16", setUint16);
map.set("sint32", setInt32);
map.set("uint32", setUint32);
map.set("float16", (target, offset, value, littleEndian) => setUint16(target, offset, floatFuncs.float32ToFloat16(value), littleEndian));
map.set("float32", setFloat32);
return map;
};
var datumSetters = createDatumSetters();
var LITTLE_ENDIAN = true;
var dataExtractorFactory = {
of: (marshallFormats) => {
lazyDebug(LOGGER4, () => "Create data extractor");
let totalStride = 0;
const dataBridges = [];
for (const userFormat of marshallFormats) {
lazyTrace(LOGGER4, () => `Create ref from user format ${JSON.stringify(userFormat)}`);
const userFormatRef = toMarshalledRef(userFormat);
const { datumType } = userFormat;
const datumStride = strideFuncs.ofVertexFormat(datumType);
const stride = userFormatRef.datumCount * datumStride;
const datumSetter = datumSetters.get(datumType);
if (datumSetter === void 0) {
throw Error(`Cannot set datum of type ${datumType}`);
}
const datumOffset = totalStride;
dataBridges.push((offset, instance, dataView) => {
const values = userFormatRef.valuesOf(instance);
if (typeof values === "number") {
lazyTrace(LOGGER4, () => `Setting value ${values} at offset ${offset + datumOffset}`);
datumSetter(dataView, offset + datumOffset, values, LITTLE_ENDIAN);
} else {
for (const value of values) {
lazyTrace(LOGGER4, () => `Setting value ${value} at offset ${offset + datumOffset}`);
datumSetter(dataView, offset + datumOffset, value, LITTLE_ENDIAN);
offset += datumStride;
}
}
});
totalStride += stride;
}
return {
extract(instances) {
lazyDebug(LOGGER4, () => `Extract data from ${instances.length} instances`);
const totalSize = instances.length * totalStride;
lazyTrace(LOGGER4, () => `Creating data view to hold extracted instance data of size ${totalSize}`);
const buffer = new ArrayBuffer(totalSize);
const dataView = new DataView(buffer);
instances.forEach((instance, index) => {
lazyTrace(LOGGER4, () => `Extracting data from instance ${JSON.stringify(instance)}`);
dataBridges.forEach((extractor) => extractor(index * totalStride, instance, dataView));
});
return buffer;
}
};
}
};
var toMarshalledRef = (userFormat) => {
if (isUserFormatScalar(userFormat)) {
return ofScalar(userFormat.scalar);
} else if (isUserFormatVec2(userFormat)) {
return Array.isArray(userFormat.vec2) ? ofVecSplit(userFormat.vec2) : ofVecDirect(userFormat.vec2, 2);
} else if (isUserFormatVec3(userFormat)) {
return Array.isArray(userFormat.vec3) ? ofVecSplit(userFormat.vec3) : ofVecDirect(userFormat.vec3, 3);
} else if (isUserFormatVec4(userFormat)) {
return Array.isArray(userFormat.vec4) ? ofVecSplit(userFormat.vec4) : ofVecDirect(userFormat.vec4, 4);
} else if (isUserFormatEntityIndex(userFormat)) {
const { entityIndexFromResizeableEntityCache: { key, target } } = userFormat;
return ofEntityIndex(key, target);
} else {
throw Error(`Cannot create format reference from ${JSON.stringify(userFormat)}`);
}
};
var ofScalar = (path) => {
lazyDebug(LOGGER4, () => `Creating scalar ref from path '${path}'`);
const refPath = toRefPath(path);
return {
datumCount: 1,
valuesOf: (instance) => {
const value = valueOfInstanceAtPath(instance, refPath);
lazyTrace(LOGGER4, () => `Found value ${JSON.stringify(value)} at path '${path}'`);
return value;
}
};
};
var ofVecDirect = (path, vecLength) => {
lazyDebug(LOGGER4, () => `Creating vec direct ref from path '${path}'`);
const refPath = toRefPath(path);
return {
datumCount: vecLength,
valuesOf: (instance) => {
const value = valueAtPath(instance, refPath, 0);
if (Array.isArray(value)) {
lazyTrace(LOGGER4, () => `Found array ${JSON.stringify(value)} at path '${path}'`);
return value;
}
throw Error(`Value ${JSON.stringify(value)} at path ${refPath} is not an array`);
}
};
};
var ofVecSplit = (paths) => {
lazyDebug(LOGGER4, () => `Creating vec split ref from path '${JSON.stringify(paths)}'`);
const refPaths = paths.map(toRefPath);
return {
datumCount: paths.length,
valuesOf: (instance) => {
const values = refPaths.map((refPath) => valueOfInstanceAtPath(instance, refPath));
lazyTrace(LOGGER4, () => `Found array ${JSON.stringify(values)} at paths '${JSON.stringify(paths)}'`);
return values;
}
};
};
var ofEntityIndex = (key, target) => {
lazyDebug(LOGGER4, () => `Creating entity index ref from key '${key}'`);
const refPath = toRefPath(key);
return {
datumCount: 1,
valuesOf(instance) {
const id = valueAtPath(instance, refPath, 0);
if (typeof id !== "string") {
throw Error(`Value found at path ${key} must be a string but was a ${typeof id}`);
} else {
const index = target.indexOf(id);
if (index === -1) {
lazyWarn(LOGGER4, () => `ID at path ${key} was '${id}' and could not be found, index falling back to -1`);
}
lazyTrace(LOGGER4, () => `Found entity index ${index} for id '${id}'`);
return index;
}
}
};
};
var toRefPath = (path) => {
const parts = path.split(".");
const refPath = parts.map((part) => stringFuncs.canBePositiveInt(part) ? Number(part) : part);
lazyTrace(LOGGER4, () => `Converted path '${path}' to ref path ${JSON.stringify(refPath)}`);
return refPath;
};
var valueAtPath = (input, refPath, pathIndex) => {
if (pathIndex > refPath.length) {
throw Error(`Cannot use index ${pathIndex} larger than reference path. Path: ${refPath}. Input: ${JSON.stringify(input)}`);
}
if (pathIndex === refPath.length) {
lazyTrace(LOGGER4, () => `Found value ${input} at path ${JSON.stringify(refPath)}`);
return input;
}
const indexValue = refPath[pathIndex];
if (typeof input !== "object" || input === null) {
throw Error(`Cannot index field ${input} with index ${indexValue}. Path: ${refPath}. Input: ${JSON.stringify(input)}`);
}
if (typeof indexValue === "string" || typeof indexValue === "number") {
return valueAtPath(input[indexValue], refPath, pathIndex + 1);
}
throw Error(`Cannot index using non-integer or string field ${indexValue}. Path: ${refPath}. Input: ${JSON.stringify(input)}`);
};
var valueOfInstanceAtPath = (instance, refPath) => {
const value = valueAtPath(instance, refPath, 0);
if (typeof value === "number") {
return value;
}
throw Error(`Value ${JSON.stringify(value)} at path ${refPath} is not a number`);
};
// src/buffer-resources.ts
var LOGGER5 = getLogger("buffer");
var bufferResourcesFactory = {
ofMesh: (name, mesh) => {
LOGGER5.debug(`Creating mesh buffer ${name}`);
const indices = bufferFactory.ofData(meshFuncs.toIndicesData(mesh), `${name}-indices`, GPUBufferUsage.INDEX);
const vertices = bufferFactory.ofData(meshFuncs.toVerticesData(mesh), `${name}-vertices`, GPUBufferUsage.VERTEX);
return {
indices,
vertices
};
},
ofUniformAndIn