diff options
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/capability_info.ts')
-rw-r--r-- | dom/webgpu/tests/cts/checkout/src/webgpu/capability_info.ts | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/capability_info.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/capability_info.ts new file mode 100644 index 0000000000..4981328ce3 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/capability_info.ts @@ -0,0 +1,1123 @@ +// 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, numericKeysOf, valueof } from '../common/util/data_tables.js'; +import { assertTypeTrue, TypeEqual } from '../common/util/types.js'; +import { assert, unreachable } from '../common/util/util.js'; + +import { GPUConst, kMaxUnsignedLongValue, kMaxUnsignedLongLongValue } from './constants.js'; +import { ImageCopyType } from './util/texture/layout.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 query does not require any features. + '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]: {}; +} = /* prettier-ignore */ { + 'out-of-memory': {}, + 'validation': {}, + 'internal': {}, +}; +/** List of all GPUErrorFilter values. */ +export const kErrorScopeFilters = keysOf(kErrorScopeFilterInfo); +export const kGeneratableErrorScopeFilters = kErrorScopeFilters.filter(e => e !== 'internal'); + +// Textures + +// Definitions for use locally. To access the table entries, use `kTextureFormatInfo`. + +// Note that we repeat the header multiple times in order to make it easier to read. +const kRegularTextureFormatInfo = /* prettier-ignore */ makeTable( + ['renderable', 'multisample', 'resolve', 'color', 'depth', 'stencil', 'storage', 'copySrc', 'copyDst', 'sampleType', 'bytesPerBlock', 'blockWidth', 'blockHeight', 'renderTargetPixelByteCost', 'renderTargetComponentAlignment', 'feature', 'baseFormat'] as const, + [ , , , true, false, false, , true, true, , , 1, 1, undefined, undefined, , undefined] as const, { + // 8-bit formats + 'r8unorm': [ true, true, true, , , , false, , , 'float', 1, , , 1, 1], + 'r8snorm': [ false, false, false, , , , false, , , 'float', 1], + 'r8uint': [ true, true, false, , , , false, , , 'uint', 1, , , 1, 1], + 'r8sint': [ true, true, false, , , , false, , , 'sint', 1, , , 1, 1], + // 16-bit formats + 'r16uint': [ true, true, false, , , , false, , , 'uint', 2, , , 2, 2], + 'r16sint': [ true, true, false, , , , false, , , 'sint', 2, , , 2, 2], + 'r16float': [ true, true, true, , , , false, , , 'float', 2, , , 2, 2], + 'rg8unorm': [ true, true, true, , , , false, , , 'float', 2, , , 2, 1], + 'rg8snorm': [ false, false, false, , , , false, , , 'float', 2], + 'rg8uint': [ true, true, false, , , , false, , , 'uint', 2, , , 2, 1], + 'rg8sint': [ true, true, false, , , , false, , , 'sint', 2, , , 2, 1], + // 32-bit formats + 'r32uint': [ true, false, false, , , , true, , , 'uint', 4, , , 4, 4], + 'r32sint': [ true, false, false, , , , true, , , 'sint', 4, , , 4, 4], + 'r32float': [ true, true, false, , , , true, , , 'unfilterable-float', 4, , , 4, 4], + 'rg16uint': [ true, true, false, , , , false, , , 'uint', 4, , , 4, 2], + 'rg16sint': [ true, true, false, , , , false, , , 'sint', 4, , , 4, 2], + 'rg16float': [ true, true, true, , , , false, , , 'float', 4, , , 4, 2], + 'rgba8unorm': [ true, true, true, , , , true, , , 'float', 4, , , 8, 1, , 'rgba8unorm'], + 'rgba8unorm-srgb': [ true, true, true, , , , false, , , 'float', 4, , , 8, 1, , 'rgba8unorm'], + 'rgba8snorm': [ false, false, false, , , , true, , , 'float', 4], + 'rgba8uint': [ true, true, false, , , , true, , , 'uint', 4, , , 4, 1], + 'rgba8sint': [ true, true, false, , , , true, , , 'sint', 4, , , 4, 1], + 'bgra8unorm': [ true, true, true, , , , false, , , 'float', 4, , , 8, 1, , 'bgra8unorm'], + 'bgra8unorm-srgb': [ true, true, true, , , , false, , , 'float', 4, , , 8, 1, , 'bgra8unorm'], + // Packed 32-bit formats + 'rgb10a2unorm': [ true, true, true, , , , false, , , 'float', 4, , , 8, 4], + 'rg11b10ufloat': [ false, false, false, , , , false, , , 'float', 4, , , 8, 4], + 'rgb9e5ufloat': [ false, false, false, , , , false, , , 'float', 4], + // 64-bit formats + 'rg32uint': [ true, false, false, , , , true, , , 'uint', 8, , , 8, 4], + 'rg32sint': [ true, false, false, , , , true, , , 'sint', 8, , , 8, 4], + 'rg32float': [ true, false, false, , , , true, , , 'unfilterable-float', 8, , , 8, 4], + 'rgba16uint': [ true, true, false, , , , true, , , 'uint', 8, , , 8, 2], + 'rgba16sint': [ true, true, false, , , , true, , , 'sint', 8, , , 8, 2], + 'rgba16float': [ true, true, true, , , , true, , , 'float', 8, , , 8, 2], + // 128-bit formats + 'rgba32uint': [ true, false, false, , , , true, , , 'uint', 16, , , 16, 4], + 'rgba32sint': [ true, false, false, , , , true, , , 'sint', 16, , , 16, 4], + 'rgba32float': [ true, false, false, , , , true, , , 'unfilterable-float', 16, , , 16, 4], +} as const); +/* prettier-ignore */ +const kTexFmtInfoHeader = ['renderable', 'multisample', 'resolve', 'color', 'depth', 'stencil', 'storage', 'copySrc', 'copyDst', 'sampleType', 'bytesPerBlock', 'blockWidth', 'blockHeight', 'renderTargetPixelByteCost', 'renderTargetComponentAlignment', 'feature', 'baseFormat'] as const; +const kSizedDepthStencilFormatInfo = /* prettier-ignore */ makeTable(kTexFmtInfoHeader, + [ true, true, false, false, , , false, , , , , 1, 1, undefined, undefined, , undefined] as const, { + 'depth32float': [ , , , , true, false, , true, false, 'depth', 4], + 'depth16unorm': [ , , , , true, false, , true, true, 'depth', 2], + 'stencil8': [ , , , , false, true, , true, true, 'uint', 1], +} as const); + +// Multi aspect sample type are now set to their first aspect +const kUnsizedDepthStencilFormatInfo = /* prettier-ignore */ makeTable(kTexFmtInfoHeader, + [ true, true, false, false, , , false, false, false, , undefined, 1, 1, , , , undefined] as const, { + 'depth24plus': [ , , , , true, false, , , , 'depth'], + 'depth24plus-stencil8': [ , , , , true, true, , , , 'depth'], + // MAINTENANCE_TODO: These should really be sized formats; see below MAINTENANCE_TODO about multi-aspect formats. + 'depth32float-stencil8': [ , , , , true, true, , , , 'depth', , , , , , 'depth32float-stencil8'], +} as const); + +// Separated compressed formats by type +const kBCTextureFormatInfo = /* prettier-ignore */ makeTable(kTexFmtInfoHeader, + [ false, false, false, true, false, false, false, true, true, , , 4, 4, , , , undefined] as const, { + // Block Compression (BC) formats + 'bc1-rgba-unorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-bc', 'bc1-rgba-unorm'], + 'bc1-rgba-unorm-srgb': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-bc', 'bc1-rgba-unorm'], + 'bc2-rgba-unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc', 'bc2-rgba-unorm'], + 'bc2-rgba-unorm-srgb': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc', 'bc2-rgba-unorm'], + 'bc3-rgba-unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc', 'bc3-rgba-unorm'], + 'bc3-rgba-unorm-srgb': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc', 'bc3-rgba-unorm'], + 'bc4-r-unorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-bc'], + 'bc4-r-snorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-bc'], + 'bc5-rg-unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc'], + 'bc5-rg-snorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc'], + 'bc6h-rgb-ufloat': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc'], + 'bc6h-rgb-float': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc'], + 'bc7-rgba-unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc', 'bc7-rgba-unorm'], + 'bc7-rgba-unorm-srgb': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-bc', 'bc7-rgba-unorm'], +} as const); +const kETC2TextureFormatInfo = /* prettier-ignore */ makeTable(kTexFmtInfoHeader, + [ false, false, false, true, false, false, false, true, true, , , 4, 4, , , , undefined] as const, { + // Ericsson Compression (ETC2) formats + 'etc2-rgb8unorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-etc2', 'etc2-rgb8unorm'], + 'etc2-rgb8unorm-srgb': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-etc2', 'etc2-rgb8unorm'], + 'etc2-rgb8a1unorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-etc2', 'etc2-rgb8a1unorm'], + 'etc2-rgb8a1unorm-srgb': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-etc2', 'etc2-rgb8a1unorm'], + 'etc2-rgba8unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-etc2', 'etc2-rgba8unorm'], + 'etc2-rgba8unorm-srgb': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-etc2', 'etc2-rgba8unorm'], + 'eac-r11unorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-etc2'], + 'eac-r11snorm': [ , , , , , , , , , 'float', 8, 4, 4, , , 'texture-compression-etc2'], + 'eac-rg11unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-etc2'], + 'eac-rg11snorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-etc2'], +} as const); +const kASTCTextureFormatInfo = /* prettier-ignore */ makeTable(kTexFmtInfoHeader, + [ false, false, false, true, false, false, false, true, true, , , , , , , , undefined] as const, { + // Adaptable Scalable Compression (ASTC) formats + 'astc-4x4-unorm': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-astc', 'astc-4x4-unorm'], + 'astc-4x4-unorm-srgb': [ , , , , , , , , , 'float', 16, 4, 4, , , 'texture-compression-astc', 'astc-4x4-unorm'], + 'astc-5x4-unorm': [ , , , , , , , , , 'float', 16, 5, 4, , , 'texture-compression-astc', 'astc-5x4-unorm'], + 'astc-5x4-unorm-srgb': [ , , , , , , , , , 'float', 16, 5, 4, , , 'texture-compression-astc', 'astc-5x4-unorm'], + 'astc-5x5-unorm': [ , , , , , , , , , 'float', 16, 5, 5, , , 'texture-compression-astc', 'astc-5x5-unorm'], + 'astc-5x5-unorm-srgb': [ , , , , , , , , , 'float', 16, 5, 5, , , 'texture-compression-astc', 'astc-5x5-unorm'], + 'astc-6x5-unorm': [ , , , , , , , , , 'float', 16, 6, 5, , , 'texture-compression-astc', 'astc-6x5-unorm'], + 'astc-6x5-unorm-srgb': [ , , , , , , , , , 'float', 16, 6, 5, , , 'texture-compression-astc', 'astc-6x5-unorm'], + 'astc-6x6-unorm': [ , , , , , , , , , 'float', 16, 6, 6, , , 'texture-compression-astc', 'astc-6x6-unorm'], + 'astc-6x6-unorm-srgb': [ , , , , , , , , , 'float', 16, 6, 6, , , 'texture-compression-astc', 'astc-6x6-unorm'], + 'astc-8x5-unorm': [ , , , , , , , , , 'float', 16, 8, 5, , , 'texture-compression-astc', 'astc-8x5-unorm'], + 'astc-8x5-unorm-srgb': [ , , , , , , , , , 'float', 16, 8, 5, , , 'texture-compression-astc', 'astc-8x5-unorm'], + 'astc-8x6-unorm': [ , , , , , , , , , 'float', 16, 8, 6, , , 'texture-compression-astc', 'astc-8x6-unorm'], + 'astc-8x6-unorm-srgb': [ , , , , , , , , , 'float', 16, 8, 6, , , 'texture-compression-astc', 'astc-8x6-unorm'], + 'astc-8x8-unorm': [ , , , , , , , , , 'float', 16, 8, 8, , , 'texture-compression-astc', 'astc-8x8-unorm'], + 'astc-8x8-unorm-srgb': [ , , , , , , , , , 'float', 16, 8, 8, , , 'texture-compression-astc', 'astc-8x8-unorm'], + 'astc-10x5-unorm': [ , , , , , , , , , 'float', 16, 10, 5, , , 'texture-compression-astc', 'astc-10x5-unorm'], + 'astc-10x5-unorm-srgb': [ , , , , , , , , , 'float', 16, 10, 5, , , 'texture-compression-astc', 'astc-10x5-unorm'], + 'astc-10x6-unorm': [ , , , , , , , , , 'float', 16, 10, 6, , , 'texture-compression-astc', 'astc-10x6-unorm'], + 'astc-10x6-unorm-srgb': [ , , , , , , , , , 'float', 16, 10, 6, , , 'texture-compression-astc', 'astc-10x6-unorm'], + 'astc-10x8-unorm': [ , , , , , , , , , 'float', 16, 10, 8, , , 'texture-compression-astc', 'astc-10x8-unorm'], + 'astc-10x8-unorm-srgb': [ , , , , , , , , , 'float', 16, 10, 8, , , 'texture-compression-astc', 'astc-10x8-unorm'], + 'astc-10x10-unorm': [ , , , , , , , , , 'float', 16, 10, 10, , , 'texture-compression-astc', 'astc-10x10-unorm'], + 'astc-10x10-unorm-srgb': [ , , , , , , , , , 'float', 16, 10, 10, , , 'texture-compression-astc', 'astc-10x10-unorm'], + 'astc-12x10-unorm': [ , , , , , , , , , 'float', 16, 12, 10, , , 'texture-compression-astc', 'astc-12x10-unorm'], + 'astc-12x10-unorm-srgb': [ , , , , , , , , , 'float', 16, 12, 10, , , 'texture-compression-astc', 'astc-12x10-unorm'], + 'astc-12x12-unorm': [ , , , , , , , , , 'float', 16, 12, 12, , , 'texture-compression-astc', 'astc-12x12-unorm'], + 'astc-12x12-unorm-srgb': [ , , , , , , , , , 'float', 16, 12, 12, , , 'texture-compression-astc', 'astc-12x12-unorm'], +} as const); + +// Definitions for use locally. To access the table entries, use `kTextureFormatInfo`. + +// MAINTENANCE_TODO: Consider generating the exports below programmatically by filtering the big list, instead +// of using these local constants? Requires some type magic though. +/* prettier-ignore */ const kCompressedTextureFormatInfo = { ...kBCTextureFormatInfo, ...kETC2TextureFormatInfo, ...kASTCTextureFormatInfo } as const; +/* prettier-ignore */ const kColorTextureFormatInfo = { ...kRegularTextureFormatInfo, ...kCompressedTextureFormatInfo } as const; +/* prettier-ignore */ const kEncodableTextureFormatInfo = { ...kRegularTextureFormatInfo, ...kSizedDepthStencilFormatInfo } as const; +/* prettier-ignore */ const kSizedTextureFormatInfo = { ...kRegularTextureFormatInfo, ...kSizedDepthStencilFormatInfo, ...kCompressedTextureFormatInfo } as const; +/* prettier-ignore */ const kDepthStencilFormatInfo = { ...kSizedDepthStencilFormatInfo, ...kUnsizedDepthStencilFormatInfo } as const; +/* prettier-ignore */ const kUncompressedTextureFormatInfo = { ...kRegularTextureFormatInfo, ...kSizedDepthStencilFormatInfo, ...kUnsizedDepthStencilFormatInfo } as const; +/* prettier-ignore */ const kAllTextureFormatInfo = { ...kUncompressedTextureFormatInfo, ...kCompressedTextureFormatInfo } as const; + +/** A "regular" texture format (uncompressed, sized, single-plane color formats). */ +/* prettier-ignore */ export type RegularTextureFormat = keyof typeof kRegularTextureFormatInfo; +/** A sized depth/stencil texture format. */ +/* prettier-ignore */ export type SizedDepthStencilFormat = keyof typeof kSizedDepthStencilFormatInfo; +/** An unsized depth/stencil texture format. */ +/* prettier-ignore */ export type UnsizedDepthStencilFormat = keyof typeof kUnsizedDepthStencilFormatInfo; +/** A compressed (block) texture format. */ +/* prettier-ignore */ export type CompressedTextureFormat = keyof typeof kCompressedTextureFormatInfo; + +/** A color texture format (regular | compressed). */ +/* prettier-ignore */ export type ColorTextureFormat = keyof typeof kColorTextureFormatInfo; +/** An encodable texture format (regular | sized depth/stencil). */ +/* prettier-ignore */ export type EncodableTextureFormat = keyof typeof kEncodableTextureFormatInfo; +/** A sized texture format (regular | sized depth/stencil | compressed). */ +/* prettier-ignore */ export type SizedTextureFormat = keyof typeof kSizedTextureFormatInfo; +/** A depth/stencil format (sized | unsized). */ +/* prettier-ignore */ export type DepthStencilFormat = keyof typeof kDepthStencilFormatInfo; +/** An uncompressed (block size 1x1) format (regular | depth/stencil). */ +/* prettier-ignore */ export type UncompressedTextureFormat = keyof typeof kUncompressedTextureFormatInfo; + +/* prettier-ignore */ export const kRegularTextureFormats: readonly RegularTextureFormat[] = keysOf( kRegularTextureFormatInfo); +/* prettier-ignore */ export const kSizedDepthStencilFormats: readonly SizedDepthStencilFormat[] = keysOf( kSizedDepthStencilFormatInfo); +/* prettier-ignore */ export const kUnsizedDepthStencilFormats: readonly UnsizedDepthStencilFormat[] = keysOf(kUnsizedDepthStencilFormatInfo); +/* prettier-ignore */ export const kCompressedTextureFormats: readonly CompressedTextureFormat[] = keysOf( kCompressedTextureFormatInfo); + +/* prettier-ignore */ export const kColorTextureFormats: readonly ColorTextureFormat[] = keysOf( kColorTextureFormatInfo); +/* prettier-ignore */ export const kEncodableTextureFormats: readonly EncodableTextureFormat[] = keysOf( kEncodableTextureFormatInfo); +/* prettier-ignore */ export const kSizedTextureFormats: readonly SizedTextureFormat[] = keysOf( kSizedTextureFormatInfo); +/* prettier-ignore */ export const kDepthStencilFormats: readonly DepthStencilFormat[] = keysOf( kDepthStencilFormatInfo); +/* prettier-ignore */ export const kUncompressedTextureFormats: readonly UncompressedTextureFormat[] = keysOf(kUncompressedTextureFormatInfo); +/* prettier-ignore */ export const kAllTextureFormats: readonly GPUTextureFormat[] = keysOf( kAllTextureFormatInfo); + +// CompressedTextureFormat are unrenderable so filter from RegularTextureFormats for color targets is enough +export const kRenderableColorTextureFormats = kRegularTextureFormats.filter( + v => kColorTextureFormatInfo[v].renderable +); +assert( + kRenderableColorTextureFormats.every( + f => + kAllTextureFormatInfo[f].renderTargetComponentAlignment !== undefined && + kAllTextureFormatInfo[f].renderTargetPixelByteCost !== undefined + ) +); + +// 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); + +/** Per-GPUTextureFormat info. */ +// Exists just for documentation. Otherwise could be inferred by `makeTable`. +// MAINTENANCE_TODO: Refactor this to separate per-aspect data for multi-aspect formats. In particular: +// - bytesPerBlock only makes sense on a per-aspect basis. But this table can't express that. +// So we put depth32float-stencil8 to be an unsized format for now. +export type TextureFormatInfo = { + /** Whether the format can be used as `RENDER_ATTACHMENT`. */ + renderable: boolean; + /** Whether the format can be used in a multisample texture. */ + multisample: boolean; + /** Whether the texture with the format can be used as a resolve target. */ + resolve: boolean; + /** Whether the format has a color aspect. */ + color: boolean; + /** Whether the format has a depth aspect. */ + depth: boolean; + /** Whether the format has a stencil aspect. */ + stencil: boolean; + /** Whether the format can be used as `STORAGE`. */ + storage: boolean; + /** Whether the format can be used as `COPY_SRC`. */ + copySrc: boolean; + /** Whether the format can be used as `COPY_DST`. */ + copyDst: boolean; + /** Byte size of one texel block, or `undefined` if the format is unsized. */ + bytesPerBlock: number | undefined; + /** Width, in texels, of one texel block. */ + blockWidth: number; + /** Height, in texels, of one texel block. */ + blockHeight: number; + /** The raw, unaligned, byte cost towards the color attachment bytes per sample. + * (See https://www.w3.org/TR/webgpu/#abstract-opdef-calculating-color-attachment-bytes-per-sample). */ + renderTargetPixelByteCost: number | undefined; + /** The alignment used for the format when computing the color attachment bytes per sample. */ + renderTargetComponentAlignment: number | undefined; + /** Optional feature required to use this format, or `undefined` if none. */ + feature: GPUFeatureName | undefined; + // Add fields as needed +}; +/** Per-GPUTextureFormat info. */ +export const kTextureFormatInfo: { + readonly [k in GPUTextureFormat]: TextureFormatInfo & + // TextureFormatInfo exists just for documentation (and verification of the table data types). + // The next line constrains the types so that accessing kTextureFormatInfo with + // a subtype of GPUTextureFormat actually returns nicely a constrained info type + // (e.g. with `bytesPerBlock: number` instead of `bytesPerBlock: number | undefined`). + typeof kAllTextureFormatInfo[k]; +} = kAllTextureFormatInfo; +/** List of all GPUTextureFormat values. */ +/* prettier-ignore */ export const kTextureFormats: readonly GPUTextureFormat[] = keysOf(kAllTextureFormatInfo); + +/** Valid GPUTextureFormats for `copyExternalImageToTexture`, by spec. */ +export const kValidTextureFormatsForCopyE2T = [ + 'r8unorm', + 'r16float', + 'r32float', + 'rg8unorm', + 'rg16float', + 'rg32float', + 'rgba8unorm', + 'rgba8unorm-srgb', + 'bgra8unorm', + 'bgra8unorm-srgb', + 'rgb10a2unorm', + 'rgba16float', + 'rgba32float', +] as const; + +/** 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); + +/** 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); + +const kDepthStencilFormatCapabilityInBufferTextureCopy = { + // kUnsizedDepthStencilFormats + depth24plus: { + CopyB2T: [], + CopyT2B: [], + texelAspectSize: { 'depth-only': -1, 'stencil-only': -1 }, + }, + 'depth24plus-stencil8': { + CopyB2T: ['stencil-only'], + CopyT2B: ['stencil-only'], + texelAspectSize: { 'depth-only': -1, 'stencil-only': 1 }, + }, + + // kSizedDepthStencilFormats + depth16unorm: { + CopyB2T: ['all', 'depth-only'], + CopyT2B: ['all', 'depth-only'], + texelAspectSize: { 'depth-only': 2, 'stencil-only': -1 }, + }, + depth32float: { + CopyB2T: [], + CopyT2B: ['all', 'depth-only'], + texelAspectSize: { 'depth-only': 4, 'stencil-only': -1 }, + }, + 'depth32float-stencil8': { + CopyB2T: ['stencil-only'], + CopyT2B: ['depth-only', 'stencil-only'], + texelAspectSize: { 'depth-only': 4, 'stencil-only': 1 }, + }, + stencil8: { + CopyB2T: ['all', 'stencil-only'], + CopyT2B: ['all', 'stencil-only'], + texelAspectSize: { 'depth-only': -1, 'stencil-only': 1 }, + }, +} as const; + +/** `kDepthStencilFormatResolvedAspect[format][aspect]` returns the aspect-specific format for a + * depth-stencil format, or `undefined` if the format doesn't have the aspect. + */ +export const kDepthStencilFormatResolvedAspect: { + readonly [k in DepthStencilFormat]: { + readonly [a in GPUTextureAspect]: DepthStencilFormat | undefined; + }; +} = { + // kUnsizedDepthStencilFormats + depth24plus: { + all: 'depth24plus', + 'depth-only': 'depth24plus', + 'stencil-only': undefined, + }, + 'depth24plus-stencil8': { + all: 'depth24plus-stencil8', + 'depth-only': 'depth24plus', + 'stencil-only': 'stencil8', + }, + + // kSizedDepthStencilFormats + depth16unorm: { + all: 'depth16unorm', + 'depth-only': 'depth16unorm', + 'stencil-only': undefined, + }, + depth32float: { + all: 'depth32float', + 'depth-only': 'depth32float', + 'stencil-only': undefined, + }, + 'depth32float-stencil8': { + all: 'depth32float-stencil8', + 'depth-only': 'depth32float', + 'stencil-only': 'stencil8', + }, + stencil8: { + all: 'stencil8', + 'depth-only': undefined, + 'stencil-only': 'stencil8', + }, +} as const; + +/** + * @returns the GPUTextureFormat corresponding to the @param aspect of @param format. + * This allows choosing the correct format for depth-stencil aspects when creating pipelines that + * will have to match the resolved format of views, or to get per-aspect information like the + * `blockByteSize`. + * + * Many helpers use an `undefined` `aspect` to means `'all'` so this is also the default for this + * function. + */ +export function resolvePerAspectFormat( + format: GPUTextureFormat, + aspect?: GPUTextureAspect +): GPUTextureFormat { + if (aspect === 'all' || aspect === undefined) { + return format; + } + assert(kTextureFormatInfo[format].depth || kTextureFormatInfo[format].stencil); + const resolved = kDepthStencilFormatResolvedAspect[format as DepthStencilFormat][aspect ?? 'all']; + assert(resolved !== undefined); + return resolved; +} + +/** + * Gets all copyable aspects for copies between texture and buffer for specified depth/stencil format and copy type, by spec. + */ +export function depthStencilFormatCopyableAspects( + type: ImageCopyType, + format: DepthStencilFormat +): readonly GPUTextureAspect[] { + const appliedType = type === 'WriteTexture' ? 'CopyB2T' : type; + return kDepthStencilFormatCapabilityInBufferTextureCopy[format][appliedType]; +} + +/** + * Computes whether a copy between a depth/stencil texture aspect and a buffer is supported, by spec. + */ +export function depthStencilBufferTextureCopySupported( + type: ImageCopyType, + format: DepthStencilFormat, + aspect: GPUTextureAspect +): boolean { + const supportedAspects: readonly GPUTextureAspect[] = depthStencilFormatCopyableAspects( + type, + format + ); + return supportedAspects.includes(aspect); +} + +/** + * Returns the byte size of the depth or stencil aspect of the specified depth/stencil format, + * or -1 if none. + */ +export function depthStencilFormatAspectSize( + format: DepthStencilFormat, + aspect: 'depth-only' | 'stencil-only' +) { + const texelAspectSize = + kDepthStencilFormatCapabilityInBufferTextureCopy[format].texelAspectSize[aspect]; + assert(texelAspectSize > 0); + return texelAspectSize; +} + +/** + * Returns true iff a texture can be created with the provided GPUTextureDimension + * (defaulting to 2d) and GPUTextureFormat, by spec. + */ +export function textureDimensionAndFormatCompatible( + dimension: undefined | GPUTextureDimension, + format: GPUTextureFormat +): boolean { + const info = kAllTextureFormatInfo[format]; + return !( + (dimension === '1d' || dimension === '3d') && + (info.blockWidth > 1 || info.depth || info.stencil) + ); +} + +/** 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; + /** 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; + /** 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', 'wgslType'] as const, + [ , , , ] as const, { + // 8 bit components + 'uint8x2': [ 1, 'uint', 2, 'vec2<u32>'], + 'uint8x4': [ 1, 'uint', 4, 'vec4<u32>'], + 'sint8x2': [ 1, 'sint', 2, 'vec2<i32>'], + 'sint8x4': [ 1, 'sint', 4, 'vec4<i32>'], + 'unorm8x2': [ 1, 'unorm', 2, 'vec2<f32>'], + 'unorm8x4': [ 1, 'unorm', 4, 'vec4<f32>'], + 'snorm8x2': [ 1, 'snorm', 2, 'vec2<f32>'], + 'snorm8x4': [ 1, 'snorm', 4, 'vec4<f32>'], + // 16 bit components + 'uint16x2': [ 2, 'uint', 2, 'vec2<u32>'], + 'uint16x4': [ 2, 'uint', 4, 'vec4<u32>'], + 'sint16x2': [ 2, 'sint', 2, 'vec2<i32>'], + 'sint16x4': [ 2, 'sint', 4, 'vec4<i32>'], + 'unorm16x2': [ 2, 'unorm', 2, 'vec2<f32>'], + 'unorm16x4': [ 2, 'unorm', 4, 'vec4<f32>'], + 'snorm16x2': [ 2, 'snorm', 2, 'vec2<f32>'], + 'snorm16x4': [ 2, 'snorm', 4, 'vec4<f32>'], + 'float16x2': [ 2, 'float', 2, 'vec2<f32>'], + 'float16x4': [ 2, 'float', 4, 'vec4<f32>'], + // 32 bit components + 'float32': [ 4, 'float', 1, 'f32'], + 'float32x2': [ 4, 'float', 2, 'vec2<f32>'], + 'float32x3': [ 4, 'float', 3, 'vec3<f32>'], + 'float32x4': [ 4, 'float', 4, 'vec4<f32>'], + 'uint32': [ 4, 'uint', 1, 'u32'], + 'uint32x2': [ 4, 'uint', 2, 'vec2<u32>'], + 'uint32x3': [ 4, 'uint', 3, 'vec3<u32>'], + 'uint32x4': [ 4, 'uint', 4, 'vec4<u32>'], + 'sint32': [ 4, 'sint', 1, 'i32'], + 'sint32x2': [ 4, 'sint', 2, 'vec2<i32>'], + 'sint32x3': [ 4, 'sint', 3, 'vec3<i32>'], + 'sint32x4': [ 4, 'sint', 4, 'vec4<i32>'] +} 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' + | 'storageTex'; +/** + * 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' + | 'storageTex'; +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', + 'storageTex', + '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 max: number; + // Add fields as needed + }; +} = /* prettier-ignore */ { + 'uniformBuf': { class: 'uniformBuf', max: 12, }, + 'storageBuf': { class: 'storageBuf', max: 8, }, + 'sampler': { class: 'sampler', max: 16, }, + 'sampledTex': { class: 'sampledTex', max: 16, }, + 'storageTex': { class: 'storageTex', max: 4, }, +}; + +/** + * Default `PerPipelineLayout` binding limits, by spec. + */ +export const kPerPipelineBindingLimits: { + readonly [k in PerPipelineBindingLimitClass]: { + /** Which `PerPipelineLayout` binding limit class. */ + readonly class: k; + /** Maximum number of allowed bindings with `hasDynamicOffset: true` in that class. */ + readonly maxDynamic: number; + // Add fields as needed + }; +} = /* prettier-ignore */ { + 'uniformBuf': { class: 'uniformBuf', maxDynamic: 8, }, + 'storageBuf': { class: 'storageBuf', maxDynamic: 4, }, + 'sampler': { class: 'sampler', maxDynamic: 0, }, + 'sampledTex': { class: 'sampledTex', maxDynamic: 0, }, + 'storageTex': { class: 'storageTex', maxDynamic: 0, }, +}; + +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, }, + storageTex: { resource: 'storageTex', perStageLimitClass: kPerStageBindingLimits.storageTex, perPipelineLimitClass: kPerPipelineBindingLimits.storageTex, }, +}; + +// 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) { + return { + usage: GPUConst.TextureUsage.STORAGE_BINDING, + ...kBindingKind.storageTex, + ...kValidStagesStorageWrite, + }; +} +/** List of all GPUStorageTextureAccess values. */ +export const kStorageTextureAccessValues = ['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 } }] : []), + { texture: { multisampled: false } }, + { texture: { multisampled: true } }, + ] 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(format: GPUTextureFormat): readonly BGLEntry[] { + return [{ storageTexture: { access: 'write-only', format } }] as const; +} +/** Generate a list of possible texture-or-storageTexture-typed BGLEntry values. */ +export function sampledAndStorageBindingEntries( + includeUndefined: boolean, + storageTextureFormat: GPUTextureFormat = 'rgba8unorm' +): readonly BGLEntry[] { + return [ + ...textureBindingEntries(includeUndefined), + ...storageTextureBindingEntries(storageTextureFormat), + ] 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, + storageTextureFormat: GPUTextureFormat = 'rgba8unorm' +): readonly BGLEntry[] { + return [ + ...bufferBindingEntries(includeUndefined), + ...samplerBindingEntries(includeUndefined), + ...sampledAndStorageBindingEntries(includeUndefined, storageTextureFormat), + ] 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]; + +/** + * List of all possible texture sampleCount values. + * + * MAINTENANCE_TODO: Switch existing tests to use kTextureSampleCounts + */ +export const kTextureSampleCounts = [1, 4] as const; + +// 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 */ +export const kLimitInfo = /* prettier-ignore */ makeTable( + [ 'class', 'default', 'maximumValue'] as const, + [ 'maximum', , kMaxUnsignedLongValue] as const, { + 'maxTextureDimension1D': [ , 8192, ], + 'maxTextureDimension2D': [ , 8192, ], + 'maxTextureDimension3D': [ , 2048, ], + 'maxTextureArrayLayers': [ , 256, ], + + 'maxBindGroups': [ , 4, ], + 'maxDynamicUniformBuffersPerPipelineLayout': [ , 8, ], + 'maxDynamicStorageBuffersPerPipelineLayout': [ , 4, ], + 'maxSampledTexturesPerShaderStage': [ , 16, ], + 'maxSamplersPerShaderStage': [ , 16, ], + 'maxStorageBuffersPerShaderStage': [ , 8, ], + 'maxStorageTexturesPerShaderStage': [ , 4, ], + 'maxUniformBuffersPerShaderStage': [ , 12, ], + + 'maxUniformBufferBindingSize': [ , 65536, kMaxUnsignedLongLongValue], + 'maxStorageBufferBindingSize': [ , 134217728, kMaxUnsignedLongLongValue], + 'minUniformBufferOffsetAlignment': ['alignment', 256, ], + 'minStorageBufferOffsetAlignment': ['alignment', 256, ], + + 'maxVertexBuffers': [ , 8, ], + 'maxBufferSize': [ , 268435456, kMaxUnsignedLongLongValue], + 'maxVertexAttributes': [ , 16, ], + 'maxVertexBufferArrayStride': [ , 2048, ], + 'maxInterStageShaderComponents': [ , 60, ], + + 'maxColorAttachments': [ , 8, ], + 'maxColorAttachmentBytesPerSample': [ , 32, ], + + 'maxComputeWorkgroupStorageSize': [ , 16384, ], + 'maxComputeInvocationsPerWorkgroup': [ , 256, ], + 'maxComputeWorkgroupSizeX': [ , 256, ], + 'maxComputeWorkgroupSizeY': [ , 256, ], + 'maxComputeWorkgroupSizeZ': [ , 64, ], + 'maxComputeWorkgroupsPerDimension': [ , 65535, ], +} as const); + +/** List of all entries of GPUSupportedLimits. */ +export const kLimits = keysOf(kLimitInfo); + +// Pipeline limits + +/** Maximum number of color attachments to a render pass, by spec. */ +export const kMaxColorAttachments = kLimitInfo.maxColorAttachments.default; +/** `maxVertexBuffers` per GPURenderPipeline, by spec. */ +export const kMaxVertexBuffers = kLimitInfo.maxVertexBuffers.default; +/** `maxVertexAttributes` per GPURenderPipeline, by spec. */ +export const kMaxVertexAttributes = kLimitInfo.maxVertexAttributes.default; +/** `maxVertexBufferArrayStride` in a vertex buffer in a GPURenderPipeline, by spec. */ +export const kMaxVertexBufferArrayStride = kLimitInfo.maxVertexBufferArrayStride.default; + +/** 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 */ { + 'depth-clip-control': {}, + 'depth32float-stencil8': {}, + 'texture-compression-bc': {}, + 'texture-compression-etc2': {}, + 'texture-compression-astc': {}, + 'timestamp-query': {}, + 'indirect-first-instance': {}, + 'shader-f16': {}, + 'rg11b10ufloat-renderable': {}, +}; +/** List of all GPUFeatureName values. */ +export const kFeatureNames = keysOf(kFeatureNameInfo); + +/** + * Check if two formats are view format compatible. + * + * This function may need to be generalized to use `baseFormat` from `kTextureFormatInfo`. + */ +export function viewCompatible(a: GPUTextureFormat, b: GPUTextureFormat): boolean { + return a === b || a + '-srgb' === b || b + '-srgb' === a; +} + +export function getFeaturesForFormats<T>( + formats: readonly (T & (GPUTextureFormat | undefined))[] +): readonly (GPUFeatureName | undefined)[] { + return Array.from(new Set(formats.map(f => (f ? kTextureFormatInfo[f].feature : undefined)))); +} + +export function filterFormatsByFeature<T>( + feature: GPUFeatureName | undefined, + formats: readonly (T & (GPUTextureFormat | undefined))[] +): readonly (T & (GPUTextureFormat | undefined))[] { + return formats.filter(f => f === undefined || kTextureFormatInfo[f].feature === feature); +} + +export const kFeaturesForFormats = getFeaturesForFormats(kTextureFormats); |