blob: a3d8f314899784a93eb6bb875256ac2e0a3fa905 [file] [log] [blame]
import { isCompatibilityDevice } from '../common/framework/test_config.js';
import { keysOf } from '../common/util/data_tables.js';
import { assert, unreachable } from '../common/util/util.js';
import { align, roundDown } from './util/math.js';
import { getTextureDimensionFromView } from './util/texture/base.js';
import { ImageCopyType } from './util/texture/layout.js';
//
// Texture format tables
//
/**
* Defaults applied to all texture format tables automatically. Used only inside
* `formatTableWithDefaults`. This ensures keys are never missing, always explicitly `undefined`.
*
* All top-level keys must be defined here, or they won't be exposed at all.
* Documentation is also written here; this makes it propagate through to the end types.
*/
const kFormatUniversalDefaults = {
/** Texel block width. */
blockWidth: undefined,
/** Texel block height. */
blockHeight: undefined,
color: undefined,
depth: undefined,
stencil: undefined,
/**
* Info when this format can be used as a color render target. The format may require a feature
* to actually be used as a render target. Eg: rg11b10ufloat which requires rg11b10ufloat-renderable
* Call {@link isTextureFormatPossiblyUsableAsColorRenderAttachment} before having a device
* Call {@link isTextureFormatColorRenderable}(device, format) to find out for a particular device.
* Use {@link kPossibleColorRenderableTextureFormats} for params.
*/
colorRender: undefined,
/**
* Whether the format can possibly be used as a multisample texture. The format may require a
* feature to actually multisampled. Eg: rg11b10ufloat which requires rg11b10ufloat-renderable
* Call {@link isTextureFormatPossiblyMultisampled} before having a device
* Call {@link isTextureFormatMultisampled}(device, format) to find out for a particular device.
* Use {@link kPossibleMultisampledTextureFormats} for params.
*/
multisample: undefined,
/** Optional feature required to use this format, or `undefined` if none. */
feature: undefined,
/** The base format for srgb formats. Specified on both srgb and equivalent non-srgb formats. */
baseFormat: undefined,
/** @deprecated Use `.color.bytes`, `.depth.bytes`, or `.stencil.bytes`. */
bytesPerBlock: undefined,
// IMPORTANT:
// Add new top-level keys both here and in TextureFormatInfo_TypeCheck.
} as const;
/**
* Takes `table` and applies `defaults` to every row, i.e. for each row,
* `{ ... kUniversalDefaults, ...defaults, ...row }`.
* This only operates at the first level; it doesn't support defaults in nested objects.
*/
function formatTableWithDefaults<Defaults extends {}, Table extends { readonly [K: string]: {} }>({
defaults,
table,
}: {
defaults: Defaults;
table: Table;
}): {
readonly [F in keyof Table]: {
readonly [K in keyof typeof kFormatUniversalDefaults]: K extends keyof Table[F]
? Table[F][K]
: K extends keyof Defaults
? Defaults[K]
: (typeof kFormatUniversalDefaults)[K];
};
} {
return Object.fromEntries(
Object.entries(table).map(([k, row]) => [
k,
{ ...kFormatUniversalDefaults, ...defaults, ...row },
])
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
) as any;
}
/** "plain color formats", plus rgb9e5ufloat. */
const kRegularTextureFormatInfo = formatTableWithDefaults({
defaults: { blockWidth: 1, blockHeight: 1 },
table: {
// plain, 8 bits per component
r8unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
colorRender: { blend: true, resolve: true, byteCost: 1, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r8snorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r8uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
colorRender: { blend: false, resolve: false, byteCost: 1, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r8sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
colorRender: { blend: false, resolve: false, byteCost: 1, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg8unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: true, resolve: true, byteCost: 2, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg8snorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg8uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: false, resolve: false, byteCost: 2, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg8sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: false, resolve: false, byteCost: 2, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba8unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 1 },
multisample: true,
baseFormat: 'rgba8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'rgba8unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 1 },
multisample: true,
baseFormat: 'rgba8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba8snorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 4,
},
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba8uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba8sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 1 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
bgra8unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 1 },
multisample: true,
baseFormat: 'bgra8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bgra8unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 1 },
multisample: true,
baseFormat: 'bgra8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
// plain, 16 bits per component
r16unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: true, resolve: false, byteCost: 2, alignment: 2 },
multisample: true,
feature: 'texture-formats-tier1',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r16snorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: true, resolve: false, byteCost: 2, alignment: 2 },
multisample: true,
feature: 'texture-formats-tier1',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r16uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: false, resolve: false, byteCost: 2, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r16sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: false, resolve: false, byteCost: 2, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r16float: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
colorRender: { blend: true, resolve: true, byteCost: 2, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg16unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: false, byteCost: 4, alignment: 2 },
multisample: true,
feature: 'texture-formats-tier1',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg16snorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: false, byteCost: 4, alignment: 2 },
multisample: true,
feature: 'texture-formats-tier1',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg16uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg16sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg16float: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 4, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba16unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: true, resolve: false, byteCost: 8, alignment: 4 },
multisample: true,
feature: 'texture-formats-tier1',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba16snorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: true, resolve: false, byteCost: 8, alignment: 2 },
multisample: true,
feature: 'texture-formats-tier1',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba16uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: false, resolve: false, byteCost: 8, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba16sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: false, resolve: false, byteCost: 8, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba16float: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 2 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
// plain, 32 bits per component
r32uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: true,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r32sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: true,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
r32float: {
color: {
type: 'unfilterable-float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: true,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 4, alignment: 4 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg32uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: false, resolve: false, byteCost: 8, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg32sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: false, resolve: false, byteCost: 8, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg32float: {
color: {
type: 'unfilterable-float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 8,
},
colorRender: { blend: false, resolve: false, byteCost: 8, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba32uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 16,
},
colorRender: { blend: false, resolve: false, byteCost: 16, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba32sint: {
color: {
type: 'sint',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 16,
},
colorRender: { blend: false, resolve: false, byteCost: 16, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgba32float: {
color: {
type: 'unfilterable-float',
copySrc: true,
copyDst: true,
storage: true,
readWriteStorage: false,
bytes: 16,
},
colorRender: { blend: false, resolve: false, byteCost: 16, alignment: 4 },
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
// plain, mixed component width, 32 bits per texel
rgb10a2uint: {
color: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: false, resolve: false, byteCost: 8, alignment: 4 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rgb10a2unorm: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 4 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
rg11b10ufloat: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
colorRender: { blend: true, resolve: true, byteCost: 8, alignment: 4 },
multisample: true,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
// packed
rgb9e5ufloat: {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 4,
},
multisample: false,
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
},
} as const);
// MAINTENANCE_TODO: Distinguishing "sized" and "unsized" depth stencil formats doesn't make sense
// because one aspect can be sized and one can be unsized. This should be cleaned up, but is kept
// this way during a migration phase.
const kSizedDepthStencilFormatInfo = formatTableWithDefaults({
defaults: { blockWidth: 1, blockHeight: 1, multisample: true },
table: {
stencil8: {
stencil: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
bytesPerBlock: 1,
},
depth16unorm: {
depth: {
type: 'depth',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 2,
},
bytesPerBlock: 2,
},
depth32float: {
depth: {
type: 'depth',
copySrc: true,
copyDst: false,
storage: false,
readWriteStorage: false,
bytes: 4,
},
bytesPerBlock: 4,
},
},
} as const);
const kUnsizedDepthStencilFormatInfo = formatTableWithDefaults({
defaults: { blockWidth: 1, blockHeight: 1, multisample: true },
table: {
depth24plus: {
depth: {
type: 'depth',
copySrc: false,
copyDst: false,
storage: false,
readWriteStorage: false,
bytes: undefined,
},
},
'depth24plus-stencil8': {
depth: {
type: 'depth',
copySrc: false,
copyDst: false,
storage: false,
readWriteStorage: false,
bytes: undefined,
},
stencil: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
},
'depth32float-stencil8': {
depth: {
type: 'depth',
copySrc: true,
copyDst: false,
storage: false,
readWriteStorage: false,
bytes: 4,
},
stencil: {
type: 'uint',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 1,
},
feature: 'depth32float-stencil8',
},
},
} as const);
const kBCTextureFormatInfo = formatTableWithDefaults({
defaults: {
blockWidth: 4,
blockHeight: 4,
multisample: false,
feature: 'texture-compression-bc',
},
table: {
'bc1-rgba-unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
baseFormat: 'bc1-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc1-rgba-unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
baseFormat: 'bc1-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc2-rgba-unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'bc2-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc2-rgba-unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'bc2-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc3-rgba-unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'bc3-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc3-rgba-unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'bc3-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc4-r-unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc4-r-snorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc5-rg-unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc5-rg-snorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc6h-rgb-ufloat': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc6h-rgb-float': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc7-rgba-unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'bc7-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'bc7-rgba-unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'bc7-rgba-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
},
} as const);
const kETC2TextureFormatInfo = formatTableWithDefaults({
defaults: {
blockWidth: 4,
blockHeight: 4,
multisample: false,
feature: 'texture-compression-etc2',
},
table: {
'etc2-rgb8unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
baseFormat: 'etc2-rgb8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'etc2-rgb8unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
baseFormat: 'etc2-rgb8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'etc2-rgb8a1unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
baseFormat: 'etc2-rgb8a1unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'etc2-rgb8a1unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
baseFormat: 'etc2-rgb8a1unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'etc2-rgba8unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'etc2-rgba8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'etc2-rgba8unorm-srgb': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'etc2-rgba8unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'eac-r11unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'eac-r11snorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 8,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'eac-rg11unorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'eac-rg11snorm': {
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
},
} as const);
const kASTCTextureFormatInfo = formatTableWithDefaults({
defaults: {
multisample: false,
feature: 'texture-compression-astc',
},
table: {
'astc-4x4-unorm': {
blockWidth: 4,
blockHeight: 4,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-4x4-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-4x4-unorm-srgb': {
blockWidth: 4,
blockHeight: 4,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-4x4-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-5x4-unorm': {
blockWidth: 5,
blockHeight: 4,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-5x4-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-5x4-unorm-srgb': {
blockWidth: 5,
blockHeight: 4,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-5x4-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-5x5-unorm': {
blockWidth: 5,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-5x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-5x5-unorm-srgb': {
blockWidth: 5,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-5x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-6x5-unorm': {
blockWidth: 6,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-6x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-6x5-unorm-srgb': {
blockWidth: 6,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-6x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-6x6-unorm': {
blockWidth: 6,
blockHeight: 6,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-6x6-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-6x6-unorm-srgb': {
blockWidth: 6,
blockHeight: 6,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-6x6-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-8x5-unorm': {
blockWidth: 8,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-8x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-8x5-unorm-srgb': {
blockWidth: 8,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-8x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-8x6-unorm': {
blockWidth: 8,
blockHeight: 6,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-8x6-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-8x6-unorm-srgb': {
blockWidth: 8,
blockHeight: 6,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-8x6-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-8x8-unorm': {
blockWidth: 8,
blockHeight: 8,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-8x8-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-8x8-unorm-srgb': {
blockWidth: 8,
blockHeight: 8,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-8x8-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x5-unorm': {
blockWidth: 10,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x5-unorm-srgb': {
blockWidth: 10,
blockHeight: 5,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x5-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x6-unorm': {
blockWidth: 10,
blockHeight: 6,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x6-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x6-unorm-srgb': {
blockWidth: 10,
blockHeight: 6,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x6-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x8-unorm': {
blockWidth: 10,
blockHeight: 8,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x8-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x8-unorm-srgb': {
blockWidth: 10,
blockHeight: 8,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x8-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x10-unorm': {
blockWidth: 10,
blockHeight: 10,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x10-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-10x10-unorm-srgb': {
blockWidth: 10,
blockHeight: 10,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-10x10-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-12x10-unorm': {
blockWidth: 12,
blockHeight: 10,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-12x10-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-12x10-unorm-srgb': {
blockWidth: 12,
blockHeight: 10,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-12x10-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-12x12-unorm': {
blockWidth: 12,
blockHeight: 12,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-12x12-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
'astc-12x12-unorm-srgb': {
blockWidth: 12,
blockHeight: 12,
color: {
type: 'float',
copySrc: true,
copyDst: true,
storage: false,
readWriteStorage: false,
bytes: 16,
},
baseFormat: 'astc-12x12-unorm',
/*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
},
},
} as const);
// Definitions for use locally.
// 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 kBCCompressedTextureFormats: readonly CompressedTextureFormat[] = keysOf( kBCTextureFormatInfo);
/* prettier-ignore */ export const kASTCCompressedTextureFormats: readonly CompressedTextureFormat[] = keysOf( kASTCTextureFormatInfo);
/* 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);
/** Per-GPUTextureFormat-per-aspect info. */
interface TextureFormatAspectInfo {
/** Whether the aspect can be used as `COPY_SRC`. */
copySrc: boolean;
/** Whether the aspect can be used as `COPY_DST`. */
copyDst: boolean;
/** Whether the aspect can be used as `STORAGE`. */
storage: boolean;
/** Whether the aspect can be used as `STORAGE` with `read-write` storage texture access. */
readWriteStorage: boolean;
/** The "texel block copy footprint" of one texel block; `undefined` if the aspect is unsized. */
bytes: number | undefined;
}
/** Per GPUTextureFormat-per-aspect info for color aspects. */
interface TextureFormatColorAspectInfo extends TextureFormatAspectInfo {
bytes: number;
/** "Best" sample type of the format. "float" also implies "unfilterable-float". */
type: 'float' | 'uint' | 'sint' | 'unfilterable-float';
}
/** Per GPUTextureFormat-per-aspect info for depth aspects. */
interface TextureFormatDepthAspectInfo extends TextureFormatAspectInfo {
/** "depth" also implies "unfilterable-float". */
type: 'depth';
}
/** Per GPUTextureFormat-per-aspect info for stencil aspects. */
interface TextureFormatStencilAspectInfo extends TextureFormatAspectInfo {
bytes: 1;
type: 'uint';
}
/**
* Per-GPUTextureFormat info.
* This is not actually the type of values in kTextureFormatInfo; that type is fully const
* so that it can be narrowed very precisely at usage sites by the compiler.
* This type exists only as a type check on the inferred type of kTextureFormatInfo.
*/
type TextureFormatInfo_TypeCheck = {
blockWidth: number;
blockHeight: number;
multisample: boolean;
baseFormat: GPUTextureFormat | undefined;
feature: GPUFeatureName | undefined;
bytesPerBlock: number | undefined;
// IMPORTANT:
// Add new top-level keys both here and in kUniversalDefaults.
} & (
| {
/** Color aspect info. */
color: TextureFormatColorAspectInfo;
/** Defined if the format is a color format that can be used as `RENDER_ATTACHMENT`. */
colorRender:
| undefined
| {
/** Whether the format is blendable. */
blend: boolean;
/** Whether the format can be a multisample resolve target. */
resolve: boolean;
/** The "render target pixel byte cost" of the format. */
byteCost: number;
/** The "render target component alignment" of the format. */
alignment: number;
};
}
| (
| {
/** Depth aspect info. */
depth: TextureFormatDepthAspectInfo;
/** Stencil aspect info. */
stencil: undefined | TextureFormatStencilAspectInfo;
multisample: true;
}
| {
/** Stencil aspect info. */
stencil: TextureFormatStencilAspectInfo;
multisample: true;
}
)
);
/**
* DO NOT EXPORT THIS - functions that need info from this table should use the appropriate
* method for their needs.
*
* For a list of textures formats for test parameters there are:
*
* Lists of formats that might require features to be enabled
* * kPossibleColorRenderableTextureFormats
* * kPossibleStorageTextureFormats
* * kPossibleReadWriteStorageTextureFormats
* * kPossibleMultisampledTextureFormats
*
* Lists of formats that end in -srgb
* * kDifferentBaseFormatTextureFormats (includes compressed textures)
* * kDifferentBaseFormatRegularTextureFormats (does not include compressed textures)
*
* Formats that require a feature to use at all (mostly compressed formats)
* * kOptionalTextureFormats
*
* Misc
* * kRegularTextureFormats
* * kSizedDepthStencilFormats
* * kUnsizedDepthStencilFormats
* * kCompressedTextureFormats
* * kUncompressedTextureFormats
* * kColorTextureFormats - color formats including compressed and sint/uint
* * kEncodableTextureFormats - formats that TexelView supports.
* * kSizedTextureFormats - formats that have a known size (so not depth24plus ...)
* * kDepthStencilFormats - depth, stencil, depth-stencil
* * kDepthTextureFormats - depth and depth-stencil
* * kStencilTextureFormats - stencil and depth-stencil
* * kAllTextureFormats
*
* If one of the list above does not work, add a new one or to filter in beforeAllSubcases you generally want to use
* You will not know if you can actually use a texture for the given use case until the test runs and has a device.
*
* * isTextureFormatPossiblyUsableAsRenderAttachment
* * isTextureFormatPossiblyUsableAsColorRenderAttachment
* * isTextureFormatPossiblyMultisampled
* * isTextureFormatPossiblyStorageReadable
* * isTextureFormatPossiblyStorageReadWritable
* * isTextureFormatPossiblyFilterableAsTextureF32
* * isTextureFormatPossiblyUsableWithCopyExternalImageToTexture
*
* These are also usable before or during a test
*
* * isColorTextureFormat
* * isDepthTextureFormat
* * isStencilTextureFormat
* * isDepthOrStencilTextureFormat
* * isEncodableTextureFormat
* * isRegularTextureFormat
* * isCompressedFloatTextureFormat
* * isSintOrUintFormat
*
* To skip a test use the `skipIfXXX` tests in `GPUTest` if possible. Otherwise these functions
* require a device to give a correct answer.
*
* * isTextureFormatUsableAsRenderAttachment
* * isTextureFormatColorRenderable
* * isTextureFormatResolvable
* * isTextureFormatBlendable
* * isTextureFormatMultisampled
* * isTextureFormatUsableAsStorageTexture
* * isTextureFormatUsableAsReadWriteStorageTexture
* * isTextureFormatUsableAsStorageFormatInCreateShaderModule
* * isTextureFormatUsableWithCopyExternalImageToTexture
*
* Per-GPUTextureFormat info.
*/
const kTextureFormatInfo = {
...kRegularTextureFormatInfo,
...kSizedDepthStencilFormatInfo,
...kUnsizedDepthStencilFormatInfo,
...kBCTextureFormatInfo,
...kETC2TextureFormatInfo,
...kASTCTextureFormatInfo,
} as const;
/** Defining this variable verifies the type of kTextureFormatInfo2. It is not used. */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const kTextureFormatInfo_TypeCheck: {
readonly [F in GPUTextureFormat]: TextureFormatInfo_TypeCheck;
} = kTextureFormatInfo;
// Depth texture formats including formats that also support stencil
export const kDepthTextureFormats = [
...kDepthStencilFormats.filter(v => kTextureFormatInfo[v].depth),
] as const;
// Stencil texture formats including formats that also support depth
export const kStencilTextureFormats = kDepthStencilFormats.filter(
v => kTextureFormatInfo[v].stencil
);
export const kTextureFormatTier1AllowsResolve: readonly ColorTextureFormat[] = [
'r8snorm',
'rg8snorm',
'rgba8snorm',
'rg11b10ufloat',
] as const;
export const kTextureFormatTier1ThrowsWhenNotEnabled: readonly ColorTextureFormat[] = [
'r16unorm',
'r16snorm',
'rg16unorm',
'rg16snorm',
'rgba16unorm',
'rgba16snorm',
] as const;
export const kTextureFormatTier1AllowsRenderAttachmentBlendableMultisample: readonly ColorTextureFormat[] =
[
'r16unorm',
'r16snorm',
'rg16unorm',
'rg16snorm',
'rgba16unorm',
'rgba16snorm',
'r8snorm',
'rg8snorm',
'rgba8snorm',
'rg11b10ufloat',
] as const;
export const kTextureFormatsTier1EnablesStorageReadOnlyWriteOnly: readonly ColorTextureFormat[] = [
'r8unorm',
'r8snorm',
'r8uint',
'r8sint',
'rg8unorm',
'rg8snorm',
'rg8uint',
'rg8sint',
'r16uint',
'r16sint',
'r16float',
'rg16uint',
'rg16sint',
'rg16float',
'rgb10a2uint',
'rgb10a2unorm',
'rg11b10ufloat',
] as const;
export const kTextureFormatsTier2EnablesStorageReadWrite: readonly ColorTextureFormat[] = [
'r8unorm',
'r8uint',
'r8sint',
'rgba8unorm',
'rgba8uint',
'rgba8sint',
'r16uint',
'r16sint',
'r16float',
'rgba16uint',
'rgba16sint',
'rgba16float',
'rgba32uint',
'rgba32sint',
'rgba32float',
] as const;
// Texture formats that may possibly be used as a storage texture.
// Some may require certain features to be enabled.
export const kPossibleStorageTextureFormats = [
...kRegularTextureFormats.filter(f => kTextureFormatInfo[f].color?.storage),
'bgra8unorm',
// these can be used as storage when texture-formats-tier1 is enabled
...kTextureFormatsTier1EnablesStorageReadOnlyWriteOnly,
] as readonly RegularTextureFormat[];
// Texture formats that may possibly be used as a storage texture.
// Some may require certain features to be enabled.
export const kPossibleReadWriteStorageTextureFormats = [
...kPossibleStorageTextureFormats.filter(f => kTextureFormatInfo[f].color?.readWriteStorage),
// these can be used as storage when texture-formats-tier2 is enabled
...kTextureFormatsTier2EnablesStorageReadWrite,
] as readonly RegularTextureFormat[];
// Texture formats that may possibly be multisampled.
// Some may require certain features to be enabled.
export const kPossibleMultisampledTextureFormats = [
...kRegularTextureFormats.filter(f => kTextureFormatInfo[f].multisample),
...kDepthStencilFormats.filter(f => kTextureFormatInfo[f].multisample),
] as const;
// Texture formats that may possibly be color renderable.
// Some may require certain features to be enabled.
export const kPossibleColorRenderableTextureFormats = [
...kRegularTextureFormats.filter(f => kTextureFormatInfo[f].colorRender),
] as const;
export type PossibleColorRenderTextureFormat =
(typeof kPossibleColorRenderableTextureFormats)[number];
// Texture formats that have a different base format. This is effectively all -srgb formats
// including compressed formats.
export const kDifferentBaseFormatTextureFormats = kColorTextureFormats.filter(
f => kTextureFormatInfo[f].baseFormat && kTextureFormatInfo[f].baseFormat !== f
);
// "Regular" texture formats that have a different base format. This is effectively all -srgb formats
// except compressed formats.
export const kDifferentBaseFormatRegularTextureFormats = kRegularTextureFormats.filter(
f => kTextureFormatInfo[f].baseFormat && kTextureFormatInfo[f].baseFormat !== f
);
// Textures formats that are optional
export const kOptionalTextureFormats = kAllTextureFormats.filter(
t => kTextureFormatInfo[t].feature !== undefined
);
function isSnormTextureFormat(format: GPUTextureFormat): boolean {
return format.endsWith('snorm');
}
/**
* Returns true if a texture can be possibly used with copyExternalImageToTexture.
* The texture may require certain features to be enabled.
*/
export function isTextureFormatPossiblyUsableWithCopyExternalImageToTexture(
format: GPUTextureFormat
): boolean {
return (
isColorTextureFormat(format) &&
!isSintOrUintFormat(format) &&
!isCompressedTextureFormat(format) &&
!isSnormTextureFormat(format) &&
isTextureFormatPossiblyUsableAsColorRenderAttachment(format)
);
}
/**
* Returns true if a texture can be used with copyExternalImageToTexture.
*/
export function isTextureFormatUsableWithCopyExternalImageToTexture(
device: GPUDevice,
format: GPUTextureFormat
): boolean {
return (
isColorTextureFormat(format) &&
!isSintOrUintFormat(format) &&
!isCompressedTextureFormat(format) &&
!isSnormTextureFormat(format) &&
isTextureFormatColorRenderable(device, format)
);
}
//
// Other related stuff
//
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;
}
/**
* @returns the sample type of the specified aspect of the specified format.
*/
export function sampleTypeForFormatAndAspect(
format: GPUTextureFormat,
aspect: GPUTextureAspect
): 'uint' | 'depth' | 'float' | 'sint' | 'unfilterable-float' {
const info = kTextureFormatInfo[format];
if (info.color) {
assert(aspect === 'all', `color format ${format} used with aspect ${aspect}`);
return info.color.type;
} else if (info.depth && info.stencil) {
if (aspect === 'depth-only') {
return info.depth.type;
} else if (aspect === 'stencil-only') {
return info.stencil.type;
} else {
unreachable(`depth-stencil format ${format} used with aspect ${aspect}`);
}
} else if (info.depth) {
assert(aspect !== 'stencil-only', `depth-only format ${format} used with aspect ${aspect}`);
return info.depth.type;
} else if (info.stencil) {
assert(aspect !== 'depth-only', `stencil-only format ${format} used with aspect ${aspect}`);
return info.stencil.type;
}
unreachable();
}
/**
* 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 textureFormatAndDimensionPossiblyCompatible(
dimension: undefined | GPUTextureDimension,
format: GPUTextureFormat
): boolean {
if (dimension === '3d' && (isBCTextureFormat(format) || isASTCTextureFormat(format))) {
return true;
}
const info = kAllTextureFormatInfo[format];
return !(
(dimension === '1d' || dimension === '3d') &&
(info.blockWidth > 1 || info.depth || info.stencil)
);
}
/**
* Returns true iff a texture can be created with the provided GPUTextureDimension
* (defaulting to 2d) and GPUTextureFormat for a GPU device, by spec.
*/
export function textureDimensionAndFormatCompatibleForDevice(
device: GPUDevice,
dimension: undefined | GPUTextureDimension,
format: GPUTextureFormat
): boolean {
if (
dimension === '3d' &&
((isBCTextureFormat(format) && device.features.has('texture-compression-bc-sliced-3d')) ||
(isASTCTextureFormat(format) && device.features.has('texture-compression-astc-sliced-3d')))
) {
return true;
}
const info = kAllTextureFormatInfo[format];
return !(
(dimension === '1d' || dimension === '3d') &&
(info.blockWidth > 1 || info.depth || info.stencil)
);
}
/**
* Returns true iff a texture can be used with the provided GPUTextureViewDimension
*/
export function textureViewDimensionAndFormatCompatibleForDevice(
device: GPUDevice,
dimension: GPUTextureViewDimension,
format: GPUTextureFormat
): boolean {
return textureDimensionAndFormatCompatibleForDevice(
device,
getTextureDimensionFromView(dimension),
format
);
}
/**
* Check if two formats are view format compatible.
*/
export function textureFormatsAreViewCompatible(
device: GPUDevice,
a: GPUTextureFormat,
b: GPUTextureFormat
) {
return isCompatibilityDevice(device)
? a === b
: a === b || a + '-srgb' === b || b + '-srgb' === a;
}
/**
* Gets the block width, height, and bytes per block for a color texture format.
* This is for color textures only. For all texture formats @see {@link getBlockInfoForTextureFormat}
* The point of this function is bytesPerBlock is always defined so no need to check that it's not
* vs getBlockInfoForTextureFormat where it may not be defined.
*/
export function getBlockInfoForColorTextureFormat(format: ColorTextureFormat) {
const info = kTextureFormatInfo[format];
return {
blockWidth: info.blockWidth,
blockHeight: info.blockHeight,
bytesPerBlock: info.color?.bytes,
};
}
/**
* Gets the block width, height, and bytes per block for a sized texture format.
* This is for sized textures only. For all texture formats @see {@link getBlockInfoForTextureFormat}
* The point of this function is bytesPerBlock is always defined so no need to check that it's not
* vs getBlockInfoForTextureFormat where it may not be defined.
*/
export function getBlockInfoForSizedTextureFormat(format: SizedTextureFormat) {
const info = kTextureFormatInfo[format];
const bytesPerBlock = info.color?.bytes || info.depth?.bytes || info.stencil?.bytes;
assert(!!bytesPerBlock);
return {
blockWidth: info.blockWidth,
blockHeight: info.blockHeight,
bytesPerBlock,
};
}
/**
* Gets the block width, height, and bytes per block for an encodable texture format.
* This is for encodable textures only. For all texture formats @see {@link getBlockInfoForTextureFormat}
* The point of this function is bytesPerBlock is always defined so no need to check that it's not
* vs getBlockInfoForTextureFormat where it may not be defined.
*/
export function getBlockInfoForEncodableTextureFormat(format: EncodableTextureFormat) {
const info = kTextureFormatInfo[format];
const bytesPerBlock = info.color?.bytes || info.depth?.bytes || info.stencil?.bytes;
assert(!!bytesPerBlock);
return {
blockWidth: info.blockWidth,
blockHeight: info.blockHeight,
bytesPerBlock,
};
}
/**
* Gets the block width, height, and bytes per block for a color texture format.
* Note that bytesPerBlock will be undefined if format's size is undefined.
* If you are only using color or encodable formats, @see {@link getBlockInfoForColorTextureFormat}
* or {@link getBlockInfoForEncodableTextureFormat}
*/
export function getBlockInfoForTextureFormat(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return {
blockWidth: info.blockWidth,
blockHeight: info.blockHeight,
bytesPerBlock: info.color?.bytes ?? info.depth?.bytes ?? info.stencil?.bytes,
};
}
/**
* Returns the "byteCost" of rendering to a color texture format.
*/
export function getColorRenderByteCost(format: PossibleColorRenderTextureFormat) {
const byteCost = kTextureFormatInfo[format].colorRender?.byteCost;
// MAINTENANCE_TODO: remove this assert. The issue is typescript thinks
// PossibleColorRenderTextureFormat contains all texture formats and not just
// a filtered list.
assert(byteCost !== undefined);
return byteCost;
}
/**
* Returns the "alignment" of rendering to a color texture format.
*/
export function getColorRenderAlignment(format: PossibleColorRenderTextureFormat) {
const alignment = kTextureFormatInfo[format].colorRender?.alignment;
// MAINTENANCE_TODO: remove this assert. The issue is typescript thinks
// PossibleColorRenderTextureFormat contains all texture formats and not just
// a filtered list.
assert(alignment !== undefined);
return alignment;
}
/**
* Gets the baseFormat for a texture format.
*/
export function getBaseFormatForTextureFormat(
format: (typeof kDifferentBaseFormatTextureFormats)[number]
): ColorTextureFormat {
return kTextureFormatInfo[format].baseFormat!;
}
export function getBaseFormatForRegularTextureFormat(
format: RegularTextureFormat
): RegularTextureFormat | undefined {
return kTextureFormatInfo[format].baseFormat as RegularTextureFormat;
}
/**
* Gets the feature needed for a give texture format or undefined if none.
*/
export function getRequiredFeatureForTextureFormat(format: GPUTextureFormat) {
return kTextureFormatInfo[format].feature;
}
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);
}
function isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format: GPUTextureFormat) {
return kTextureFormatTier1AllowsRenderAttachmentBlendableMultisample.includes(
format as ColorTextureFormat
);
}
function isTextureFormatTier1EnablesResolve(format: GPUTextureFormat) {
return kTextureFormatTier1AllowsResolve.includes(format as ColorTextureFormat);
}
function isTextureFormatTier1EnablesStorageReadOnlyWriteOnly(format: GPUTextureFormat) {
return kTextureFormatsTier1EnablesStorageReadOnlyWriteOnly.includes(format as ColorTextureFormat);
}
function isTextureFormatTier2EnablesStorageReadWrite(format: GPUTextureFormat) {
return kTextureFormatsTier2EnablesStorageReadWrite.includes(format as ColorTextureFormat);
}
export function canCopyToAspectOfTextureFormat(format: GPUTextureFormat, aspect: GPUTextureAspect) {
const info = kTextureFormatInfo[format];
switch (aspect) {
case 'depth-only':
assert(isDepthTextureFormat(format));
return info.depth && info.depth.copyDst;
case 'stencil-only':
assert(isStencilTextureFormat(format));
return info.stencil && info.stencil.copyDst;
case 'all':
return (
(!isDepthTextureFormat(format) || info.depth?.copyDst) &&
(!isStencilTextureFormat(format) || info.stencil?.copyDst) &&
(!isColorTextureFormat(format) || !info.color?.copyDst)
);
}
}
export function canCopyFromAspectOfTextureFormat(
format: GPUTextureFormat,
aspect: GPUTextureAspect
) {
const info = kTextureFormatInfo[format];
switch (aspect) {
case 'depth-only':
assert(isDepthTextureFormat(format));
return info.depth && info.depth.copySrc;
case 'stencil-only':
assert(isStencilTextureFormat(format));
return info.stencil && info.stencil.copySrc;
case 'all':
return (
(!isDepthTextureFormat(format) || info.depth?.copySrc) &&
(!isStencilTextureFormat(format) || info.stencil?.copySrc) &&
(!isColorTextureFormat(format) || !info.color?.copySrc)
);
}
}
/**
* Returns true if all aspects of texture can be copied to (used with COPY_DST)
*/
export function canCopyToAllAspectsOfTextureFormat(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return (
(!info.color || info.color.copyDst) &&
(!info.depth || info.depth.copyDst) &&
(!info.stencil || info.stencil.copyDst)
);
}
/**
* Returns true if all aspects of texture can be copied from (used with COPY_SRC)
*/
export function canCopyFromAllAspectsOfTextureFormat(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return (
(!info.color || info.color.copySrc) &&
(!info.depth || info.depth.copySrc) &&
(!info.stencil || info.stencil.copySrc)
);
}
export function isCompressedTextureFormat(format: GPUTextureFormat) {
return format in kCompressedTextureFormatInfo;
}
export function isBCTextureFormat(format: GPUTextureFormat) {
return format in kBCTextureFormatInfo;
}
export function isASTCTextureFormat(format: GPUTextureFormat) {
return format in kASTCTextureFormatInfo;
}
export function isColorTextureFormat(format: GPUTextureFormat) {
return !!kTextureFormatInfo[format].color;
}
export function isDepthTextureFormat(format: GPUTextureFormat) {
return !!kTextureFormatInfo[format].depth;
}
export function isStencilTextureFormat(format: GPUTextureFormat) {
return !!kTextureFormatInfo[format].stencil;
}
export function isDepthStencilTextureFormat(format: GPUTextureFormat) {
return isDepthTextureFormat(format) && isStencilTextureFormat(format);
}
export function isDepthOrStencilTextureFormat(format: GPUTextureFormat) {
return isDepthTextureFormat(format) || isStencilTextureFormat(format);
}
export function isEncodableTextureFormat(format: GPUTextureFormat) {
return kEncodableTextureFormats.includes(format as EncodableTextureFormat);
}
/**
* Returns if a texture can be used as a render attachment. some color formats and all
* depth textures and stencil textures are usable with usage RENDER_ATTACHMENT.
*/
export function isTextureFormatUsableAsRenderAttachment(
device: GPUDevice,
format: GPUTextureFormat
) {
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)) {
return device.features.has('texture-formats-tier1');
}
return kTextureFormatInfo[format].colorRender || isDepthOrStencilTextureFormat(format);
}
/**
* Returns if a texture can be used as a "colorAttachment".
*/
export function isTextureFormatColorRenderable(
device: GPUDevice,
format: GPUTextureFormat
): boolean {
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)) {
return device.features.has('texture-formats-tier1');
}
return !!kAllTextureFormatInfo[format].colorRender;
}
/**
* Returns if a texture can be blended.
*/
export function isTextureFormatBlendable(device: GPUDevice, format: GPUTextureFormat): boolean {
if (!isTextureFormatColorRenderable(device, format)) {
return false;
}
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
}
if (is32Float(format)) {
return device.features.has('float32-blendable');
}
return !!kAllTextureFormatInfo[format].colorRender?.blend;
}
/**
* Returns the texture's type (float, unsigned-float, sint, uint, depth)
*/
export function getTextureFormatType(format: GPUTextureFormat, aspect: GPUTextureAspect = 'all') {
const info = kTextureFormatInfo[format];
let type;
switch (aspect) {
case 'all':
type = info.color?.type ?? info.depth?.type ?? info.stencil?.type;
break;
case 'depth-only':
type = info.depth?.type;
break;
case 'stencil-only':
type = info.stencil?.type;
break;
}
assert(!!type);
return type;
}
/**
* Returns the regular texture's type (float, unsigned-float, sint, uint)
*/
export function getTextureFormatColorType(format: RegularTextureFormat) {
const info = kTextureFormatInfo[format];
const type = info.color?.type;
assert(!!type);
return type;
}
/**
* Returns true if a texture can possibly be used as a render attachment.
* The texture may require certain features to be enabled.
*/
export function isTextureFormatPossiblyUsableAsRenderAttachment(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return (
isDepthOrStencilTextureFormat(format) ||
!!info.colorRender ||
isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)
);
}
/**
* Returns true if a texture can possibly be used as a color render attachment.
* The texture may require certain features to be enabled.
*/
export function isTextureFormatPossiblyUsableAsColorRenderAttachment(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return (
!!info.colorRender || isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)
);
}
/**
* Returns true if a texture can possibly be used multisampled.
* The texture may require certain features to be enabled.
*/
export function isTextureFormatPossiblyMultisampled(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return (
info.multisample || isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)
);
}
/**
* Returns true if a texture can possibly be used as a storage texture.
* The texture may require certain features to be enabled.
*/
export function isTextureFormatPossiblyStorageReadable(format: GPUTextureFormat) {
return (
!!kTextureFormatInfo[format].color?.storage ||
isTextureFormatTier1EnablesStorageReadOnlyWriteOnly(format)
);
}
/**
* Returns true if a texture can possibly be used as a read-write storage texture.
* The texture may require certain features to be enabled.
*/
export function isTextureFormatPossiblyStorageReadWritable(format: GPUTextureFormat) {
return (
!!kTextureFormatInfo[format].color?.readWriteStorage ||
isTextureFormatTier2EnablesStorageReadWrite(format)
);
}
export function is16Float(format: GPUTextureFormat) {
return format === 'r16float' || format === 'rg16float' || format === 'rgba16float';
}
export function is32Float(format: GPUTextureFormat) {
return format === 'r32float' || format === 'rg32float' || format === 'rgba32float';
}
/**
* Returns true if texture is filterable as `texture_xxx<f32>`
*
* examples:
* * 'rgba8unorm' -> true
* * 'depth16unorm' -> false
* * 'rgba32float' -> true (you need to enable feature 'float32-filterable')
*/
export function isTextureFormatPossiblyFilterableAsTextureF32(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return info.color?.type === 'float' || is32Float(format);
}
export const kCompatModeUnsupportedStorageTextureFormats: readonly GPUTextureFormat[] = [
'rg32float',
'rg32sint',
'rg32uint',
] as const;
/**
* Return true if the format can be used as a write only storage texture.
* Note: Some formats can be compiled in a shader but can not be used
* in a pipeline or elsewhere. This function returns whether or not the format
* can be used in general. If you want to know if the format can used when compiling
* a shader @see {@link isTextureFormatUsableAsStorageFormatInCreateShaderModule}
*/
function isTextureFormatUsableAsWriteOnlyStorageTexture(
device: GPUDevice,
format: GPUTextureFormat
): boolean {
if (isCompatibilityDevice(device)) {
if (kCompatModeUnsupportedStorageTextureFormats.indexOf(format) >= 0) {
return false;
}
}
if (format === 'bgra8unorm' && device.features.has('bgra8unorm-storage')) {
return true;
}
if (
isTextureFormatTier1EnablesStorageReadOnlyWriteOnly(format) &&
device.features.has('texture-formats-tier1')
) {
return true;
}
const info = kTextureFormatInfo[format];
return !!(info.color?.storage || info.depth?.storage || info.stencil?.storage);
}
/**
* Return true if the format can be used with the given access mode
* access can be either GPUStorageTextureAccess or WGSL access
* Note: Some formats can be compiled in a shader but can not be used
* in a pipeline or elsewhere. This function returns whether or not the format
* can be used in general. If you want to know if the format can used when compiling
* a shader @see {@link isTextureFormatUsableAsStorageFormatInCreateShaderModule}
*/
export function isTextureFormatUsableWithStorageAccessMode(
device: GPUDevice,
format: GPUTextureFormat,
access: GPUStorageTextureAccess | 'read' | 'write' | 'read_write'
) {
switch (access) {
case 'read':
case 'read-only':
return isTextureFormatUsableAsReadOnlyStorageTexture(device, format);
case 'write':
case 'write-only':
return isTextureFormatUsableAsWriteOnlyStorageTexture(device, format);
case 'read_write':
case 'read-write':
return isTextureFormatUsableAsReadWriteStorageTexture(device, format);
}
}
/**
* Return true if the format can be used as a read only storage texture.
* Note: Some formats can be compiled in a shader but can not be used
* in a pipeline or elsewhere. This function returns whether or not the format
* can be used in general. If you want to know if the format can used when compiling
* a shader @see {@link isTextureFormatUsableAsStorageFormatInCreateShaderModule}
*/
function isTextureFormatUsableAsReadOnlyStorageTexture(
device: GPUDevice,
format: GPUTextureFormat
): boolean {
// This is the only storage texture format that isn't readable as a storage format.
if (format === 'bgra8unorm') {
return false;
}
// All other formats that can be used as a storage texture can be used as
// both read-only and write-only.
return isTextureFormatUsableAsWriteOnlyStorageTexture(device, format);
}
/**
* Returns true if format can be used with createShaderModule on the device.
* Some formats may require a feature to be enabled before they can be used
* as a storage texture. Others, can't be used in a pipeline but can be compiled
* in a shader. Examples are rg32float, rg32uint, rg32sint which are not usable
* in compat mode but shaders can be compiled. Similarly, bgra8unorm can be
* compiled but can't be used in a pipeline unless feature 'bgra8unorm-storage'
* is available.
*/
export function isTextureFormatUsableAsStorageFormatInCreateShaderModule(
device: GPUDevice,
format: GPUTextureFormat
): boolean {
return kPossibleStorageTextureFormats.includes(
format as (typeof kPossibleStorageTextureFormats)[number]
);
}
function isTextureFormatUsableAsReadWriteStorageTexture(
device: GPUDevice,
format: GPUTextureFormat
): boolean {
if (isTextureFormatTier2EnablesStorageReadWrite(format)) {
return device.features.has('texture-formats-tier2');
}
return !!kTextureFormatInfo[format].color?.readWriteStorage;
}
export function isRegularTextureFormat(format: GPUTextureFormat) {
return format in kRegularTextureFormatInfo;
}
/**
* Returns true if format is both compressed and a float format, for example 'bc6h-rgb-ufloat'.
*/
export function isCompressedFloatTextureFormat(format: GPUTextureFormat) {
return isCompressedTextureFormat(format) && format.includes('float');
}
/**
* Returns true if format is sint or uint
*/
export function isSintOrUintFormat(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
const type = info.color?.type ?? info.depth?.type ?? info.stencil?.type;
return type === 'sint' || type === 'uint';
}
/**
* Returns true if format can be multisampled.
*/
export const kCompatModeUnsupportedMultisampledTextureFormats: readonly GPUTextureFormat[] = [
'r8uint',
'r8sint',
'rg8uint',
'rg8sint',
'rgba8uint',
'rgba8sint',
'r16uint',
'r16sint',
'rg16uint',
'rg16sint',
'rgba16uint',
'rgba16sint',
'rgb10a2uint',
'rgba16float',
'r32float',
] as const;
/**
* Returns true if you can make a multisampled texture from the given format.
*/
export function isTextureFormatMultisampled(device: GPUDevice, format: GPUTextureFormat): boolean {
if (isCompatibilityDevice(device)) {
if (kCompatModeUnsupportedMultisampledTextureFormats.indexOf(format) >= 0) {
return false;
}
}
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)) {
return device.features.has('texture-formats-tier1');
}
return kAllTextureFormatInfo[format].multisample;
}
/**
* Returns true if a texture can be "resolved". uint/sint formats can be multisampled but
* can not be resolved.
*/
export function isTextureFormatResolvable(device: GPUDevice, format: GPUTextureFormat): boolean {
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesResolve(format)) {
return device.features.has('texture-formats-tier1');
}
// You can't resolve a non-multisampled format.
if (!isTextureFormatMultisampled(device, format)) {
return false;
}
const info = kAllTextureFormatInfo[format];
return !!info.colorRender?.resolve;
}
// MAINTENANCE_TODD: See if we can remove this. This doesn't seem useful since
// formats are not on/off by feature. Some are on but a feature allows them to be
// used in more cases, like going from un-renderable to renderable, etc...
export const kFeaturesForFormats = getFeaturesForFormats(kAllTextureFormats);
/**
* Given an array of texture formats return the number of bytes per sample.
*/
export function computeBytesPerSampleFromFormats(formats: readonly GPUTextureFormat[]) {
let bytesPerSample = 0;
for (const format of formats) {
// MAINTENANCE_TODO: Add colorRender to rg11b10ufloat format in kTextureFormatInfo
// The issue is if we add it now lots of tests will break as they'll think they can
// render to the format but are not enabling 'rg11b10ufloat-renderable'. Once we
// get the CTS refactored (see issue 4181), then fix this.
const info = kTextureFormatInfo[format];
const alignedBytesPerSample = align(bytesPerSample, info.colorRender!.alignment);
bytesPerSample = alignedBytesPerSample + info.colorRender!.byteCost;
}
return bytesPerSample;
}
/**
* Given an array of GPUColorTargetState return the number of bytes per sample
*/
export function computeBytesPerSample(targets: GPUColorTargetState[]) {
return computeBytesPerSampleFromFormats(targets.map(({ format }) => format));
}
/**
* Returns the maximum valid size in each dimension for a given texture format.
* This is useful because compressed formats must be a multiple of blocks in size
* so, for example, the largest valid width of a 2d texture
* roundDown(device.limits.maxTextureDimension2D, blockWidth)
*/
export function getMaxValidTextureSizeForFormatAndDimension(
device: GPUDevice,
format: GPUTextureFormat,
dimension: GPUTextureDimension
): [number, number, number] {
const info = getBlockInfoForTextureFormat(format);
switch (dimension) {
case '1d':
return [device.limits.maxTextureDimension1D, 1, 1];
case '2d':
return [
roundDown(device.limits.maxTextureDimension2D, info.blockWidth),
roundDown(device.limits.maxTextureDimension2D, info.blockHeight),
device.limits.maxTextureArrayLayers,
];
case '3d':
return [
roundDown(device.limits.maxTextureDimension3D, info.blockWidth),
roundDown(device.limits.maxTextureDimension3D, info.blockHeight),
device.limits.maxTextureDimension3D,
];
}
}