typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
61 lines (60 loc) • 2.78 kB
JavaScript
import { memoryLayoutOf } from "../../data/offsetUtils.js";
import { sizeOf } from "../../data/sizeOf.js";
import { isGPUBuffer } from "../../types.js";
import { logger } from "../../tgpuLogger.js";
export const DISPATCH_INDIRECT_SIZE = 12; // 3 x u32 (x, y, z)
export const DRAW_INDIRECT_SIZE = 16; // 4 x 4
export const DRAW_INDEXED_INDIRECT_SIZE = 20; // 5 x 4
export function collectBindGroupPairs(layouts, catchall, map) {
return layouts.flatMap((layout, idx) => {
const resource = catchall && idx === catchall[0] ? catchall[1] : map?.get(layout);
return resource ? [[layout, resource]] : [];
});
}
export function collectVertexBufferPairs(layouts, map) {
return layouts.flatMap((layout) => {
const resource = map?.get(layout);
return resource ? [[layout, resource]] : [];
});
}
export function restoreTimestampPriors(pipeline, priors) {
let result = pipeline;
if (priors.timestampWrites) {
result = result.withTimestampWrites(priors.timestampWrites);
}
if (priors.performanceCallback) {
result = result.withPerformanceCallback(priors.performanceCallback);
}
return result;
}
const IndirectOperationToRequiredData = {
dispatchWorkgroupsIndirect: '3 x u32',
drawIndirect: '4 x u32',
drawIndexedIndirect: '3 x u32, i32, u32',
};
function validateIndirectBufferSize(bufferSize, offset, requiredBytes, operation) {
if (offset + requiredBytes > bufferSize) {
throw new Error(`Buffer too small for ${operation}. Required: ${requiredBytes} bytes at offset ${offset}, but buffer is only ${bufferSize} bytes.`);
}
if (offset % 4 !== 0) {
throw new Error(`Indirect buffer offset must be a multiple of 4. Got: ${offset}`);
}
}
export function resolveIndirectOffset(indirectBuffer, start, requiredSize, operation) {
if (isGPUBuffer(indirectBuffer)) {
const offset = typeof start === 'number' ? start : (start?.offset ?? 0);
validateIndirectBufferSize(indirectBuffer.size, offset, requiredSize, operation);
return offset;
}
const offsetInfo = start
? typeof start === 'number'
? { offset: start, contiguous: requiredSize }
: start
: memoryLayoutOf(indirectBuffer.dataType);
const { offset, contiguous } = offsetInfo;
validateIndirectBufferSize(sizeOf(indirectBuffer.dataType), offset, requiredSize, operation);
if (contiguous < requiredSize) {
logger.warn('suspicious', `${operation}: Starting at offset ${offset}, only ${contiguous} contiguous bytes are available before padding. '${operation}' requires ${requiredSize} bytes (${IndirectOperationToRequiredData[operation]}). Reading across padding may result in undefined behavior.`);
}
return offset;
}