blob: 1bd5d3b7c698f869599b23809a88414761c6bcb6 [file] [log] [blame] [edit]
// MAINTENANCE_TODO: The generated Typedoc for this file is hard to navigate because it's
// alphabetized. Consider using namespaces or renames to fix this?
/* eslint-disable no-sparse-arrays */
import {
keysOf,
makeTable,
makeTableRenameAndFilter,
numericKeysOf,
valueof,
} from '../common/util/data_tables.js';
import { assertTypeTrue, TypeEqual } from '../common/util/types.js';
import { unreachable } from '../common/util/util.js';
import { GPUConst, kMaxUnsignedLongValue, kMaxUnsignedLongLongValue } from './constants.js';
// Base device limits can be found in constants.ts.
// Queries
/** Maximum number of queries in GPUQuerySet, by spec. */
export const kMaxQueryCount = 4096;
/** Per-GPUQueryType info. */
export type QueryTypeInfo = {
/** Optional feature required to use this GPUQueryType. */
readonly feature: GPUFeatureName | undefined;
// Add fields as needed
};
export const kQueryTypeInfo: {
readonly [k in GPUQueryType]: QueryTypeInfo;
} =
/* prettier-ignore */ {
'occlusion': { feature: undefined },
'timestamp': { feature: 'timestamp-query' },
};
/** List of all GPUQueryType values. */
export const kQueryTypes = keysOf(kQueryTypeInfo);
// Buffers
/** Required alignment of a GPUBuffer size, by spec. */
export const kBufferSizeAlignment = 4;
/** Per-GPUBufferUsage copy info. */
export const kBufferUsageCopyInfo: {
readonly [name: string]: GPUBufferUsageFlags;
} =
/* prettier-ignore */ {
'COPY_NONE': 0,
'COPY_SRC': GPUConst.BufferUsage.COPY_SRC,
'COPY_DST': GPUConst.BufferUsage.COPY_DST,
'COPY_SRC_DST': GPUConst.BufferUsage.COPY_SRC | GPUConst.BufferUsage.COPY_DST,
};
/** List of all GPUBufferUsage copy values. */
export const kBufferUsageCopy = keysOf(kBufferUsageCopyInfo);
/** Per-GPUBufferUsage keys and info. */
type BufferUsageKey = keyof typeof GPUConst.BufferUsage;
export const kBufferUsageKeys = keysOf(GPUConst.BufferUsage);
export const kBufferUsageInfo: {
readonly [k in BufferUsageKey]: GPUBufferUsageFlags;
} = {
...GPUConst.BufferUsage,
};
/** List of all GPUBufferUsage values. */
export const kBufferUsages = Object.values(GPUConst.BufferUsage);
export const kAllBufferUsageBits = kBufferUsages.reduce(
(previousSet, currentUsage) => previousSet | currentUsage,
0
);
// Errors
/** Per-GPUErrorFilter info. */
export const kErrorScopeFilterInfo: {
readonly [k in GPUErrorFilter]: {
generatable: boolean;
};
} =
/* prettier-ignore */ {
'internal': { generatable: false },
'out-of-memory': { generatable: true },
'validation': { generatable: true },
};
/** List of all GPUErrorFilter values. */
export const kErrorScopeFilters = keysOf(kErrorScopeFilterInfo);
export const kGeneratableErrorScopeFilters = kErrorScopeFilters.filter(
e => kErrorScopeFilterInfo[e].generatable
);
// Canvases
// The formats of GPUTextureFormat for canvas context.
export const kCanvasTextureFormats = ['bgra8unorm', 'rgba8unorm', 'rgba16float'] as const;
// The alpha mode for canvas context.
export const kCanvasAlphaModesInfo: {
readonly [k in GPUCanvasAlphaMode]: {};
} = /* prettier-ignore */ {
'opaque': {},
'premultiplied': {},
};
export const kCanvasAlphaModes = keysOf(kCanvasAlphaModesInfo);
// The color spaces for canvas context
export const kCanvasColorSpacesInfo: {
readonly [k in PredefinedColorSpace]: {};
} = /* prettier-ignore */ {
'srgb': {},
'display-p3': {},
};
export const kCanvasColorSpaces = keysOf(kCanvasColorSpacesInfo);
// Textures (except for texture format info)
/** Per-GPUTextureDimension info. */
export const kTextureDimensionInfo: {
readonly [k in GPUTextureDimension]: {};
} = /* prettier-ignore */ {
'1d': {},
'2d': {},
'3d': {},
};
/** List of all GPUTextureDimension values. */
export const kTextureDimensions = keysOf(kTextureDimensionInfo);
/** Per-GPUTextureAspect info. */
export const kTextureAspectInfo: {
readonly [k in GPUTextureAspect]: {};
} = /* prettier-ignore */ {
'all': {},
'depth-only': {},
'stencil-only': {},
};
/** List of all GPUTextureAspect values. */
export const kTextureAspects = keysOf(kTextureAspectInfo);
// Misc
/** Per-GPUCompareFunction info. */
export const kCompareFunctionInfo: {
readonly [k in GPUCompareFunction]: {};
} =
/* prettier-ignore */ {
'never': {},
'less': {},
'equal': {},
'less-equal': {},
'greater': {},
'not-equal': {},
'greater-equal': {},
'always': {},
};
/** List of all GPUCompareFunction values. */
export const kCompareFunctions = keysOf(kCompareFunctionInfo);
/** Per-GPUStencilOperation info. */
export const kStencilOperationInfo: {
readonly [k in GPUStencilOperation]: {};
} =
/* prettier-ignore */ {
'keep': {},
'zero': {},
'replace': {},
'invert': {},
'increment-clamp': {},
'decrement-clamp': {},
'increment-wrap': {},
'decrement-wrap': {},
};
/** List of all GPUStencilOperation values. */
export const kStencilOperations = keysOf(kStencilOperationInfo);
// More textures (except for texture format info)
/** Per-GPUTextureUsage type info. */
export const kTextureUsageTypeInfo: {
readonly [name: string]: number;
} =
/* prettier-ignore */ {
'texture': Number(GPUConst.TextureUsage.TEXTURE_BINDING),
'storage': Number(GPUConst.TextureUsage.STORAGE_BINDING),
'render': Number(GPUConst.TextureUsage.RENDER_ATTACHMENT),
};
/** List of all GPUTextureUsage type values. */
export const kTextureUsageType = keysOf(kTextureUsageTypeInfo);
/** Per-GPUTextureUsage copy info. */
export const kTextureUsageCopyInfo: {
readonly [name: string]: number;
} =
/* prettier-ignore */ {
'none': 0,
'src': Number(GPUConst.TextureUsage.COPY_SRC),
'dst': Number(GPUConst.TextureUsage.COPY_DST),
'src-dest': Number(GPUConst.TextureUsage.COPY_SRC) | Number(GPUConst.TextureUsage.COPY_DST),
};
/** List of all GPUTextureUsage copy values. */
export const kTextureUsageCopy = keysOf(kTextureUsageCopyInfo);
/** Per-GPUTextureUsage info. */
export const kTextureUsageInfo: {
readonly [k in valueof<typeof GPUConst.TextureUsage>]: {};
} = {
[GPUConst.TextureUsage.COPY_SRC]: {},
[GPUConst.TextureUsage.COPY_DST]: {},
[GPUConst.TextureUsage.TEXTURE_BINDING]: {},
[GPUConst.TextureUsage.STORAGE_BINDING]: {},
[GPUConst.TextureUsage.RENDER_ATTACHMENT]: {},
};
/** List of all GPUTextureUsage values. */
export const kTextureUsages = numericKeysOf<GPUTextureUsageFlags>(kTextureUsageInfo);
// Texture View
/** Per-GPUTextureViewDimension info. */
export type TextureViewDimensionInfo = {
/** Whether a storage texture view can have this view dimension. */
readonly storage: boolean;
// Add fields as needed
};
/** Per-GPUTextureViewDimension info. */
export const kTextureViewDimensionInfo: {
readonly [k in GPUTextureViewDimension]: TextureViewDimensionInfo;
} =
/* prettier-ignore */ {
'1d': { storage: true },
'2d': { storage: true },
'2d-array': { storage: true },
'cube': { storage: false },
'cube-array': { storage: false },
'3d': { storage: true },
};
/** List of all GPUTextureDimension values. */
export const kTextureViewDimensions = keysOf(kTextureViewDimensionInfo);
// Vertex formats
/** Per-GPUVertexFormat info. */
// Exists just for documentation. Otherwise could be inferred by `makeTable`.
export type VertexFormatInfo = {
/** Number of bytes in each component. */
readonly bytesPerComponent: 1 | 2 | 4 | 'packed';
/** The data encoding (float, normalized, or integer) for each component. */
readonly type: 'float' | 'unorm' | 'snorm' | 'uint' | 'sint';
/** Number of components. */
readonly componentCount: 1 | 2 | 3 | 4;
/** Size in bytes. */
readonly byteSize: 2 | 4 | 8 | 12 | 16;
/** The completely matching WGSL type for vertex format */
readonly wgslType:
| 'f32'
| 'vec2<f32>'
| 'vec3<f32>'
| 'vec4<f32>'
| 'u32'
| 'vec2<u32>'
| 'vec3<u32>'
| 'vec4<u32>'
| 'i32'
| 'vec2<i32>'
| 'vec3<i32>'
| 'vec4<i32>';
// Add fields as needed
};
/** Per-GPUVertexFormat info. */
export const kVertexFormatInfo: {
readonly [k in GPUVertexFormat]: VertexFormatInfo;
} =
/* prettier-ignore */ makeTable(
['bytesPerComponent', 'type', 'componentCount', 'byteSize', 'wgslType'] as const,
[ , , , , ] as const, {
// 8 bit components
'uint8x2': [ 1, 'uint', 2, 2, 'vec2<u32>'],
'uint8x4': [ 1, 'uint', 4, 4, 'vec4<u32>'],
'sint8x2': [ 1, 'sint', 2, 2, 'vec2<i32>'],
'sint8x4': [ 1, 'sint', 4, 4, 'vec4<i32>'],
'unorm8x2': [ 1, 'unorm', 2, 2, 'vec2<f32>'],
'unorm8x4': [ 1, 'unorm', 4, 4, 'vec4<f32>'],
'snorm8x2': [ 1, 'snorm', 2, 2, 'vec2<f32>'],
'snorm8x4': [ 1, 'snorm', 4, 4, 'vec4<f32>'],
// 16 bit components
'uint16x2': [ 2, 'uint', 2, 4, 'vec2<u32>'],
'uint16x4': [ 2, 'uint', 4, 8, 'vec4<u32>'],
'sint16x2': [ 2, 'sint', 2, 4, 'vec2<i32>'],
'sint16x4': [ 2, 'sint', 4, 8, 'vec4<i32>'],
'unorm16x2': [ 2, 'unorm', 2, 4, 'vec2<f32>'],
'unorm16x4': [ 2, 'unorm', 4, 8, 'vec4<f32>'],
'snorm16x2': [ 2, 'snorm', 2, 4, 'vec2<f32>'],
'snorm16x4': [ 2, 'snorm', 4, 8, 'vec4<f32>'],
'float16x2': [ 2, 'float', 2, 4, 'vec2<f32>'],
'float16x4': [ 2, 'float', 4, 8, 'vec4<f32>'],
// 32 bit components
'float32': [ 4, 'float', 1, 4, 'f32'],
'float32x2': [ 4, 'float', 2, 8, 'vec2<f32>'],
'float32x3': [ 4, 'float', 3, 12, 'vec3<f32>'],
'float32x4': [ 4, 'float', 4, 16, 'vec4<f32>'],
'uint32': [ 4, 'uint', 1, 4, 'u32'],
'uint32x2': [ 4, 'uint', 2, 8, 'vec2<u32>'],
'uint32x3': [ 4, 'uint', 3, 12, 'vec3<u32>'],
'uint32x4': [ 4, 'uint', 4, 16, 'vec4<u32>'],
'sint32': [ 4, 'sint', 1, 4, 'i32'],
'sint32x2': [ 4, 'sint', 2, 8, 'vec2<i32>'],
'sint32x3': [ 4, 'sint', 3, 12, 'vec3<i32>'],
'sint32x4': [ 4, 'sint', 4, 16, 'vec4<i32>'],
// 32 bit packed
'unorm10-10-10-2': [ 'packed', 'unorm', 4, 4, 'vec4<f32>']
} as const);
/** List of all GPUVertexFormat values. */
export const kVertexFormats = keysOf(kVertexFormatInfo);
// Typedefs for bindings
/**
* Classes of `PerShaderStage` binding limits. Two bindings with the same class
* count toward the same `PerShaderStage` limit(s) in the spec (if any).
*/
export type PerStageBindingLimitClass =
| 'uniformBuf'
| 'storageBuf'
| 'sampler'
| 'sampledTex'
| 'readonlyStorageTex'
| 'writeonlyStorageTex'
| 'readwriteStorageTex';
/**
* Classes of `PerPipelineLayout` binding limits. Two bindings with the same class
* count toward the same `PerPipelineLayout` limit(s) in the spec (if any).
*/
export type PerPipelineBindingLimitClass = PerStageBindingLimitClass;
export type ValidBindableResource =
| 'uniformBuf'
| 'storageBuf'
| 'filtSamp'
| 'nonFiltSamp'
| 'compareSamp'
| 'sampledTex'
| 'sampledTexMS'
| 'readonlyStorageTex'
| 'writeonlyStorageTex'
| 'readwriteStorageTex';
type ErrorBindableResource = 'errorBuf' | 'errorSamp' | 'errorTex';
/**
* Types of resource binding which have distinct binding rules, by spec
* (e.g. filtering vs non-filtering sampler, multisample vs non-multisample texture).
*/
export type BindableResource = ValidBindableResource | ErrorBindableResource;
export const kBindableResources = [
'uniformBuf',
'storageBuf',
'filtSamp',
'nonFiltSamp',
'compareSamp',
'sampledTex',
'sampledTexMS',
'readonlyStorageTex',
'writeonlyStorageTex',
'readwriteStorageTex',
'errorBuf',
'errorSamp',
'errorTex',
] as const;
assertTypeTrue<TypeEqual<BindableResource, (typeof kBindableResources)[number]>>();
// Bindings
/** Dynamic buffer offsets require offset to be divisible by 256, by spec. */
export const kMinDynamicBufferOffsetAlignment = 256;
/** Default `PerShaderStage` binding limits, by spec. */
export const kPerStageBindingLimits: {
readonly [k in PerStageBindingLimitClass]: {
/** Which `PerShaderStage` binding limit class. */
readonly class: k;
/** Maximum number of allowed bindings in that class. */
readonly maxLimit: (typeof kLimits)[number];
// Add fields as needed
};
} =
/* prettier-ignore */ {
'uniformBuf': { class: 'uniformBuf', maxLimit: 'maxUniformBuffersPerShaderStage', },
'storageBuf': { class: 'storageBuf', maxLimit: 'maxStorageBuffersPerShaderStage', },
'sampler': { class: 'sampler', maxLimit: 'maxSamplersPerShaderStage', },
'sampledTex': { class: 'sampledTex', maxLimit: 'maxSampledTexturesPerShaderStage', },
'readonlyStorageTex': { class: 'readonlyStorageTex', maxLimit: 'maxStorageTexturesPerShaderStage', },
'writeonlyStorageTex': { class: 'writeonlyStorageTex', maxLimit: 'maxStorageTexturesPerShaderStage', },
'readwriteStorageTex': { class: 'readwriteStorageTex', maxLimit: 'maxStorageTexturesPerShaderStage', },
};
/**
* Default `PerPipelineLayout` binding limits, by spec.
*/
export const kPerPipelineBindingLimits: {
readonly [k in PerPipelineBindingLimitClass]: {
/** Which `PerPipelineLayout` binding limit class. */
readonly class: k;
/**
* The name of the limit for the maximum number of allowed bindings with `hasDynamicOffset: true` in that class.
*/
readonly maxDynamicLimit: (typeof kLimits)[number] | '';
// Add fields as needed
};
} =
/* prettier-ignore */ {
'uniformBuf': { class: 'uniformBuf', maxDynamicLimit: 'maxDynamicUniformBuffersPerPipelineLayout', },
'storageBuf': { class: 'storageBuf', maxDynamicLimit: 'maxDynamicStorageBuffersPerPipelineLayout', },
'sampler': { class: 'sampler', maxDynamicLimit: '', },
'sampledTex': { class: 'sampledTex', maxDynamicLimit: '', },
'readonlyStorageTex': { class: 'readonlyStorageTex', maxDynamicLimit: '', },
'writeonlyStorageTex': { class: 'writeonlyStorageTex', maxDynamicLimit: '', },
'readwriteStorageTex': { class: 'readwriteStorageTex', maxDynamicLimit: '', },
};
interface BindingKindInfo {
readonly resource: ValidBindableResource;
readonly perStageLimitClass: (typeof kPerStageBindingLimits)[PerStageBindingLimitClass];
readonly perPipelineLimitClass: (typeof kPerPipelineBindingLimits)[PerPipelineBindingLimitClass];
// Add fields as needed
}
const kBindingKind: {
readonly [k in ValidBindableResource]: BindingKindInfo;
} =
/* prettier-ignore */ {
uniformBuf: { resource: 'uniformBuf', perStageLimitClass: kPerStageBindingLimits.uniformBuf, perPipelineLimitClass: kPerPipelineBindingLimits.uniformBuf, },
storageBuf: { resource: 'storageBuf', perStageLimitClass: kPerStageBindingLimits.storageBuf, perPipelineLimitClass: kPerPipelineBindingLimits.storageBuf, },
filtSamp: { resource: 'filtSamp', perStageLimitClass: kPerStageBindingLimits.sampler, perPipelineLimitClass: kPerPipelineBindingLimits.sampler, },
nonFiltSamp: { resource: 'nonFiltSamp', perStageLimitClass: kPerStageBindingLimits.sampler, perPipelineLimitClass: kPerPipelineBindingLimits.sampler, },
compareSamp: { resource: 'compareSamp', perStageLimitClass: kPerStageBindingLimits.sampler, perPipelineLimitClass: kPerPipelineBindingLimits.sampler, },
sampledTex: { resource: 'sampledTex', perStageLimitClass: kPerStageBindingLimits.sampledTex, perPipelineLimitClass: kPerPipelineBindingLimits.sampledTex, },
sampledTexMS: { resource: 'sampledTexMS', perStageLimitClass: kPerStageBindingLimits.sampledTex, perPipelineLimitClass: kPerPipelineBindingLimits.sampledTex, },
readonlyStorageTex: { resource: 'readonlyStorageTex', perStageLimitClass: kPerStageBindingLimits.readonlyStorageTex, perPipelineLimitClass: kPerPipelineBindingLimits.readonlyStorageTex, },
writeonlyStorageTex: { resource: 'writeonlyStorageTex', perStageLimitClass: kPerStageBindingLimits.writeonlyStorageTex, perPipelineLimitClass: kPerPipelineBindingLimits.writeonlyStorageTex, },
readwriteStorageTex: { resource: 'readwriteStorageTex', perStageLimitClass: kPerStageBindingLimits.readwriteStorageTex, perPipelineLimitClass: kPerPipelineBindingLimits.readwriteStorageTex, },
};
// Binding type info
const kValidStagesAll = {
validStages:
GPUConst.ShaderStage.VERTEX | GPUConst.ShaderStage.FRAGMENT | GPUConst.ShaderStage.COMPUTE,
} as const;
const kValidStagesStorageWrite = {
validStages: GPUConst.ShaderStage.FRAGMENT | GPUConst.ShaderStage.COMPUTE,
} as const;
/** Binding type info (including class limits) for the specified GPUBufferBindingLayout. */
export function bufferBindingTypeInfo(d: GPUBufferBindingLayout) {
/* prettier-ignore */
switch (d.type ?? 'uniform') {
case 'uniform': return { usage: GPUConst.BufferUsage.UNIFORM, ...kBindingKind.uniformBuf, ...kValidStagesAll, };
case 'storage': return { usage: GPUConst.BufferUsage.STORAGE, ...kBindingKind.storageBuf, ...kValidStagesStorageWrite, };
case 'read-only-storage': return { usage: GPUConst.BufferUsage.STORAGE, ...kBindingKind.storageBuf, ...kValidStagesAll, };
}
}
/** List of all GPUBufferBindingType values. */
export const kBufferBindingTypes = ['uniform', 'storage', 'read-only-storage'] as const;
assertTypeTrue<TypeEqual<GPUBufferBindingType, (typeof kBufferBindingTypes)[number]>>();
/** Binding type info (including class limits) for the specified GPUSamplerBindingLayout. */
export function samplerBindingTypeInfo(d: GPUSamplerBindingLayout) {
/* prettier-ignore */
switch (d.type ?? 'filtering') {
case 'filtering': return { ...kBindingKind.filtSamp, ...kValidStagesAll, };
case 'non-filtering': return { ...kBindingKind.nonFiltSamp, ...kValidStagesAll, };
case 'comparison': return { ...kBindingKind.compareSamp, ...kValidStagesAll, };
}
}
/** List of all GPUSamplerBindingType values. */
export const kSamplerBindingTypes = ['filtering', 'non-filtering', 'comparison'] as const;
assertTypeTrue<TypeEqual<GPUSamplerBindingType, (typeof kSamplerBindingTypes)[number]>>();
/** Binding type info (including class limits) for the specified GPUTextureBindingLayout. */
export function sampledTextureBindingTypeInfo(d: GPUTextureBindingLayout) {
/* prettier-ignore */
if (d.multisampled) {
return { usage: GPUConst.TextureUsage.TEXTURE_BINDING, ...kBindingKind.sampledTexMS, ...kValidStagesAll, };
} else {
return { usage: GPUConst.TextureUsage.TEXTURE_BINDING, ...kBindingKind.sampledTex, ...kValidStagesAll, };
}
}
/** List of all GPUTextureSampleType values. */
export const kTextureSampleTypes = [
'float',
'unfilterable-float',
'depth',
'sint',
'uint',
] as const;
assertTypeTrue<TypeEqual<GPUTextureSampleType, (typeof kTextureSampleTypes)[number]>>();
/** Binding type info (including class limits) for the specified GPUStorageTextureBindingLayout. */
export function storageTextureBindingTypeInfo(d: GPUStorageTextureBindingLayout) {
switch (d.access) {
case undefined:
case 'write-only':
return {
usage: GPUConst.TextureUsage.STORAGE_BINDING,
...kBindingKind.writeonlyStorageTex,
...kValidStagesStorageWrite,
};
case 'read-only':
return {
usage: GPUConst.TextureUsage.STORAGE_BINDING,
...kBindingKind.readonlyStorageTex,
...kValidStagesAll,
};
case 'read-write':
return {
usage: GPUConst.TextureUsage.STORAGE_BINDING,
...kBindingKind.readwriteStorageTex,
...kValidStagesStorageWrite,
};
}
}
/** List of all GPUStorageTextureAccess values. */
export const kStorageTextureAccessValues = ['read-only', 'read-write', 'write-only'] as const;
assertTypeTrue<TypeEqual<GPUStorageTextureAccess, (typeof kStorageTextureAccessValues)[number]>>();
/** GPUBindGroupLayoutEntry, but only the "union" fields, not the common fields. */
export type BGLEntry = Omit<GPUBindGroupLayoutEntry, 'binding' | 'visibility'>;
/** Binding type info (including class limits) for the specified BGLEntry. */
export function texBindingTypeInfo(e: BGLEntry) {
if (e.texture !== undefined) return sampledTextureBindingTypeInfo(e.texture);
if (e.storageTexture !== undefined) return storageTextureBindingTypeInfo(e.storageTexture);
unreachable();
}
/** BindingTypeInfo (including class limits) for the specified BGLEntry. */
export function bindingTypeInfo(e: BGLEntry) {
if (e.buffer !== undefined) return bufferBindingTypeInfo(e.buffer);
if (e.texture !== undefined) return sampledTextureBindingTypeInfo(e.texture);
if (e.sampler !== undefined) return samplerBindingTypeInfo(e.sampler);
if (e.storageTexture !== undefined) return storageTextureBindingTypeInfo(e.storageTexture);
unreachable('GPUBindGroupLayoutEntry has no BindingLayout');
}
/**
* Generate a list of possible buffer-typed BGLEntry values.
*
* Note: Generates different `type` options, but not `hasDynamicOffset` options.
*/
export function bufferBindingEntries(includeUndefined: boolean): readonly BGLEntry[] {
return [
...(includeUndefined ? [{ buffer: { type: undefined } }] : []),
{ buffer: { type: 'uniform' } },
{ buffer: { type: 'storage' } },
{ buffer: { type: 'read-only-storage' } },
] as const;
}
/** Generate a list of possible sampler-typed BGLEntry values. */
export function samplerBindingEntries(includeUndefined: boolean): readonly BGLEntry[] {
return [
...(includeUndefined ? [{ sampler: { type: undefined } }] : []),
{ sampler: { type: 'comparison' } },
{ sampler: { type: 'filtering' } },
{ sampler: { type: 'non-filtering' } },
] as const;
}
/**
* Generate a list of possible texture-typed BGLEntry values.
*
* Note: Generates different `multisampled` options, but not `sampleType` or `viewDimension` options.
*/
export function textureBindingEntries(includeUndefined: boolean): readonly BGLEntry[] {
return [
...(includeUndefined
? [{ texture: { multisampled: undefined, sampleType: 'unfilterable-float' } } as const]
: []),
{ texture: { multisampled: false, sampleType: 'unfilterable-float' } },
{ texture: { multisampled: true, sampleType: 'unfilterable-float' } },
] as const;
}
/**
* Generate a list of possible storageTexture-typed BGLEntry values.
*
* Note: Generates different `access` options, but not `format` or `viewDimension` options.
*/
export function storageTextureBindingEntries(): readonly BGLEntry[] {
return [
{ storageTexture: { access: 'write-only', format: 'r32float' } },
{ storageTexture: { access: 'read-only', format: 'r32float' } },
{ storageTexture: { access: 'read-write', format: 'r32float' } },
] as const;
}
/** Generate a list of possible texture-or-storageTexture-typed BGLEntry values. */
export function sampledAndStorageBindingEntries(includeUndefined: boolean): readonly BGLEntry[] {
return [...textureBindingEntries(includeUndefined), ...storageTextureBindingEntries()] as const;
}
/**
* Generate a list of possible BGLEntry values of every type, but not variants with different:
* - buffer.hasDynamicOffset
* - texture.sampleType
* - texture.viewDimension
* - storageTexture.viewDimension
*/
export function allBindingEntries(includeUndefined: boolean): readonly BGLEntry[] {
return [
...bufferBindingEntries(includeUndefined),
...samplerBindingEntries(includeUndefined),
...sampledAndStorageBindingEntries(includeUndefined),
] as const;
}
// Shader stages
/** List of all GPUShaderStage values. */
export type ShaderStageKey = keyof typeof GPUConst.ShaderStage;
export const kShaderStageKeys = Object.keys(GPUConst.ShaderStage) as ShaderStageKey[];
export const kShaderStages: readonly GPUShaderStageFlags[] = [
GPUConst.ShaderStage.VERTEX,
GPUConst.ShaderStage.FRAGMENT,
GPUConst.ShaderStage.COMPUTE,
];
/** List of all possible combinations of GPUShaderStage values. */
export const kShaderStageCombinations: readonly GPUShaderStageFlags[] = [0, 1, 2, 3, 4, 5, 6, 7];
export const kShaderStageCombinationsWithStage: readonly GPUShaderStageFlags[] = [
1, 2, 3, 4, 5, 6, 7,
];
/**
* List of all possible texture sampleCount values.
*
* MAINTENANCE_TODO: Switch existing tests to use kTextureSampleCounts
*/
export const kTextureSampleCounts = [1, 4] as const;
// Sampler info
/** List of all mipmap filter modes. */
export const kMipmapFilterModes: readonly GPUMipmapFilterMode[] = ['nearest', 'linear'];
assertTypeTrue<TypeEqual<GPUMipmapFilterMode, (typeof kMipmapFilterModes)[number]>>();
/** List of address modes. */
export const kAddressModes: readonly GPUAddressMode[] = [
'clamp-to-edge',
'repeat',
'mirror-repeat',
];
assertTypeTrue<TypeEqual<GPUAddressMode, (typeof kAddressModes)[number]>>();
// Blend factors and Blend components
/** List of all GPUBlendFactor values. */
export const kBlendFactors: readonly GPUBlendFactor[] = [
'zero',
'one',
'src',
'one-minus-src',
'src-alpha',
'one-minus-src-alpha',
'dst',
'one-minus-dst',
'dst-alpha',
'one-minus-dst-alpha',
'src-alpha-saturated',
'constant',
'one-minus-constant',
];
/** List of all GPUBlendOperation values. */
export const kBlendOperations: readonly GPUBlendOperation[] = [
'add', //
'subtract',
'reverse-subtract',
'min',
'max',
];
// Primitive topologies
export const kPrimitiveTopology: readonly GPUPrimitiveTopology[] = [
'point-list',
'line-list',
'line-strip',
'triangle-list',
'triangle-strip',
];
assertTypeTrue<TypeEqual<GPUPrimitiveTopology, (typeof kPrimitiveTopology)[number]>>();
export const kIndexFormat: readonly GPUIndexFormat[] = ['uint16', 'uint32'];
assertTypeTrue<TypeEqual<GPUIndexFormat, (typeof kIndexFormat)[number]>>();
/** Info for each entry of GPUSupportedLimits */
const [kLimitInfoKeys, kLimitInfoDefaults, kLimitInfoData] =
/* prettier-ignore */ [
[ 'class', 'core', 'compatibility', 'maximumValue'] as const,
[ 'maximum', , , kMaxUnsignedLongValue] as const, {
'maxTextureDimension1D': [ , 8192, 4096, ],
'maxTextureDimension2D': [ , 8192, 4096, ],
'maxTextureDimension3D': [ , 2048, 1024, ],
'maxTextureArrayLayers': [ , 256, 256, ],
'maxBindGroups': [ , 4, 4, ],
'maxBindGroupsPlusVertexBuffers': [ , 24, 24, ],
'maxBindingsPerBindGroup': [ , 1000, 1000, ],
'maxDynamicUniformBuffersPerPipelineLayout': [ , 8, 8, ],
'maxDynamicStorageBuffersPerPipelineLayout': [ , 4, 4, ],
'maxSampledTexturesPerShaderStage': [ , 16, 16, ],
'maxSamplersPerShaderStage': [ , 16, 16, ],
'maxStorageBuffersPerShaderStage': [ , 8, 4, ],
'maxStorageTexturesPerShaderStage': [ , 4, 4, ],
'maxUniformBuffersPerShaderStage': [ , 12, 12, ],
'maxUniformBufferBindingSize': [ , 65536, 16384, kMaxUnsignedLongLongValue],
'maxStorageBufferBindingSize': [ , 134217728, 134217728, kMaxUnsignedLongLongValue],
'minUniformBufferOffsetAlignment': ['alignment', 256, 256, ],
'minStorageBufferOffsetAlignment': ['alignment', 256, 256, ],
'maxVertexBuffers': [ , 8, 8, ],
'maxBufferSize': [ , 268435456, 268435456, kMaxUnsignedLongLongValue],
'maxVertexAttributes': [ , 16, 16, ],
'maxVertexBufferArrayStride': [ , 2048, 2048, ],
'maxInterStageShaderComponents': [ , 60, 60, ],
'maxInterStageShaderVariables': [ , 16, 15, ],
'maxColorAttachments': [ , 8, 4, ],
'maxColorAttachmentBytesPerSample': [ , 32, 32, ],
'maxComputeWorkgroupStorageSize': [ , 16384, 16384, ],
'maxComputeInvocationsPerWorkgroup': [ , 256, 128, ],
'maxComputeWorkgroupSizeX': [ , 256, 128, ],
'maxComputeWorkgroupSizeY': [ , 256, 128, ],
'maxComputeWorkgroupSizeZ': [ , 64, 64, ],
'maxComputeWorkgroupsPerDimension': [ , 65535, 65535, ],
} as const];
/**
* Feature levels corresponding to core WebGPU and WebGPU
* in compatibility mode. They can be passed to
* getDefaultLimits though if you have access to an adapter
* it's preferred to use getDefaultLimitsForAdapter.
*/
export const kFeatureLevels = ['core', 'compatibility'] as const;
export type FeatureLevel = (typeof kFeatureLevels)[number];
const kLimitKeys = ['class', 'default', 'maximumValue'] as const;
const kLimitInfoCore = makeTableRenameAndFilter(
{ default: 'core' },
kLimitKeys,
kLimitInfoKeys,
kLimitInfoDefaults,
kLimitInfoData
);
const kLimitInfoCompatibility = makeTableRenameAndFilter(
{ default: 'compatibility' },
kLimitKeys,
kLimitInfoKeys,
kLimitInfoDefaults,
kLimitInfoData
);
const kLimitInfos = {
core: kLimitInfoCore,
compatibility: kLimitInfoCompatibility,
} as const;
export const kLimitClasses = Object.fromEntries(
Object.entries(kLimitInfoCore).map(([k, { class: c }]) => [k, c])
);
export function getDefaultLimits(featureLevel: FeatureLevel) {
return kLimitInfos[featureLevel];
}
export function getDefaultLimitsForAdapter(adapter: GPUAdapter) {
// MAINTENANCE_TODO: Remove casts when GPUAdapter IDL has isCompatibilityMode.
return getDefaultLimits(
(adapter as unknown as { isCompatibilityMode: boolean }).isCompatibilityMode
? 'compatibility'
: 'core'
);
}
/** List of all entries of GPUSupportedLimits. */
export const kLimits = keysOf(kLimitInfoCore);
/**
* The number of color attachments to test.
* The CTS needs to generate a consistent list of tests.
* We can't use any default limits since they different from core to compat mode
* So, tests should use this value and filter out any values that are out of
* range for the current device.
*
* The test in maxColorAttachments.spec.ts tests that kMaxColorAttachmentsToTest
* is large enough to cover all devices tested.
*/
export const kMaxColorAttachmentsToTest = 32;
/** The size of indirect draw parameters in the indirectBuffer of drawIndirect */
export const kDrawIndirectParametersSize = 4;
/** The size of indirect drawIndexed parameters in the indirectBuffer of drawIndexedIndirect */
export const kDrawIndexedIndirectParametersSize = 5;
/** Per-GPUFeatureName info. */
export const kFeatureNameInfo: {
readonly [k in GPUFeatureName]: {};
} =
/* prettier-ignore */ {
'bgra8unorm-storage': {},
'depth-clip-control': {},
'depth32float-stencil8': {},
'texture-compression-bc': {},
'texture-compression-etc2': {},
'texture-compression-astc': {},
'timestamp-query': {},
'indirect-first-instance': {},
'shader-f16': {},
'rg11b10ufloat-renderable': {},
'float32-filterable': {},
};
/** List of all GPUFeatureName values. */
export const kFeatureNames = keysOf(kFeatureNameInfo);
/** List of all known WGSL language features */
export const kKnownWGSLLanguageFeatures = [
'readonly_and_readwrite_storage_textures',
'packed_4x8_integer_dot_product',
'unrestricted_pointer_parameters',
] as const;
export type WGSLLanguageFeature = (typeof kKnownWGSLLanguageFeatures)[number];