@motion-core/motion-gpu
Version:
Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte, React, and Vue adapter entrypoints.
515 lines (514 loc) • 16.9 kB
JavaScript
import { createMotionGPUError } from "./error-report.js";
//#region src/lib/core/format-capabilities.ts
/** Texture formats recognized by the WebGPU typings supported in this release. */
var TEXTURE_FORMATS = /* @__PURE__ */ new Set([
"r8unorm",
"r8snorm",
"r8uint",
"r8sint",
"r16unorm",
"r16snorm",
"r16uint",
"r16sint",
"r16float",
"rg8unorm",
"rg8snorm",
"rg8uint",
"rg8sint",
"r32uint",
"r32sint",
"r32float",
"rg16unorm",
"rg16snorm",
"rg16uint",
"rg16sint",
"rg16float",
"rgba8unorm",
"rgba8unorm-srgb",
"rgba8snorm",
"rgba8uint",
"rgba8sint",
"bgra8unorm",
"bgra8unorm-srgb",
"rgb9e5ufloat",
"rgb10a2uint",
"rgb10a2unorm",
"rg11b10ufloat",
"rg32uint",
"rg32sint",
"rg32float",
"rgba16unorm",
"rgba16snorm",
"rgba16uint",
"rgba16sint",
"rgba16float",
"rgba32uint",
"rgba32sint",
"rgba32float",
"stencil8",
"depth16unorm",
"depth24plus",
"depth24plus-stencil8",
"depth32float",
"depth32float-stencil8",
"bc1-rgba-unorm",
"bc1-rgba-unorm-srgb",
"bc2-rgba-unorm",
"bc2-rgba-unorm-srgb",
"bc3-rgba-unorm",
"bc3-rgba-unorm-srgb",
"bc4-r-unorm",
"bc4-r-snorm",
"bc5-rg-unorm",
"bc5-rg-snorm",
"bc6h-rgb-ufloat",
"bc6h-rgb-float",
"bc7-rgba-unorm",
"bc7-rgba-unorm-srgb",
"etc2-rgb8unorm",
"etc2-rgb8unorm-srgb",
"etc2-rgb8a1unorm",
"etc2-rgb8a1unorm-srgb",
"etc2-rgba8unorm",
"etc2-rgba8unorm-srgb",
"eac-r11unorm",
"eac-r11snorm",
"eac-rg11unorm",
"eac-rg11snorm",
"astc-4x4-unorm",
"astc-4x4-unorm-srgb",
"astc-5x4-unorm",
"astc-5x4-unorm-srgb",
"astc-5x5-unorm",
"astc-5x5-unorm-srgb",
"astc-6x5-unorm",
"astc-6x5-unorm-srgb",
"astc-6x6-unorm",
"astc-6x6-unorm-srgb",
"astc-8x5-unorm",
"astc-8x5-unorm-srgb",
"astc-8x6-unorm",
"astc-8x6-unorm-srgb",
"astc-8x8-unorm",
"astc-8x8-unorm-srgb",
"astc-10x5-unorm",
"astc-10x5-unorm-srgb",
"astc-10x6-unorm",
"astc-10x6-unorm-srgb",
"astc-10x8-unorm",
"astc-10x8-unorm-srgb",
"astc-10x10-unorm",
"astc-10x10-unorm-srgb",
"astc-12x10-unorm",
"astc-12x10-unorm-srgb",
"astc-12x12-unorm",
"astc-12x12-unorm-srgb"
]);
var COLOR_RENDERABLE_FORMATS = /* @__PURE__ */ new Set([
"r8unorm",
"r8snorm",
"r8uint",
"r8sint",
"r16unorm",
"r16snorm",
"r16uint",
"r16sint",
"r16float",
"rg8unorm",
"rg8snorm",
"rg8uint",
"rg8sint",
"r32uint",
"r32sint",
"r32float",
"rg16unorm",
"rg16snorm",
"rg16uint",
"rg16sint",
"rg16float",
"rgba8unorm",
"rgba8unorm-srgb",
"rgba8snorm",
"rgba8uint",
"rgba8sint",
"bgra8unorm",
"bgra8unorm-srgb",
"rgb10a2uint",
"rgb10a2unorm",
"rg11b10ufloat",
"rg32uint",
"rg32sint",
"rg32float",
"rgba16unorm",
"rgba16snorm",
"rgba16uint",
"rgba16sint",
"rgba16float",
"rgba32uint",
"rgba32sint",
"rgba32float"
]);
var FORMAT_REQUIRED_FEATURE = Object.freeze({
r16unorm: "texture-formats-tier1",
r16snorm: "texture-formats-tier1",
rg16unorm: "texture-formats-tier1",
rg16snorm: "texture-formats-tier1",
rgba16unorm: "texture-formats-tier1",
rgba16snorm: "texture-formats-tier1",
"bgra8unorm-srgb": "core-features-and-limits",
"depth32float-stencil8": "depth32float-stencil8",
"bc1-rgba-unorm": "texture-compression-bc",
"bc1-rgba-unorm-srgb": "texture-compression-bc",
"bc2-rgba-unorm": "texture-compression-bc",
"bc2-rgba-unorm-srgb": "texture-compression-bc",
"bc3-rgba-unorm": "texture-compression-bc",
"bc3-rgba-unorm-srgb": "texture-compression-bc",
"bc4-r-unorm": "texture-compression-bc",
"bc4-r-snorm": "texture-compression-bc",
"bc5-rg-unorm": "texture-compression-bc",
"bc5-rg-snorm": "texture-compression-bc",
"bc6h-rgb-ufloat": "texture-compression-bc",
"bc6h-rgb-float": "texture-compression-bc",
"bc7-rgba-unorm": "texture-compression-bc",
"bc7-rgba-unorm-srgb": "texture-compression-bc",
"etc2-rgb8unorm": "texture-compression-etc2",
"etc2-rgb8unorm-srgb": "texture-compression-etc2",
"etc2-rgb8a1unorm": "texture-compression-etc2",
"etc2-rgb8a1unorm-srgb": "texture-compression-etc2",
"etc2-rgba8unorm": "texture-compression-etc2",
"etc2-rgba8unorm-srgb": "texture-compression-etc2",
"eac-r11unorm": "texture-compression-etc2",
"eac-r11snorm": "texture-compression-etc2",
"eac-rg11unorm": "texture-compression-etc2",
"eac-rg11snorm": "texture-compression-etc2",
"astc-4x4-unorm": "texture-compression-astc",
"astc-4x4-unorm-srgb": "texture-compression-astc",
"astc-5x4-unorm": "texture-compression-astc",
"astc-5x4-unorm-srgb": "texture-compression-astc",
"astc-5x5-unorm": "texture-compression-astc",
"astc-5x5-unorm-srgb": "texture-compression-astc",
"astc-6x5-unorm": "texture-compression-astc",
"astc-6x5-unorm-srgb": "texture-compression-astc",
"astc-6x6-unorm": "texture-compression-astc",
"astc-6x6-unorm-srgb": "texture-compression-astc",
"astc-8x5-unorm": "texture-compression-astc",
"astc-8x5-unorm-srgb": "texture-compression-astc",
"astc-8x6-unorm": "texture-compression-astc",
"astc-8x6-unorm-srgb": "texture-compression-astc",
"astc-8x8-unorm": "texture-compression-astc",
"astc-8x8-unorm-srgb": "texture-compression-astc",
"astc-10x5-unorm": "texture-compression-astc",
"astc-10x5-unorm-srgb": "texture-compression-astc",
"astc-10x6-unorm": "texture-compression-astc",
"astc-10x6-unorm-srgb": "texture-compression-astc",
"astc-10x8-unorm": "texture-compression-astc",
"astc-10x8-unorm-srgb": "texture-compression-astc",
"astc-10x10-unorm": "texture-compression-astc",
"astc-10x10-unorm-srgb": "texture-compression-astc",
"astc-12x10-unorm": "texture-compression-astc",
"astc-12x10-unorm-srgb": "texture-compression-astc",
"astc-12x12-unorm": "texture-compression-astc",
"astc-12x12-unorm-srgb": "texture-compression-astc"
});
var RENDER_REQUIRED_FEATURE = Object.freeze({
r8snorm: "texture-formats-tier1",
rg8snorm: "texture-formats-tier1",
rgba8snorm: "texture-formats-tier1",
rg11b10ufloat: "rg11b10ufloat-renderable"
});
var STORAGE_FORMATS = /* @__PURE__ */ new Set([
"r8unorm",
"r8snorm",
"r8uint",
"r8sint",
"r16unorm",
"r16snorm",
"r16uint",
"r16sint",
"r16float",
"rg8unorm",
"rg8snorm",
"rg8uint",
"rg8sint",
"rg16unorm",
"rg16snorm",
"rg16uint",
"rg16sint",
"rg16float",
"rgba8unorm",
"rgba8snorm",
"rgba8uint",
"rgba8sint",
"rgba16unorm",
"rgba16snorm",
"rgba16float",
"rgba16uint",
"rgba16sint",
"r32float",
"r32sint",
"r32uint",
"rg32float",
"rg32sint",
"rg32uint",
"rgba32float",
"rgba32uint",
"rgba32sint",
"bgra8unorm",
"rgb10a2uint",
"rgb10a2unorm",
"rg11b10ufloat"
]);
var EMPTY_STORAGE_ACCESS = Object.freeze([]);
var STORAGE_WRITE_REQUIRED_TIER1 = /* @__PURE__ */ new Set([
"r8unorm",
"r8snorm",
"r8uint",
"r8sint",
"r16uint",
"r16sint",
"r16float",
"rg8unorm",
"rg8snorm",
"rg8uint",
"rg8sint",
"rg16uint",
"rg16sint",
"rg16float",
"rgb10a2uint",
"rgb10a2unorm",
"rg11b10ufloat"
]);
var STORAGE_WRITE_REQUIRED_CORE = /* @__PURE__ */ new Set([
"rg32uint",
"rg32sint",
"rg32float"
]);
var STORAGE_READ_WRITE_FORMATS = /* @__PURE__ */ new Set([
"r8unorm",
"r8uint",
"r8sint",
"r16uint",
"r16sint",
"r16float",
"rgba8unorm",
"rgba8uint",
"rgba8sint",
"rgba16uint",
"rgba16sint",
"rgba16float",
"r32uint",
"r32sint",
"r32float",
"rgba32uint",
"rgba32sint",
"rgba32float"
]);
var STORAGE_READ_WRITE_BASELINE = /* @__PURE__ */ new Set([
"r32uint",
"r32sint",
"r32float"
]);
function hasFeature(features, feature) {
if (features?.has(feature) === true) return true;
if (feature === "texture-formats-tier1" && features?.has("texture-formats-tier2")) return true;
if (feature === "rg11b10ufloat-renderable" && (features?.has("texture-formats-tier1") || features?.has("texture-formats-tier2"))) return true;
return false;
}
/** Rejects texture format strings outside the WebGPU contract known to this release. */
function assertTextureFormat(format, label = "Texture") {
if (typeof format !== "string" || !TEXTURE_FORMATS.has(format)) throw new Error(`${label} format "${String(format)}" is not a recognized GPUTextureFormat.`);
}
/** Reports whether a texture format uses 32-bit floating-point channels. */
function isFloat32TextureFormat(format) {
return format === "r32float" || format === "rg32float" || format === "rgba32float";
}
/** Reports whether the format has any storage-texture access in the WebGPU capability table. */
function isStorageTextureFormat(format) {
assertTextureFormat(format);
return STORAGE_FORMATS.has(format);
}
/** Resolves the WGSL sampled scalar represented by a texture format. */
function textureSampleScalarType(format) {
if (format === "stencil8") return "u32";
if (format.startsWith("depth")) return "depth";
if (format.endsWith("uint")) return "u32";
if (format.endsWith("sint")) return "i32";
return "f32";
}
/**
* Returns the effective capabilities of a texture format on the active device.
* This is the sole format classification table used by render, sampling and compute paths.
*/
function resolveTextureFormatCapabilities(format, deviceFeatures) {
assertTextureFormat(format);
const scalarType = textureSampleScalarType(format);
const formatFeature = FORMAT_REQUIRED_FEATURE[format];
const supported = !formatFeature || hasFeature(deviceFeatures, formatFeature);
const float32Filterable = isFloat32TextureFormat(format) && hasFeature(deviceFeatures, "float32-filterable");
const isUnfilterableNormalized16 = format === "r16unorm" || format === "r16snorm" || format === "rg16unorm" || format === "rg16snorm" || format === "rgba16unorm" || format === "rgba16snorm";
const sampleType = scalarType === null ? null : scalarType === "depth" ? "depth" : scalarType === "u32" ? "uint" : scalarType === "i32" ? "sint" : isFloat32TextureFormat(format) && !float32Filterable || isUnfilterableNormalized16 ? "unfilterable-float" : "float";
const renderFeature = RENDER_REQUIRED_FEATURE[format];
const storageFeature = format === "bgra8unorm" ? "bgra8unorm-storage" : STORAGE_WRITE_REQUIRED_TIER1.has(format) ? "texture-formats-tier1" : STORAGE_WRITE_REQUIRED_CORE.has(format) ? "core-features-and-limits" : void 0;
const staticallyRenderable = COLOR_RENDERABLE_FORMATS.has(format);
const colorRenderable = supported && staticallyRenderable && (!renderFeature || hasFeature(deviceFeatures, renderFeature));
const renderable = colorRenderable || supported && (format === "stencil8" || format.startsWith("depth"));
const storageWritable = supported && STORAGE_FORMATS.has(format) && (!storageFeature || hasFeature(deviceFeatures, storageFeature));
const storageReadWriteFeature = STORAGE_READ_WRITE_BASELINE.has(format) ? void 0 : STORAGE_READ_WRITE_FORMATS.has(format) ? "texture-formats-tier2" : void 0;
const storageReadWrite = storageWritable && STORAGE_READ_WRITE_FORMATS.has(format) && (!storageReadWriteFeature || hasFeature(deviceFeatures, storageReadWriteFeature));
const storageAccess = [];
if (storageWritable) {
storageAccess.push("write-only");
if (format !== "bgra8unorm") storageAccess.push("read-only");
if (storageReadWrite) storageAccess.push("read-write");
}
return {
format,
supported,
renderable,
colorRenderable,
sampleType,
filterable: supported && (sampleType === "float" || isFloat32TextureFormat(format) && float32Filterable),
storageAccess: storageAccess.length > 0 ? Object.freeze(storageAccess) : EMPTY_STORAGE_ACCESS,
requiredFeatures: Object.freeze({
...formatFeature ? { format: formatFeature } : {},
...renderFeature ? { renderable: renderFeature } : {},
...isFloat32TextureFormat(format) ? { filterable: "float32-filterable" } : {},
...storageFeature || storageReadWriteFeature ? { storageAccess: Object.freeze({
...storageFeature ? {
"write-only": storageFeature,
...format === "bgra8unorm" ? {} : { "read-only": storageFeature }
} : {},
...storageReadWriteFeature ? { "read-write": storageReadWriteFeature } : {}
}) } : {}
})
};
}
/** Creates a stable classified error for an unmet format capability. */
function createFormatCapabilityError(input) {
return createMotionGPUError("FORMAT_CAPABILITY_MISSING", `${input.pass} target "${input.target}" uses format "${String(input.format)}" but is missing capability "${input.capability}".${input.detail ? ` ${input.detail}` : ""}`);
}
/** Validates a format used as a MotionGPU color render target. */
function assertRenderableFormat(input) {
try {
assertTextureFormat(input.format);
} catch {
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: "recognized GPUTextureFormat"
});
}
const capabilities = resolveTextureFormatCapabilities(input.format, input.deviceFeatures);
if (!capabilities.colorRenderable) {
const requiredFeature = capabilities.requiredFeatures.format ?? capabilities.requiredFeatures.renderable;
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: requiredFeature ?? "color render attachment",
...requiredFeature ? { detail: `Enable device feature "${requiredFeature}".` } : { detail: "Use a color-renderable texture format." }
});
}
return capabilities;
}
/** Validates a built-in pass input declared as `texture_2d<f32>`. */
function assertFloatSampledFormat(input) {
let capabilities;
try {
assertTextureFormat(input.format);
capabilities = resolveTextureFormatCapabilities(input.format, input.deviceFeatures);
} catch {
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: "recognized GPUTextureFormat"
});
}
if (!capabilities.supported) {
const requiredFeature = capabilities.requiredFeatures.format;
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: requiredFeature,
detail: `Enable device feature "${requiredFeature}".`
});
}
if (capabilities.sampleType !== "float" && capabilities.sampleType !== "unfilterable-float") throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: "float texture sampling",
detail: "This built-in pass declares texture_2d<f32>; use a float-sampled format or a custom typed pass."
});
return capabilities;
}
/** Validates a built-in pass output written by a `vec4f` fragment result. */
function assertFloatRenderableFormat(input) {
const capabilities = assertRenderableFormat(input);
if (capabilities.sampleType !== "float" && capabilities.sampleType !== "unfilterable-float") throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: "float color render attachment",
detail: "This built-in pass writes vec4f; use a float/unorm/snorm format or a custom typed pass."
});
return capabilities;
}
/** Validates a storage texture access against the active device. */
function assertStorageTextureAccess(input) {
let capabilities;
try {
assertTextureFormat(input.format);
capabilities = resolveTextureFormatCapabilities(input.format, input.deviceFeatures);
} catch {
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: "recognized GPUTextureFormat"
});
}
if (!capabilities.storageAccess.includes(input.access)) {
const requiredFeature = capabilities.requiredFeatures.format ?? capabilities.requiredFeatures.storageAccess?.[input.access];
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: requiredFeature ?? `storage texture ${input.access}`,
...requiredFeature ? { detail: `Enable device feature "${requiredFeature}".` } : { detail: `The format does not support ${input.access} storage access.` }
});
}
return capabilities;
}
/** Validates that a device supports the format itself, independent of a specific usage. */
function assertTextureFormatSupported(input) {
let capabilities;
try {
assertTextureFormat(input.format);
capabilities = resolveTextureFormatCapabilities(input.format, input.deviceFeatures);
} catch {
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: "recognized GPUTextureFormat"
});
}
if (!capabilities.supported) {
const requiredFeature = capabilities.requiredFeatures.format;
throw createFormatCapabilityError({
target: input.target,
format: input.format,
pass: input.pass,
capability: requiredFeature,
detail: `Enable device feature "${requiredFeature}".`
});
}
return capabilities;
}
//#endregion
export { TEXTURE_FORMATS, assertFloatRenderableFormat, assertFloatSampledFormat, assertRenderableFormat, assertStorageTextureAccess, assertTextureFormat, assertTextureFormatSupported, createFormatCapabilityError, isFloat32TextureFormat, isStorageTextureFormat, resolveTextureFormatCapabilities, textureSampleScalarType };
//# sourceMappingURL=format-capabilities.js.map