typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
217 lines (216 loc) • 8.72 kB
JavaScript
import { stitch } from "../core/resolve/stitch.js";
import { isWgslExternalTexture, isWgslTexture, } from "../data/texture.js";
import { dualImpl, MissingCpuImplError } from "../core/function/dualImpl.js";
import { f32, i32, u32 } from "../data/numeric.js";
import { vec2u, vec3u, vec4f, vec4i, vec4u } from "../data/vector.js";
import { Void, } from "../data/wgslTypes.js";
import { getTextureFormatInfo, } from "../core/texture/textureFormats.js";
function compactSnippetArgs(args) {
return args.filter((arg) => arg !== undefined);
}
function sampleCpu(_texture, _sampler, _coords, _offsetOrArrayIndex, _maybeOffset) {
throw new MissingCpuImplError('Texture sampling relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureSample = dualImpl({
name: 'textureSample',
normalImpl: sampleCpu,
codegenImpl: (ctx, args) => ctx.gen.emitCall('textureSample', [], compactSnippetArgs(args)),
signature: (...args) => {
const isDepth = args[0].type.startsWith('texture_depth');
return {
argTypes: args,
returnType: isDepth ? f32 : vec4f,
};
},
sideEffects: false,
});
function sampleBiasCpu(_texture, _sampler, _coords, _biasOrArrayIndex, _biasOrOffset, _maybeOffset) {
throw new MissingCpuImplError('Texture sampling with bias relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureSampleBias = dualImpl({
name: 'textureSampleBias',
normalImpl: sampleBiasCpu,
codegenImpl: (ctx, args) => ctx.gen.emitCall('textureSampleBias', [], compactSnippetArgs(args)),
signature: (...args) => ({
argTypes: args,
returnType: vec4f,
}),
sideEffects: false,
});
function sampleLevelCpu(_texture, _sampler, _coords, _level, _offsetOrArrayIndex, _maybeOffset) {
throw new MissingCpuImplError('Texture sampling relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureSampleLevel = dualImpl({
name: 'textureSampleLevel',
normalImpl: sampleLevelCpu,
codegenImpl: (ctx, args) => ctx.gen.emitCall('textureSampleLevel', [], compactSnippetArgs(args)),
signature: (...args) => {
const isDepth = args[0].type.startsWith('texture_depth');
return {
argTypes: args,
returnType: isDepth ? f32 : vec4f,
};
},
sideEffects: false,
});
function textureLoadCpu(_texture, _coords, _levelOrArrayIndex) {
throw new MissingCpuImplError('`textureLoad` relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureLoad = dualImpl({
name: 'textureLoad',
normalImpl: textureLoadCpu,
codegenImpl: (ctx, args) => ctx.gen.emitCall('textureLoad', [], compactSnippetArgs(args)),
signature: (...args) => {
const texture = args[0];
if (isWgslTexture(texture)) {
const isDepth = texture.type.startsWith('texture_depth');
const sampleType = texture.sampleType;
return {
argTypes: args,
returnType: isDepth
? f32
: sampleType.type === 'f32'
? vec4f
: sampleType.type === 'u32'
? vec4u
: vec4i,
};
}
if (isWgslExternalTexture(texture)) {
return {
argTypes: args,
returnType: vec4f,
};
}
const format = texture.format;
const dataType = getTextureFormatInfo(format).vectorType;
return {
argTypes: args,
returnType: dataType,
};
},
sideEffects: false,
});
function textureStoreCpu(_texture, _coords, _arrayIndexOrValue, _maybeValue) {
throw new MissingCpuImplError('`textureStore` relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureStore = dualImpl({
name: 'textureStore',
normalImpl: textureStoreCpu,
codegenImpl: (_ctx, args) => stitch `textureStore(${args})`,
signature: (...args) => ({ argTypes: args, returnType: Void }),
sideEffects: true,
});
function textureDimensionsCpu(_texture, _level) {
throw new MissingCpuImplError('`textureDimensions` relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureDimensions = dualImpl({
name: 'textureDimensions',
normalImpl: textureDimensionsCpu,
codegenImpl: (ctx, args) => ctx.gen.emitCall('textureDimensions', [], compactSnippetArgs(args)),
signature: (...args) => {
const dim = args[0].dimension;
if (dim === '1d') {
return {
argTypes: args,
returnType: u32,
};
}
if (dim === '3d') {
return {
argTypes: args,
returnType: vec3u,
};
}
return {
argTypes: args,
returnType: vec2u,
};
},
sideEffects: false,
});
export const textureGatherCpu = (..._args) => {
throw new Error('Texture gather relies on GPU resources and cannot be executed outside of a draw call');
};
const sampleTypeToVecType = {
f32: vec4f,
i32: vec4i,
u32: vec4u,
};
export const textureGather = dualImpl({
name: 'textureGather',
normalImpl: textureGatherCpu,
codegenImpl: (_ctx, args) => stitch `textureGather(${args})`,
signature: (...args) => {
if (args[0].type.startsWith('texture')) {
const [texture, sampler, coords, _, ...rest] = args;
const isArrayTexture = texture.type === 'texture_depth_2d_array' || texture.type === 'texture_depth_cube_array';
const argTypes = isArrayTexture
? [texture, sampler, coords, [u32, i32], ...rest]
: args;
return { argTypes: argTypes, returnType: vec4f };
}
const [_, texture, sampler, coords, ...rest] = args;
const isArrayTexture = texture.type === 'texture_2d_array' || texture.type === 'texture_cube_array';
const argTypes = isArrayTexture
? [[u32, i32], texture, sampler, coords, [u32, i32], ...rest]
: [[u32, i32], texture, sampler, coords, ...rest];
return {
argTypes: argTypes,
returnType: sampleTypeToVecType[texture.sampleType.type],
};
},
sideEffects: false,
});
function textureSampleCompareCpu(_texture, _sampler, _coords, _depthRefOrArrayIndex, _depthRefOrOffset, _maybeOffset) {
throw new MissingCpuImplError('Texture comparison sampling relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureSampleCompare = dualImpl({
name: 'textureSampleCompare',
normalImpl: textureSampleCompareCpu,
codegenImpl: (_ctx, args) => stitch `textureSampleCompare(${args})`,
signature: (...args) => ({
argTypes: args,
returnType: f32,
}),
sideEffects: false,
});
function textureSampleCompareLevelCpu(_texture, _sampler, _coords, _depthRefOrArrayIndex, _depthRefOrOffset, _maybeOffset) {
throw new MissingCpuImplError('Texture comparison sampling with level relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureSampleCompareLevel = dualImpl({
name: 'textureSampleCompareLevel',
normalImpl: textureSampleCompareLevelCpu,
codegenImpl: (ctx, args) => stitch `textureSampleCompareLevel(${args})`,
signature: (...args) => ({
argTypes: args,
returnType: f32,
}),
sideEffects: false,
});
function textureSampleBaseClampToEdgeCpu(_texture, _sampler, _coords) {
throw new MissingCpuImplError('Texture sampling with base clamp to edge is not supported outside of GPU mode.');
}
function sampleGradCpu(_texture, _sampler, _coords, _ddxOrArrayIndex, _ddyOrDdx, _offsetOrDdy, _maybeOffset) {
throw new MissingCpuImplError('Texture sampling with gradients relies on GPU resources and cannot be executed outside of a draw call');
}
export const textureSampleGrad = dualImpl({
name: 'textureSampleGrad',
normalImpl: sampleGradCpu,
codegenImpl: (_ctx, args) => stitch `textureSampleGrad(${args})`,
signature: (...args) => ({
argTypes: args,
returnType: vec4f,
}),
sideEffects: false,
});
export const textureSampleBaseClampToEdge = dualImpl({
name: 'textureSampleBaseClampToEdge',
normalImpl: textureSampleBaseClampToEdgeCpu,
codegenImpl: (_ctx, args) => stitch `textureSampleBaseClampToEdge(${args})`,
signature: (...args) => ({
argTypes: args,
returnType: vec4f,
}),
sideEffects: false,
});