blob: ab1b08e12a93bb5a0b8597a23a77384a4055fdcd [file] [log] [blame]
import {
AccessMode,
AddressSpace,
AddressSpaceInfo,
kAccessModeInfo,
kAddressSpaceInfo,
} from '../../types.js';
/** An enumerator of shader stages */
export type ShaderStage = 'vertex' | 'fragment' | 'compute';
/** The list of all shader stages */
export const kShaderStages = ['vertex', 'fragment', 'compute'] as const;
/**
* declareEntrypoint emits the WGSL to declare an entry point with the name, stage and body.
* The generated function will have an appropriate return type and return statement, so that `body`
* does not have to change between stage.
* @param arg - arg specifies the
* optional entry point function name, the shader stage, and the body of the
* function, excluding any automatically generated return statements.
* @returns the WGSL string for the entry point
*/
export function declareEntryPoint(arg: {
name?: string;
stage: ShaderStage;
body: string;
}): string {
if (arg.name === undefined) {
arg.name = 'main';
}
switch (arg.stage) {
case 'vertex':
return `@vertex
fn ${arg.name}() -> @builtin(position) vec4f {
${arg.body}
return vec4f();
}`;
case 'fragment':
return `@fragment
fn ${arg.name}() {
${arg.body}
}`;
case 'compute':
return `@compute @workgroup_size(1)
fn ${arg.name}() {
${arg.body}
}`;
}
}
/**
* @returns a WGSL var declaration with given parameters for variable 'x' and
* store type i32.
*/
export function declareVarX(addressSpace: AddressSpace | '', accessMode: AccessMode | ''): string {
const parts: string[] = [];
if (addressSpace && kAddressSpaceInfo[addressSpace].binding) parts.push('@group(0) @binding(0) ');
parts.push('var');
const template_parts: string[] = [];
if (addressSpace) template_parts.push(addressSpace);
if (accessMode) template_parts.push(accessMode);
if (template_parts.length > 0) parts.push(`<${template_parts.join(',')}>`);
parts.push(' x: i32;');
return parts.join('');
}
/**
* @returns a list of booleans indicating valid cases of specifying the address
* space.
*/
export function explicitSpaceExpander(p: { addressSpace: AddressSpace }): readonly boolean[] {
const info = kAddressSpaceInfo[p.addressSpace];
return info.spell === 'must' ? [true] : [true, false];
}
/**
* @returns a list of usable access modes under given experiment conditions, or undefined
* if none are allowed.
*/
export function accessModeExpander(p: {
addressSpace: AddressSpace;
explicitAccess: boolean; // Whether the access mode will be emitted.
}): readonly (AccessMode | '')[] {
const info = kAddressSpaceInfo[p.addressSpace];
return p.explicitAccess && info.spellAccessMode !== 'never' ? info.accessModes : [''];
}
/**
* @returns a WGSL program with a single variable declaration, with the
* given parameterization
*/
export function getVarDeclShader(
p: {
addressSpace: AddressSpace; // Address space for the variable.
explicitSpace: boolean; // Should the address space be explicitly spelled?
accessMode: AccessMode | ''; // What access mode to use.
explicitAccess: boolean; // Should the access mode be explicitly spelled?
stage: ShaderStage; // What shader stage to use.
},
additionalBody?: string
): string {
const info = kAddressSpaceInfo[p.addressSpace];
const decl = declareVarX(
p.explicitSpace ? p.addressSpace : '',
p.explicitAccess ? p.accessMode : ''
);
additionalBody = additionalBody ?? '';
switch (info.scope) {
case 'module':
return decl + '\n' + declareEntryPoint({ stage: p.stage, body: additionalBody });
case 'function':
return declareEntryPoint({ stage: p.stage, body: decl + '\n' + additionalBody });
}
}
/**
* @returns the WGSL spelling of a pointer type corresponding to a variable
* declared with the given parameters.
*/
export function pointerType(p: {
addressSpace: AddressSpace; // Address space to use if p.explicitSpace
explicitSpace: boolean; // If false, use 'function' address space
accessMode: AccessMode | ''; // The access mode to use, if any
ptrStoreType: string; // The store type.
}): string {
const space = p.explicitSpace ? p.addressSpace : 'function';
const modePart = p.accessMode ? ',' + p.accessMode : '';
return `ptr<${space},${p.ptrStoreType}${modePart}>`;
}
/** @returns the effective access mode for the given experiment. */
export function effectiveAccessMode(
info: AddressSpaceInfo,
accessMode: AccessMode | ''
): AccessMode {
return accessMode || info.accessModes[0]; // default is first.
}
/** @returns whether the setup allows reads */
export function supportsRead(p: {
addressSpace: AddressSpace;
accessMode: AccessMode | '';
}): boolean {
const info = kAddressSpaceInfo[p.addressSpace];
const mode = effectiveAccessMode(info, p.accessMode);
return info.accessModes.includes(mode) && kAccessModeInfo[mode].read;
}
/** @returns whether the setup allows writes */
export function supportsWrite(p: {
addressSpace: AddressSpace;
accessMode: AccessMode | '';
}): boolean {
const info = kAddressSpaceInfo[p.addressSpace];
const mode = effectiveAccessMode(info, p.accessMode);
return info.accessModes.includes(mode) && kAccessModeInfo[mode].write;
}