UNPKG

typegpu

Version:

A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.

82 lines (81 loc) 4.6 kB
import { alignmentOf } from "../../data/alignmentOf.js"; import { memoryLayoutOf } from "../../data/offsetUtils.js"; import { sizeOf } from "../../data/sizeOf.js"; import { isWgslArray, isWgslStruct } from "../../data/wgslTypes.js"; import { invariant } from "../../errors.js"; import { roundUp } from "../../mathUtils.js"; import { getName } from "../../shared/meta.js"; import { logger } from "../../tgpuLogger.js"; /** * Warns if layout exceeds supported buffer count limits. */ export function warnIfOverflow(layouts, limits) { const entries = Object.values(layouts) .flatMap((layout) => Object.values(layout.entries)) .filter((entry) => entry !== null); const uniform = entries.filter((entry) => 'uniform' in entry).length; const storage = entries.filter((entry) => 'storage' in entry).length; if (uniform > limits.maxUniformBuffersPerShaderStage) { logger.warn('webgpu-limits-exceeded', `Total number of uniform buffers (${uniform}) exceeds maxUniformBuffersPerShaderStage (${limits.maxUniformBuffersPerShaderStage}). Consider: 1. Grouping some of the uniforms into one using 'd.struct', 2. Increasing the limit when requesting a device or creating a root.`); } if (storage > limits.maxStorageBuffersPerShaderStage) { logger.warn('webgpu-limits-exceeded', `Total number of storage buffers (${storage}) exceeds maxStorageBuffersPerShaderStage (${limits.maxStorageBuffersPerShaderStage}).`); } } function requiredAlignOf(schema) { if (isWgslStruct(schema) || isWgslArray(schema)) { return roundUp(alignmentOf(schema), 16); } return alignmentOf(schema); } /** * See https://www.w3.org/TR/WGSL/#address-space-layout-constraints */ export function warnIfNotUniformAligned(schema) { if (isWgslArray(schema)) { warnIfNotUniformAligned(schema.elementType); const stride = roundUp(sizeOf(schema.elementType), alignmentOf(schema.elementType)); if (stride % 16 !== 0) { logger.warn('uniform-schema-misaligned', `\ Schema '${getName(schema.elementType) ?? '<unnamed>'}' is used in an array in a uniform buffer, and its stride (${stride}) is not a multiple of 16. This is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices. To address this, put the element schema in a struct and wrap the prop in 'd.align(16, ...)', or use a different schema like 'vec4f'.`); } } if (isWgslStruct(schema)) { Object.values(schema.propTypes).forEach(warnIfNotUniformAligned); Object.entries(schema.propTypes).forEach(([key, value]) => { const offset = memoryLayoutOf(schema, (schema) => schema[key]).offset; const requiredAlignment = requiredAlignOf(value); if (offset % requiredAlignment) { logger.warn('uniform-schema-misaligned', `\ Schema '${getName(schema) ?? '<unnamed>'}' is used in a uniform buffer, and its property '${key}' does not meet required alignment (offset is ${offset}, required alignment is ${requiredAlignment}). This is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices. To address this, wrap the property '${key}' in 'd.align(${requiredAlignment}, ...)'.`); } }); const keys = Object.keys(schema.propTypes); for (let i = 0; i < keys.length - 1; i++) { const thisKey = keys[i]; const nextKey = keys[i + 1]; invariant(thisKey && nextKey); const thisValue = schema.propTypes[thisKey]; invariant(thisValue); if (!isWgslStruct(thisValue)) { continue; } const minimumDifference = roundUp(sizeOf(thisValue), 16); const thisKeyOffset = memoryLayoutOf(schema, (schema) => schema[thisKey]).offset; const nextKeyOffset = memoryLayoutOf(schema, (schema) => schema[nextKey]).offset; const difference = nextKeyOffset - thisKeyOffset; if (minimumDifference > difference) { logger.warn('uniform-schema-misaligned', `\ Schema '${getName(schema) ?? '<unnamed>'}' is used in a uniform buffer, and the difference between memory offsets of '${thisKey}' and '${nextKey}' props (${difference}) is less than recommended (${minimumDifference}). This is not portable (see https://www.w3.org/TR/WGSL/#address-space-layout-constraints), and will break on some devices. To address this, wrap the '${thisKey}' prop in 'd.size(${minimumDifference}, ...)'.`); } } } }