@motion-core/motion-gpu
Version:
Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte, React, and Vue adapter entrypoints.
262 lines (234 loc) • 6.99 kB
text/typescript
import type { MaterialSourceLocation } from './material-preprocess.js';
/**
* Source metadata for material declaration callsite.
*/
export interface MaterialSourceMetadata {
component?: string;
file?: string;
line?: number;
column?: number;
functionName?: string;
}
export interface ComputeSourceLocation {
kind: 'compute';
line: number;
}
export type ShaderSourceLocation = MaterialSourceLocation | ComputeSourceLocation;
/**
* One WGSL compiler diagnostic enriched with source-location metadata.
*/
export interface ShaderCompilationDiagnostic {
generatedLine: number;
message: string;
linePos?: number;
lineLength?: number;
sourceLocation: ShaderSourceLocation | null;
}
/**
* Runtime context snapshot captured for shader compilation diagnostics.
*/
export interface ShaderCompilationRuntimeContext {
materialSignature?: string;
passGraph?: {
passCount: number;
enabledPassCount: number;
inputs: string[];
outputs: string[];
};
activeRenderTargets: string[];
}
/**
* Structured payload attached to WGSL compilation errors.
*/
export interface ShaderCompilationDiagnosticsPayload {
kind: 'shader-compilation';
shaderStage?: 'fragment' | 'compute';
diagnostics: ShaderCompilationDiagnostic[];
fragmentSource: string;
computeSource?: string;
includeSources: Record<string, string>;
defineBlockSource?: string;
materialSource: MaterialSourceMetadata | null;
runtimeContext?: ShaderCompilationRuntimeContext;
}
type MotionGPUErrorWithDiagnostics = Error & {
motiongpuDiagnostics?: unknown;
};
function isMaterialSourceMetadata(value: unknown): value is MaterialSourceMetadata {
if (value === null || typeof value !== 'object') {
return false;
}
const record = value as Record<string, unknown>;
if (record.component !== undefined && typeof record.component !== 'string') {
return false;
}
if (record.file !== undefined && typeof record.file !== 'string') {
return false;
}
if (record.functionName !== undefined && typeof record.functionName !== 'string') {
return false;
}
if (record.line !== undefined && typeof record.line !== 'number') {
return false;
}
if (record.column !== undefined && typeof record.column !== 'number') {
return false;
}
return true;
}
function isShaderSourceLocation(value: unknown): value is ShaderSourceLocation | null {
if (value === null) {
return true;
}
if (typeof value !== 'object') {
return false;
}
const record = value as Record<string, unknown>;
const kind = record.kind;
if (kind !== 'fragment' && kind !== 'include' && kind !== 'define' && kind !== 'compute') {
return false;
}
return typeof record.line === 'number';
}
function isShaderCompilationDiagnostic(value: unknown): value is ShaderCompilationDiagnostic {
if (value === null || typeof value !== 'object') {
return false;
}
const record = value as Record<string, unknown>;
if (typeof record.generatedLine !== 'number') {
return false;
}
if (typeof record.message !== 'string') {
return false;
}
if (record.linePos !== undefined && typeof record.linePos !== 'number') {
return false;
}
if (record.lineLength !== undefined && typeof record.lineLength !== 'number') {
return false;
}
if (!isShaderSourceLocation(record.sourceLocation)) {
return false;
}
return true;
}
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
}
function isShaderCompilationRuntimeContext(
value: unknown
): value is ShaderCompilationRuntimeContext {
if (value === null || typeof value !== 'object') {
return false;
}
const record = value as Record<string, unknown>;
if (record.materialSignature !== undefined && typeof record.materialSignature !== 'string') {
return false;
}
if (!isStringArray(record.activeRenderTargets)) {
return false;
}
const passGraph = record.passGraph;
if (passGraph === undefined) {
return true;
}
if (passGraph === null || typeof passGraph !== 'object') {
return false;
}
const passGraphRecord = passGraph as Record<string, unknown>;
if (typeof passGraphRecord.passCount !== 'number') {
return false;
}
if (typeof passGraphRecord.enabledPassCount !== 'number') {
return false;
}
if (!isStringArray(passGraphRecord.inputs) || !isStringArray(passGraphRecord.outputs)) {
return false;
}
return true;
}
/**
* Attaches structured diagnostics payload to an Error.
*/
export function attachShaderCompilationDiagnostics(
error: Error,
payload: ShaderCompilationDiagnosticsPayload
): Error {
(error as MotionGPUErrorWithDiagnostics).motiongpuDiagnostics = payload;
return error;
}
/**
* Extracts structured diagnostics payload from unknown error value.
*/
export function getShaderCompilationDiagnostics(
error: unknown
): ShaderCompilationDiagnosticsPayload | null {
if (!(error instanceof Error)) {
return null;
}
const payload = (error as MotionGPUErrorWithDiagnostics).motiongpuDiagnostics;
if (payload === null || typeof payload !== 'object') {
return null;
}
const record = payload as Record<string, unknown>;
if (record.kind !== 'shader-compilation') {
return null;
}
if (
record.shaderStage !== undefined &&
record.shaderStage !== 'fragment' &&
record.shaderStage !== 'compute'
) {
return null;
}
if (
!Array.isArray(record.diagnostics) ||
!record.diagnostics.every(isShaderCompilationDiagnostic)
) {
return null;
}
if (typeof record.fragmentSource !== 'string') {
return null;
}
if (record.computeSource !== undefined && typeof record.computeSource !== 'string') {
return null;
}
if (record.defineBlockSource !== undefined && typeof record.defineBlockSource !== 'string') {
return null;
}
if (record.includeSources === null || typeof record.includeSources !== 'object') {
return null;
}
const includeSources = record.includeSources as Record<string, unknown>;
if (Object.values(includeSources).some((value) => typeof value !== 'string')) {
return null;
}
if (record.materialSource !== null && !isMaterialSourceMetadata(record.materialSource)) {
return null;
}
if (
record.runtimeContext !== undefined &&
!isShaderCompilationRuntimeContext(record.runtimeContext)
) {
return null;
}
return {
kind: 'shader-compilation',
...(record.shaderStage !== undefined
? { shaderStage: record.shaderStage as 'fragment' | 'compute' }
: {}),
diagnostics: record.diagnostics as ShaderCompilationDiagnostic[],
fragmentSource: record.fragmentSource,
...(record.computeSource !== undefined
? { computeSource: record.computeSource as string }
: {}),
includeSources: includeSources as Record<string, string>,
...(record.defineBlockSource !== undefined
? { defineBlockSource: record.defineBlockSource as string }
: {}),
materialSource: (record.materialSource ?? null) as MaterialSourceMetadata | null,
...(record.runtimeContext !== undefined
? { runtimeContext: record.runtimeContext as ShaderCompilationRuntimeContext }
: {})
};
}