import { keysOf } from '../common/util/data_tables.js';
import { assert } from '../common/util/util.js';

import { align } from './util/math.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.
 */
const kFormatUniversalDefaults = {
  blockWidth: undefined,
  blockHeight: undefined,
  color: undefined,
  depth: undefined,
  stencil: undefined,
  colorRender: undefined,
  multisample: undefined,
  feature: undefined,
  baseFormat: undefined,

  sampleType: undefined,
  copySrc: undefined,
  copyDst: undefined,
  bytesPerBlock: undefined,
  renderable: false,
  renderTargetPixelByteCost: undefined,
  renderTargetComponentAlignment: 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, copySrc: true, copyDst: true },
  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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      baseFormat: 'rgba8unorm',
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      baseFormat: 'rgba8unorm',
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      baseFormat: 'bgra8unorm',
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      baseFormat: 'bgra8unorm',
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
    },

    // plain, 16 bits per component

    r16uint: {
      color: {
        type: 'uint',
        copySrc: true,
        copyDst: true,
        storage: false,
        readWriteStorage: false,
        bytes: 2,
      },
      colorRender: { blend: false, resolve: false, byteCost: 2, alignment: 2 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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 },
      renderable: true,
      /*prettier-ignore*/ get renderTargetComponentAlignment() { return this.colorRender.alignment; },
      /*prettier-ignore*/ get renderTargetPixelByteCost() { return this.colorRender.byteCost; },
      multisample: true,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
    },
    rg11b10ufloat: {
      color: {
        type: 'float',
        copySrc: true,
        copyDst: true,
        storage: false,
        readWriteStorage: false,
        bytes: 4,
      },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*prettier-ignore*/ get bytesPerBlock() { return this.color.bytes; },
      renderTargetPixelByteCost: 8,
      renderTargetComponentAlignment: 4,
    },

    // packed

    rgb9e5ufloat: {
      color: {
        type: 'float',
        copySrc: true,
        copyDst: true,
        storage: false,
        readWriteStorage: false,
        bytes: 4,
      },
      multisample: false,
      /*prettier-ignore*/ get sampleType() { return this.color.type; },
      /*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, copySrc: true, renderable: true },
  table: {
    stencil8: {
      stencil: {
        type: 'uint',
        copySrc: true,
        copyDst: true,
        storage: false,
        readWriteStorage: false,
        bytes: 1,
      },
      sampleType: 'uint',
      copyDst: true,
      bytesPerBlock: 1,
    },
    depth16unorm: {
      depth: {
        type: 'depth',
        copySrc: true,
        copyDst: true,
        storage: false,
        readWriteStorage: false,
        bytes: 2,
      },
      sampleType: 'depth',
      copyDst: true,
      bytesPerBlock: 2,
    },
    depth32float: {
      depth: {
        type: 'depth',
        copySrc: true,
        copyDst: false,
        storage: false,
        readWriteStorage: false,
        bytes: 4,
      },
      sampleType: 'depth',
      copyDst: false,
      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,
      },
      copySrc: false,
      copyDst: false,
      sampleType: 'depth',
      renderable: true,
    },
    '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,
      },
      copySrc: false,
      copyDst: false,
      sampleType: 'depth',
      renderable: true,
    },
    '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',
      copySrc: false,
      copyDst: false,
      sampleType: 'depth',
      renderable: true,
    },
  },
} as const);

const kBCTextureFormatInfo = formatTableWithDefaults({
  defaults: {
    blockWidth: 4,
    blockHeight: 4,
    multisample: false,
    feature: 'texture-compression-bc',
    sampleType: 'float',
    copySrc: true,
    copyDst: true,
  },
  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',
    sampleType: 'float',
    copySrc: true,
    copyDst: true,
  },
  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',
    sampleType: 'float',
    copySrc: true,
    copyDst: true,
  },
  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. 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].colorRender
);
assert(
  kRenderableColorTextureFormats.every(
    f =>
      kAllTextureFormatInfo[f].renderTargetComponentAlignment !== undefined &&
      kAllTextureFormatInfo[f].renderTargetPixelByteCost !== undefined
  )
);

/** 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.
 * Documentation is also written here, but not actually visible to the IDE.
 */
type TextureFormatInfo_TypeCheck = {
  /** Texel block width. */
  blockWidth: number;
  /** Texel block height. */
  blockHeight: number;
  /** Whether the format can be used in a multisample texture. */
  multisample: boolean;
  /** The base format for srgb formats. Specified on both srgb and equivalent non-srgb formats. */
  baseFormat: GPUTextureFormat | undefined;
  /** Optional feature required to use this format, or `undefined` if none. */
  feature: GPUFeatureName | undefined;

  /** @deprecated */
  sampleType: GPUTextureSampleType;
  /** @deprecated */
  copySrc: boolean;
  /** @deprecated */
  copyDst: boolean;
  /** @deprecated */
  bytesPerBlock: number | undefined;
  /** @deprecated */
  renderable: boolean;
  /** @deprecated */
  renderTargetPixelByteCost: number | undefined;
  /** @deprecated */
  renderTargetComponentAlignment: 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;
        }
    )
);

/** Per-GPUTextureFormat info. */
export 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;

/** List of all GPUTextureFormat values. */
// MAINTENANCE_TODO: dedup with kAllTextureFormats
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;

//
// 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;
}

/**
 * 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)
  );
}

/**
 * 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 function isCompressedTextureFormat(format: GPUTextureFormat) {
  return format in kCompressedTextureFormatInfo;
}

export const kCompatModeUnsupportedStorageTextureFormats: readonly GPUTextureFormat[] = [
  'rg32float',
  'rg32sint',
  'rg32uint',
] as const;

export function isTextureFormatUsableAsStorageFormat(
  format: GPUTextureFormat,
  isCompatibilityMode: boolean
) {
  if (isCompatibilityMode) {
    if (kCompatModeUnsupportedStorageTextureFormats.indexOf(format) >= 0) {
      return false;
    }
  }
  return !!kTextureFormatInfo[format].color?.storage;
}

export function isEncodableTextureformat(format: GPUTextureFormat) {
  return format in kEncodableTextureFormatInfo;
}

export const kFeaturesForFormats = getFeaturesForFormats(kTextureFormats);

/**
 * 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) {
    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));
}
