typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
247 lines (246 loc) • 8.37 kB
JavaScript
import { $internal, $repr } from "../shared/symbols.js";
import { f32 } from "./numeric.js";
export function textureDescriptorToSchema(desc) {
if ('multisampled' in desc) {
if (desc.multisampled) {
if (desc.dimension === '2d') {
return textureMultisampled2d(desc.sampleType);
}
throw new Error(`Multisampled textures only support '2d' dimension, got '${desc.dimension}'`);
}
switch (desc.dimension) {
case '1d':
return texture1d(desc.sampleType);
case '2d':
return texture2d(desc.sampleType);
case '2d-array':
return texture2dArray(desc.sampleType);
case '3d':
return texture3d(desc.sampleType);
case 'cube':
return textureCube(desc.sampleType);
case 'cube-array':
return textureCubeArray(desc.sampleType);
default:
throw new Error(
// @ts-expect-error
`Unsupported texture dimension: '${desc.dimension}'`);
}
}
if (!('access' in desc)) {
throw new Error('Descriptor is neither a sampled nor a storage texture');
}
switch (desc.dimension) {
case '1d':
return textureStorage1d(desc.format, desc.access);
case '2d':
return textureStorage2d(desc.format, desc.access);
case '2d-array':
return textureStorage2dArray(desc.format, desc.access);
case '3d':
return textureStorage3d(desc.format, desc.access);
default:
throw new Error(
// @ts-expect-error
`Unsupported storage texture dimension: '${desc.dimension}'`);
}
}
function createTexture(type, props) {
const isDepth = type.startsWith('texture_depth');
const sampleTypes = isDepth
? ['depth', 'float', 'unfilterable-float']
: props.sampleType.type === 'i32'
? ['sint']
: props.sampleType.type === 'u32'
? ['uint']
: ['float', 'unfilterable-float'];
return {
[$internal]: {},
[$repr]: undefined,
type,
bindingSampleType: sampleTypes,
...props,
};
}
function createStorageTexture(type, props) {
return {
[$internal]: {},
[$repr]: undefined,
type,
...props,
};
}
const textureCache = new Map();
export const accessModeMap = {
'write-only': 'write',
'read-only': 'read',
'read-write': 'read_write',
};
function getOrCreate(key, factory) {
let cached = textureCache.get(key);
if (!cached) {
cached = factory();
textureCache.set(key, cached);
}
return cached;
}
export function texture1d(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_1d<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_1d', {
dimension: '1d',
sampleType: actualSampleType,
multisampled: false,
}));
}
export function texture2d(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_2d<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_2d', {
dimension: '2d',
sampleType: actualSampleType,
multisampled: false,
}));
}
export function textureMultisampled2d(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_multisampled_2d<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_multisampled_2d', {
dimension: '2d',
sampleType: actualSampleType,
multisampled: true,
}));
}
export function texture2dArray(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_2d_array<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_2d_array', {
dimension: '2d-array',
sampleType: actualSampleType,
multisampled: false,
}));
}
export function textureCube(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_cube<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_cube', {
dimension: 'cube',
sampleType: actualSampleType,
multisampled: false,
}));
}
export function textureCubeArray(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_cube_array<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_cube_array', {
dimension: 'cube-array',
sampleType: actualSampleType,
multisampled: false,
}));
}
export function texture3d(sampleType) {
const actualSampleType = (sampleType || f32);
const key = `texture_3d<${actualSampleType.type}>`;
return getOrCreate(key, () => createTexture('texture_3d', {
dimension: '3d',
sampleType: actualSampleType,
multisampled: false,
}));
}
export function textureStorage1d(format, access) {
const actualAccess = (access || 'write-only');
const key = `texture_storage_1d<${format}, ${accessModeMap[actualAccess]}>`;
return getOrCreate(key, () => createStorageTexture('texture_storage_1d', {
dimension: '1d',
format,
access: actualAccess,
}));
}
export function textureStorage2d(format, access) {
const actualAccess = (access || 'write-only');
const key = `texture_storage_2d<${format}, ${accessModeMap[actualAccess]}>`;
return getOrCreate(key, () => createStorageTexture('texture_storage_2d', {
dimension: '2d',
format,
access: actualAccess,
}));
}
export function textureStorage2dArray(format, access) {
const actualAccess = (access || 'write-only');
const key = `texture_storage_2d_array<${format}, ${accessModeMap[actualAccess]}>`;
return getOrCreate(key, () => createStorageTexture('texture_storage_2d_array', {
dimension: '2d-array',
format,
access: actualAccess,
}));
}
export function textureStorage3d(format, access) {
const actualAccess = (access || 'write-only');
const key = `texture_storage_3d<${format}, ${accessModeMap[actualAccess]}>`;
return getOrCreate(key, () => createStorageTexture('texture_storage_3d', {
dimension: '3d',
format,
access: actualAccess,
}));
}
export function textureDepth2d() {
const key = 'texture_depth_2d';
return getOrCreate(key, () => createTexture('texture_depth_2d', {
dimension: '2d',
sampleType: f32,
multisampled: false,
}));
}
export function textureDepthMultisampled2d() {
const key = 'texture_depth_multisampled_2d';
return getOrCreate(key, () => createTexture('texture_depth_multisampled_2d', {
dimension: '2d',
sampleType: f32,
multisampled: true,
}));
}
export function textureDepth2dArray() {
const key = 'texture_depth_2d_array';
return getOrCreate(key, () => createTexture('texture_depth_2d_array', {
dimension: '2d-array',
sampleType: f32,
multisampled: false,
}));
}
export function textureDepthCube() {
const key = 'texture_depth_cube';
return getOrCreate(key, () => createTexture('texture_depth_cube', {
dimension: 'cube',
sampleType: f32,
multisampled: false,
}));
}
export function textureDepthCubeArray() {
const key = 'texture_depth_cube_array';
return getOrCreate(key, () => createTexture('texture_depth_cube_array', {
dimension: 'cube-array',
sampleType: f32,
multisampled: false,
}));
}
export function textureExternal() {
const key = 'texture_external';
return getOrCreate(key, () => ({
[$internal]: {},
[$repr]: undefined,
type: 'texture_external',
dimension: '2d',
}));
}
export function isWgslTexture(value) {
return (!!value[$internal] && typeof value.multisampled === 'boolean');
}
export function isWgslStorageTexture(value) {
return (!!value[$internal] &&
typeof value.format === 'string' &&
typeof value.access === 'string');
}
export function isWgslExternalTexture(value) {
return (!!value[$internal] &&
value.type === 'texture_external');
}