UNPKG

@motion-core/motion-gpu

Version:

Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte, React, and Vue adapter entrypoints.

143 lines (131 loc) 3.73 kB
import { assertUniformName } from './uniforms.js'; import type { StorageBufferDefinition, StorageBufferDefinitionMap, StorageBufferType } from './types.js'; /** * Valid WGSL storage buffer element types. */ const VALID_STORAGE_BUFFER_TYPES: ReadonlySet<string> = new Set<StorageBufferType>([ 'array<f32>', 'array<vec2f>', 'array<vec3f>', 'array<vec4f>', 'array<u32>', 'array<i32>', 'array<vec4u>', 'array<vec4i>' ]); /** * Storage-compatible texture formats for `texture_storage_2d`. */ export const STORAGE_TEXTURE_FORMATS: ReadonlySet<GPUTextureFormat> = new Set([ 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba8unorm', 'rgba8snorm', 'rgba8uint', 'rgba8sint', 'rgba16float', 'rgba16uint', 'rgba16sint', 'rgba32float', 'rgba32uint', 'rgba32sint', 'bgra8unorm' ] as GPUTextureFormat[]); /** * Validates a single storage buffer definition. * * @param name - Buffer identifier. * @param definition - Storage buffer definition to validate. * @throws {Error} When any field is invalid. */ export function assertStorageBufferDefinition( name: string, definition: StorageBufferDefinition ): void { assertUniformName(name); if (!Number.isFinite(definition.size) || definition.size <= 0) { throw new Error( `Storage buffer "${name}" size must be a finite number greater than 0, got ${definition.size}` ); } if (definition.size % 4 !== 0) { throw new Error( `Storage buffer "${name}" size must be a multiple of 4, got ${definition.size}` ); } if (!VALID_STORAGE_BUFFER_TYPES.has(definition.type)) { throw new Error( `Storage buffer "${name}" has unknown type "${definition.type}". Supported types: ${[...VALID_STORAGE_BUFFER_TYPES].join(', ')}` ); } if ( definition.access !== undefined && definition.access !== 'read' && definition.access !== 'read-write' ) { throw new Error( `Storage buffer "${name}" has invalid access mode "${definition.access}". Use 'read' or 'read-write'.` ); } if (definition.initialData !== undefined) { if (definition.initialData.byteLength > definition.size) { throw new Error( `Storage buffer "${name}" initialData byte length (${definition.initialData.byteLength}) exceeds buffer size (${definition.size})` ); } } } /** * Validates and returns sorted storage buffer keys. * * @param definitions - Storage buffer definition map. * @returns Lexicographically sorted buffer keys. */ export function resolveStorageBufferKeys(definitions: StorageBufferDefinitionMap): string[] { const keys = Object.keys(definitions).sort(); for (const key of keys) { const definition = definitions[key]; if (definition) { assertStorageBufferDefinition(key, definition); } } return keys; } /** * Normalizes a storage buffer definition with defaults applied. * * @param definition - Raw definition. * @returns Normalized definition with access default. */ export function normalizeStorageBufferDefinition( definition: StorageBufferDefinition ): Required<Pick<StorageBufferDefinition, 'size' | 'type' | 'access'>> { return { size: definition.size, type: definition.type, access: definition.access ?? 'read-write' }; } /** * Validates that a texture format is storage-compatible. * * @param name - Texture identifier. * @param format - GPU texture format. * @throws {Error} When format is not storage-compatible. */ export function assertStorageTextureFormat(name: string, format: GPUTextureFormat): void { if (!STORAGE_TEXTURE_FORMATS.has(format)) { throw new Error( `Texture "${name}" with storage:true requires a storage-compatible format, but got "${format}". ` + `Supported formats: ${[...STORAGE_TEXTURE_FORMATS].join(', ')}` ); } }