diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 37e7e1f333..bd7375c719 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,7 @@ on: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: node-version: [20] diff --git a/modules/core/src/adapter/device.ts b/modules/core/src/adapter/device.ts index 437cbcc9db..d93576427a 100644 --- a/modules/core/src/adapter/device.ts +++ b/modules/core/src/adapter/device.ts @@ -5,7 +5,8 @@ import {StatsManager, lumaStats} from '../utils/stats-manager'; import {log} from '../utils/log'; import {uid} from '../utils/uid'; -import type {TextureFormat} from '../gpu-type-utils//texture-formats'; +import type {TextureFormat} from '../gpu-type-utils/texture-formats'; +import type {TextureFormatCapabilities} from '../gpu-type-utils/texture-format-capabilities'; import type {CanvasContext, CanvasContextProps} from './canvas-context'; import type {BufferProps} from './resources/buffer'; import {Buffer} from './resources/buffer'; @@ -24,6 +25,7 @@ import type {TransformFeedback, TransformFeedbackProps} from './resources/transf import type {QuerySet, QuerySetProps} from './resources/query-set'; import {isTextureFormatCompressed} from '../gpu-type-utils/decode-texture-format'; +import {getTextureFormatCapabilities} from '../gpu-type-utils/texture-format-capabilities'; /** * Identifies the GPU vendor and driver. @@ -170,7 +172,7 @@ export type WebGLDeviceFeature = // texture rendering | 'float32-renderable-webgl' | 'float16-renderable-webgl' - | 'rgb9e5ufloat_renderable-webgl' + | 'rgb9e5ufloat-renderable-webgl' | 'snorm8-renderable-webgl' | 'norm16-renderable-webgl' | 'snorm16-renderable-webgl' @@ -192,6 +194,21 @@ type WebGLCompressedTextureFeatures = | 'texture-compression-pvrtc-webgl' | 'texture-compression-atc-webgl'; +/** Texture format capabilities that have been checked against a specific device */ +export type DeviceTextureFormatCapabilities = { + format: TextureFormat; + /** Can the format be created */ + create: boolean; + /** If a feature string, the specified device feature determines if format is renderable. */ + render: boolean; + /** If a feature string, the specified device feature determines if format is filterable. */ + filter: boolean; + /** If a feature string, the specified device feature determines if format is blendable. */ + blend: boolean; + /** If a feature string, the specified device feature determines if format is storeable. */ + store: boolean; +}; + /** Device properties */ export type DeviceProps = { /** string id for debugging. Stored on the object, used in logging and set on underlying GPU objects when feasible. */ @@ -348,14 +365,47 @@ export abstract class Device { /** WebGPU style device limits */ abstract get limits(): DeviceLimits; + /** Determines what operations are supported on a texture format, checking against supported device features */ + getTextureFormatCapabilities(format: TextureFormat): DeviceTextureFormatCapabilities { + const genericCapabilities = getTextureFormatCapabilities(format); + + // Check standard features + const checkFeature = (featureOrBoolean: DeviceFeature | boolean | undefined) => + (typeof featureOrBoolean === 'string' + ? this.features.has(featureOrBoolean) + : featureOrBoolean) ?? true; + + const supported = checkFeature(genericCapabilities.create); + + const deviceCapabilities: DeviceTextureFormatCapabilities = { + format, + create: supported, + render: supported && checkFeature(genericCapabilities.render), + filter: supported && checkFeature(genericCapabilities.filter), + blend: supported && checkFeature(genericCapabilities.blend), + store: supported && checkFeature(genericCapabilities.store) + }; + + return this._getDeviceSpecificTextureFormatCapabilities(deviceCapabilities); + } + /** Check if device supports a specific texture format (creation and `nearest` sampling) */ - abstract isTextureFormatSupported(format: TextureFormat): boolean; + isTextureFormatSupported( + format: TextureFormat, + capabilities: Partial + ): boolean { + return this.getTextureFormatCapabilities(format).create; + } /** Check if linear filtering (sampler interpolation) is supported for a specific texture format */ - abstract isTextureFormatFilterable(format: TextureFormat): boolean; + isTextureFormatFilterable(format: TextureFormat): boolean { + return this.getTextureFormatCapabilities(format).filter; + } - /** Check if device supports rendering to a specific texture format */ - abstract isTextureFormatRenderable(format: TextureFormat): boolean; + /** Check if device supports rendering to a framebuffer color attachment of a specific texture format */ + isTextureFormatRenderable(format: TextureFormat): boolean { + return this.getTextureFormatCapabilities(format).render; + } /** Check if a specific texture format is GPU compressed */ isTextureFormatCompressed(format: TextureFormat): boolean { @@ -532,6 +582,14 @@ export abstract class Device { // IMPLEMENTATION + /** + * Determines what operations are supported on a texture format, checking against supported device features + * Subclasses override to apply additional checks + */ + protected abstract _getDeviceSpecificTextureFormatCapabilities( + format: DeviceTextureFormatCapabilities + ): DeviceTextureFormatCapabilities; + /** Subclasses use this to support .createBuffer() overloads */ protected _normalizeBufferProps(props: BufferProps | ArrayBuffer | ArrayBufferView): BufferProps { if (props instanceof ArrayBuffer || ArrayBuffer.isView(props)) { diff --git a/modules/core/src/gpu-type-utils/decode-texture-format.ts b/modules/core/src/gpu-type-utils/decode-texture-format.ts index d1348c1996..fb0b0f9413 100644 --- a/modules/core/src/gpu-type-utils/decode-texture-format.ts +++ b/modules/core/src/gpu-type-utils/decode-texture-format.ts @@ -29,83 +29,107 @@ export function isTextureFormatCompressed( * Decodes a texture format, returning e.g. attatchment type, components, byte length and flags (integer, signed, normalized) */ export function decodeTextureFormat(format: TextureFormat): TextureFormatInfo { + let formatInfo: TextureFormatInfo = decodeTextureFormatUsingTable(format); + if (isTextureFormatCompressed(format)) { - return decodeCompressedTextureFormat(format); + formatInfo.channels = 'rgb'; + formatInfo.components = 3; + formatInfo.bytesPerPixel = 1; + formatInfo.srgb = false; + formatInfo.compressed = true; + + const blockSize = getCompressedTextureBlockSize(format); + if (blockSize) { + formatInfo.blockWidth = blockSize.blockWidth; + formatInfo.blockHeight = blockSize.blockHeight; + } } + // Fill in missing information that can be derived from the format string const matches = RGB_FORMAT_REGEX.exec(format as string); - if (!matches) { - return decodeNonStandardFormat(format); + if (matches) { + const [, channels, length, type, srgb, suffix] = matches; + const dataType = `${type}${length}` as VertexType; + const decodedType = decodeVertexType(dataType); + const bits = decodedType.byteLength * 8; + const components = channels.length as 1 | 2 | 3 | 4; + const bitsPerChannel: [number, number, number, number] = [ + bits, + components >= 2 ? bits : 0, + components >= 3 ? bits : 0, + components >= 4 ? bits : 0 + ]; + + formatInfo = { + format, + attachment: formatInfo.attachment, + dataType: decodedType.dataType, + components, + channels: channels as 'r' | 'rg' | 'rgb' | 'rgba', + integer: decodedType.integer, + signed: decodedType.signed, + normalized: decodedType.normalized, + bitsPerChannel, + bytesPerPixel: decodedType.byteLength * channels.length, + packed: formatInfo.packed, + srgb: formatInfo.srgb + }; + + if (suffix === '-webgl') { + formatInfo.webgl = true; + } + // dataType - overwritten by decodedType + if (srgb === '-srgb') { + formatInfo.srgb = true; + } } - const [, channels, length, type, srgb, suffix] = matches; - const dataType = `${type}${length}` as VertexType; - const decodedType = decodeVertexType(dataType); - const bits = decodedType.byteLength * 8; - const components = channels.length as 1 | 2 | 3 | 4; - const bitsPerChannel: [number, number, number, number] = [ - bits, - components >= 2 ? bits : 0, - components >= 3 ? bits : 0, - components >= 4 ? bits : 0 - ]; - - const info: TextureFormatInfo = { - format, - channels: channels as 'r' | 'rg' | 'rgb' | 'rgba', - components, - bitsPerChannel, - bytesPerPixel: decodedType.byteLength * channels.length, - dataType: decodedType.dataType, - integer: decodedType.integer, - signed: decodedType.signed, - normalized: decodedType.normalized - }; - - if (suffix === '-webgl') { - info.webgl = true; + if (format.endsWith('-webgl')) { + formatInfo.webgl = true; } - // dataType - overwritten by decodedType - if (srgb === '-srgb') { - info.srgb = true; + if (format.endsWith('-srgb')) { + formatInfo.srgb = true; } - return info; -} -function decodeCompressedTextureFormat(format: CompressedTextureFormat): TextureFormatInfo { - const info: TextureFormatInfo = { - channels: 'rgb', - components: 3, - bytesPerPixel: 1, - srgb: false, - compressed: true - } as TextureFormatInfo; - - const blockSize = getCompressedTextureBlockSize(format); - if (blockSize) { - info.blockWidth = blockSize.blockWidth; - info.blockHeight = blockSize.blockHeight; - } - return info; + return formatInfo; } -function decodeNonStandardFormat(format: TextureFormat): TextureFormatInfo { - const data = TEXTURE_FORMAT_TABLE[format]; - if (!data) { - throw new Error(`Unknown format ${format}`); +/** Decode texture format info from the table */ +function decodeTextureFormatUsingTable(format: TextureFormat): TextureFormatInfo { + const info = TEXTURE_FORMAT_TABLE[format]; + if (!info) { + throw new Error(`Unknown texture format ${format}`); } - const info: TextureFormatInfo = { - ...data, - channels: data.channels || '', - components: data.components || data.channels?.length || 1, - bytesPerPixel: data.bytesPerPixel || 1, - srgb: false - } as TextureFormatInfo; - if (data.packed) { - info.packed = data.packed; - } - return info; + const bytesPerPixel = info.bytesPerPixel || 1; + const bitsPerChannel = info.bitsPerChannel || [8, 8, 8, 8]; + delete info.bitsPerChannel; + delete info.bytesPerPixel; + delete info.create; + delete info.render; + delete info.filter; + delete info.blend; + delete info.store; + + const formatInfo: TextureFormatInfo = { + ...info, + format, + attachment: info.attachment || 'color', + channels: info.channels || 'r', + components: (info.components || info.channels?.length || 1) as 1 | 2 | 3 | 4, + bytesPerPixel, + bitsPerChannel, + dataType: info.dataType || 'uint8', + srgb: info.srgb ?? false, + packed: info.packed ?? false, + webgl: info.webgl ?? false, + integer: info.integer ?? false, + signed: info.signed ?? false, + normalized: info.normalized ?? false, + compressed: info.compressed ?? false + }; + + return formatInfo; } /** Parses ASTC block widths from format string */ diff --git a/modules/core/src/gpu-type-utils/texture-features.ts b/modules/core/src/gpu-type-utils/texture-features.ts index 6c582873a3..249254785d 100644 --- a/modules/core/src/gpu-type-utils/texture-features.ts +++ b/modules/core/src/gpu-type-utils/texture-features.ts @@ -15,7 +15,7 @@ export type TextureFeature = | 'texture-compression-atc-webgl' | 'float32-renderable-webgl' | 'float16-renderable-webgl' - | 'rgb9e5ufloat_renderable-webgl' + | 'rgb9e5ufloat-renderable-webgl' | 'snorm8-renderable-webgl' | 'norm16-renderable-webgl' | 'snorm16-renderable-webgl' diff --git a/modules/core/src/gpu-type-utils/texture-format-capabilities.ts b/modules/core/src/gpu-type-utils/texture-format-capabilities.ts new file mode 100644 index 0000000000..568eda4fd3 --- /dev/null +++ b/modules/core/src/gpu-type-utils/texture-format-capabilities.ts @@ -0,0 +1,56 @@ +// luma.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import type {TextureFormat} from './texture-formats'; +import type {TextureFeature} from './texture-features'; +import {decodeTextureFormat} from './decode-texture-format'; + +import {TEXTURE_FORMAT_TABLE} from './texture-format-table'; + +/** + * Texture format capabilities. + * @note Not directly usable. Can contain TextureFeature strings that need to be checked against a specific device. + */ +export type TextureFormatCapabilities = { + format: TextureFormat; + /** Can the format be created */ + create: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is renderable. */ + render: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is filterable. */ + filter: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is blendable. */ + blend: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is storeable. */ + store: TextureFeature | boolean; +}; + +export function getTextureFormatCapabilities(format: TextureFormat): TextureFormatCapabilities { + const info = TEXTURE_FORMAT_TABLE[format]; + if (!info) { + throw new Error(`Unknown texture format ${format}`); + } + + const formatCapabilities: Required = { + format, + create: info.create ?? true, + render: info.render ?? true, + filter: info.filter ?? true, + blend: info.blend ?? true, + store: info.store ?? true + }; + + const formatInfo = decodeTextureFormat(format); + const isDepthStencil = format.startsWith('depth') || format.startsWith('stencil'); + const isSigned = formatInfo?.signed; + const isInteger = formatInfo?.integer; + const isWebGLSpecific = formatInfo?.webgl; + + // signed formats are not renderable + formatCapabilities.render &&= !isSigned; + // signed and integer formats are not filterable + formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific; + + return formatCapabilities; +} diff --git a/modules/core/src/gpu-type-utils/texture-format-info.ts b/modules/core/src/gpu-type-utils/texture-format-info.ts index ecfd236593..1c4cae0bcb 100644 --- a/modules/core/src/gpu-type-utils/texture-format-info.ts +++ b/modules/core/src/gpu-type-utils/texture-format-info.ts @@ -4,7 +4,6 @@ import {TextureFormat} from './texture-formats'; import {VertexType} from './vertex-formats'; -import {TextureFeature} from './texture-features'; /** Information about the structure of a texture format */ export type TextureFormatInfo = { @@ -40,13 +39,4 @@ export type TextureFormatInfo = { blockWidth?: number; /** Compressed formats only: Block size for ASTC formats (texture height must be a multiple of this value) */ blockHeight?: number; - - /** If a feature, the specified device feature determines if format is renderable. true if not set. */ - render?: TextureFeature | boolean; - /** If a feature, the specified device feature determines if format is filterable. true if not set. */ - filter?: TextureFeature | boolean; - /** If a feature, the specified device feature determines if format is blendable. true if not set. */ - blend?: TextureFeature | boolean; - /** If a feature, the specified device feature determines if format is storeable. true if not set. */ - store?: TextureFeature | boolean; }; diff --git a/modules/core/src/gpu-type-utils/texture-format-table.ts b/modules/core/src/gpu-type-utils/texture-format-table.ts index c115c9d1c9..a233b1b3c0 100644 --- a/modules/core/src/gpu-type-utils/texture-format-table.ts +++ b/modules/core/src/gpu-type-utils/texture-format-table.ts @@ -5,219 +5,197 @@ import {TextureFormat} from './texture-formats'; import {TextureFeature} from './texture-features'; import {TextureFormatInfo} from './texture-format-info'; - /* eslint-disable camelcase */ -// Define local device feature strings to optimize minification -const texture_compression_bc: TextureFeature = 'texture-compression-bc'; -const texture_compression_astc: TextureFeature = 'texture-compression-astc'; -const texture_compression_etc2: TextureFeature = 'texture-compression-etc2'; -const texture_compression_etc1_webgl: TextureFeature = 'texture-compression-etc1-webgl'; -const texture_compression_pvrtc_webgl: TextureFeature = 'texture-compression-pvrtc-webgl'; -const texture_compression_atc_webgl: TextureFeature = 'texture-compression-atc-webgl'; - -const float32_renderable: TextureFeature = 'float32-renderable-webgl'; -const float16_renderable: TextureFeature = 'float16-renderable-webgl'; -const rgb9e5ufloat_renderable: TextureFeature = 'rgb9e5ufloat_renderable-webgl'; -const snorm8_renderable: TextureFeature = 'snorm8-renderable-webgl'; -const norm16_renderable: TextureFeature = 'norm16-renderable-webgl'; -const snorm16_renderable: TextureFeature = 'snorm16-renderable-webgl'; - -const float32_filterable: TextureFeature = 'float32-filterable'; -const float16_filterable: TextureFeature = 'float16-filterable-webgl'; - -/** https://www.w3.org/TR/webgpu/#texture-format-caps */ - +/** + * @see https://www.w3.org/TR/webgpu/#texture-format-caps + * @see https://registry.khronos.org/OpenGL/specs/es/3.0/es_spec_3.0.pdf Table 3.13 + */ type TextureFormatDefinition = Partial & { - /** for compressed texture formats */ - f?: TextureFeature; - /** renderable if feature is present. false means the spec does not support this format */ - render?: TextureFeature | false; - /** filterable if feature is present. false means the spec does not support this format */ - filter?: TextureFeature | false; - - /** (bytes per pixel), for memory usage calculations. */ - b?: number; - /** channels */ - c?: number; - bpp?: number; - /** packed */ - p?: number; - - /** If not supported on WebGPU */ - wgpu?: false; + /** Can the format be created */ + create?: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is renderable. */ + render?: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is filterable. */ + filter?: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is blendable. */ + blend?: TextureFeature | boolean; + /** If a feature string, the specified device feature determines if format is storeable. */ + store?: TextureFeature | boolean; }; // prettier-ignore -export const TEXTURE_FORMAT_TABLE: Readonly>> = { +export const TEXTURE_FORMAT_TABLE: Readonly> = { // 8-bit formats - 'r8unorm': {bpp: 1, c: 1}, - 'r8snorm': {bpp: 1, c: 1, render: snorm8_renderable}, - 'r8uint': {bpp: 1, c: 1}, - 'r8sint': {bpp: 1, c: 1}, + 'r8unorm': {}, + 'r8snorm': {render: 'snorm8-renderable-webgl'}, + 'r8uint': {}, + 'r8sint': {}, // 16-bit formats - 'rg8unorm': {bpp: 2, c: 2}, - 'rg8snorm': {bpp: 2, c: 2, render: snorm8_renderable}, - 'rg8uint': {bpp: 2, c: 2}, - 'rg8sint': {bpp: 2, c: 2}, + 'rg8unorm': {}, + 'rg8snorm': {render: 'snorm8-renderable-webgl'}, + 'rg8uint': {}, + 'rg8sint': {}, - 'r16uint': {bpp: 2, c: 1}, - 'r16sint': {bpp: 2, c: 1}, - 'r16float': {bpp: 2, c: 1, render: float16_renderable, filter: 'float16-filterable-webgl'}, - 'r16unorm-webgl': {b:2, c:1, f: norm16_renderable}, - 'r16snorm-webgl': {b:2, c:1, f: snorm16_renderable}, + 'r16uint': {}, + 'r16sint': {}, + 'r16float': {render: 'float16-renderable-webgl', filter: 'float16-filterable-webgl'}, + 'r16unorm-webgl': {create: 'norm16-renderable-webgl'}, + 'r16snorm-webgl': {create: 'snorm16-renderable-webgl'}, // Packed 16-bit formats - 'rgba4unorm-webgl': {channels: 'rgba', bpp: 2, bitsPerChannel: [4, 4, 4, 4], packed: true}, - 'rgb565unorm-webgl': {channels: 'rgb', bpp: 2, bitsPerChannel: [5, 6, 5, 0], packed: true}, - 'rgb5a1unorm-webgl': {channels: 'rgba', bpp: 2, bitsPerChannel: [5, 5, 5, 1], packed: true}, + 'rgba4unorm-webgl': {channels: 'rgba', bitsPerChannel: [4, 4, 4, 4], packed: true}, + 'rgb565unorm-webgl': {channels: 'rgb', bitsPerChannel: [5, 6, 5, 0], packed: true}, + 'rgb5a1unorm-webgl': {channels: 'rgba', bitsPerChannel: [5, 5, 5, 1], packed: true}, // 24-bit formats - 'rgb8unorm-webgl': {bpp: 3, c: 3, wgpu: false}, - 'rgb8snorm-webgl': {bpp: 3, c: 3, wgpu: false}, + 'rgb8unorm-webgl': {}, + 'rgb8snorm-webgl': {}, // 32-bit formats - 'rgba8unorm': {c: 2, bpp: 4, bitsPerChannel: [8, 8, 8, 8]}, - 'rgba8unorm-srgb': {c: 4, bpp: 4, bitsPerChannel: [8, 8, 8, 8]}, - 'rgba8snorm': {bpp: 4, c: 4, bitsPerChannel: [8, 8, 8, 8], render: snorm8_renderable}, - 'rgba8uint': {c: 4, bpp: 4, bitsPerChannel: [8, 8, 8, 8]}, - 'rgba8sint': {c: 4, bpp: 4,bitsPerChannel: [8, 8, 8, 8]}, + 'rgba8unorm': {bitsPerChannel: [8, 8, 8, 8]}, + 'rgba8unorm-srgb': {bitsPerChannel: [8, 8, 8, 8]}, + 'rgba8snorm': {bitsPerChannel: [8, 8, 8, 8], render: 'snorm8-renderable-webgl'}, + 'rgba8uint': {bitsPerChannel: [8, 8, 8, 8]}, + 'rgba8sint': {bitsPerChannel: [8, 8, 8, 8]}, // 32-bit, reverse colors, webgpu only - 'bgra8unorm': {bpp: 4, c: 4}, - 'bgra8unorm-srgb': {bpp: 4, c: 4}, + 'bgra8unorm': {}, + 'bgra8unorm-srgb': {}, - 'rg16uint': {c: 1, bpp: 4}, - 'rg16sint': {c: 2, bpp: 4}, - 'rg16float': {c: 2, bpp: 4, render: float16_renderable, filter: float16_filterable}, - 'rg16unorm-webgl': {b:2, c:2, render: norm16_renderable}, - 'rg16snorm-webgl': {b:2, c:2, render: snorm16_renderable}, + 'r32float': {}, - 'r32uint': {c: 1, bpp: 4}, - 'r32sint': {c: 1, bpp: 4}, - 'r32float': {c: 1, bpp: 4, render: float32_renderable, filter: float32_filterable}, + 'rg16uint': {}, + 'rg16sint': {}, + 'rg16float': {render: 'float16-renderable-webgl', filter: 'float16-filterable-webgl'}, + 'rg16unorm-webgl': {render: 'norm16-renderable-webgl'}, + 'rg16snorm-webgl': {render: 'snorm16-renderable-webgl'}, + 'r32uint': {}, + 'r32sint': {}, + // Packed 32 bit formats - 'rgb9e5ufloat': {channels: 'rgb', packed: true, c: 3, bpp: 4,p: 1, render: rgb9e5ufloat_renderable}, // , filter: true}, - 'rg11b10ufloat': {channels: 'rgb', c: 3, bpp: 4, bitsPerChannel: [11, 11, 10, 0], packed: true, p: 1,render: float32_renderable}, - 'rgb10a2unorm': {channels: 'rgba', c: 4, bpp: 4, bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1}, - 'rgb10a2uint-webgl': {channels: 'rgba', c: 4, bpp: 4, bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1, wgpu: false}, + 'rgb9e5ufloat': {channels: 'rgb', packed: true, render: 'rgb9e5ufloat-renderable-webgl'}, // , filter: true}, + 'rg11b10ufloat': {channels: 'rgb', bitsPerChannel: [11, 11, 10, 0], packed: true, render: 'float32-renderable-webgl'}, + 'rgb10a2unorm': {channels: 'rgba', bitsPerChannel: [10, 10, 10, 2], packed: true}, + 'rgb10a2uint-webgl': {channels: 'rgba', bitsPerChannel: [10, 10, 10, 2], packed: true}, // 48-bit formats - 'rgb16unorm-webgl': {b:2, c:3, f: norm16_renderable}, // rgb not renderable - 'rgb16snorm-webgl': {b:2, c:3, f: norm16_renderable}, // rgb not renderable + 'rgb16unorm-webgl': {create: 'norm16-renderable-webgl'}, // rgb not renderable + 'rgb16snorm-webgl': {create: 'norm16-renderable-webgl'}, // rgb not renderable // 64-bit formats - 'rg32uint': {bpp: 8, c: 2, bitsPerChannel: [32, 32, 0, 0]}, - 'rg32sint': {bpp: 8, c: 2, bitsPerChannel: [32, 32, 0, 0]}, - 'rg32float': {bpp: 8, c: 2, bitsPerChannel: [32, 32, 0, 0], render: false, filter: float32_filterable}, - 'rgba16uint': {bpp: 8, c: 4}, - 'rgba16sint': {bpp: 8, c: 4}, - 'rgba16float': {bpp: 8, c: 4, render: float16_renderable, filter: float16_filterable}, - 'rgba16unorm-webgl': {b:2, c:4, render: norm16_renderable}, - 'rgba16snorm-webgl': {b:2, c:4, render: snorm16_renderable}, + 'rg32uint': {bitsPerChannel: [32, 32, 0, 0]}, + 'rg32sint': {bitsPerChannel: [32, 32, 0, 0]}, + 'rgba16uint': {}, + 'rgba16sint': {}, + 'rgba16unorm-webgl': {render: 'norm16-renderable-webgl'}, + 'rgba16snorm-webgl': {render: 'snorm16-renderable-webgl'}, + + 'rg32float': {render: 'float32-renderable-webgl', filter: 'float32-filterable'}, + 'rgba16float': {render: 'float16-renderable-webgl', filter: 'float16-filterable-webgl'}, // 96-bit formats (deprecated!) - 'rgb32float-webgl': {render: float32_renderable, filter: float32_filterable}, + 'rgb32float-webgl': {create: 'float32-renderable-webgl'}, // rgb not renderable // 128-bit formats - 'rgba32uint': {bpp: 16, c: 4}, - 'rgba32sint': {bpp: 16, c: 4}, - 'rgba32float': {bpp: 16, c: 4, render: float32_renderable, filter: float32_filterable}, - + 'rgba32float': {}, + 'rgba32uint': {}, + 'rgba32sint': {}, + // Depth/stencil // Depth and stencil formats - stencil8: {attachment: 'stencil', c: 1, bpp: 1,components: 1, bitsPerChannel: [8, 0, 0, 0], dataType: 'uint8'}, - 'depth16unorm': {attachment: 'depth', c: 1, bpp: 2,components: 1, bitsPerChannel: [16, 0, 0, 0], dataType: 'uint16'}, - 'depth24plus': {attachment: 'depth', c: 1, bpp: 3, components: 1, bitsPerChannel: [24, 0, 0, 0], dataType: 'uint32'}, - 'depth32float': {attachment: 'depth', c: 1, bpp: 4, components: 1, bitsPerChannel: [32, 0, 0, 0], dataType: 'float32'}, + stencil8: {attachment: 'stencil', components: 1, bitsPerChannel: [8, 0, 0, 0], dataType: 'uint8'}, + 'depth16unorm': {attachment: 'depth', components: 1, bitsPerChannel: [16, 0, 0, 0], dataType: 'uint16'}, + 'depth24plus': {attachment: 'depth', components: 1, bitsPerChannel: [24, 0, 0, 0], dataType: 'uint32'}, + 'depth32float': {attachment: 'depth', components: 1, bitsPerChannel: [32, 0, 0, 0], dataType: 'float32'}, // The depth component of the "depth24plus" and "depth24plus-stencil8" formats may be implemented as either a 24-bit depth value or a "depth32float" value. - 'depth24plus-stencil8': {attachment: 'depth-stencil', bpp: 4, c: 2, p: 1, components: 2,bitsPerChannel: [24, 8, 0, 0], packed: true}, + 'depth24plus-stencil8': {attachment: 'depth-stencil', components: 2, bitsPerChannel: [24, 8, 0, 0], packed: true}, // "depth32float-stencil8" feature - 'depth32float-stencil8': {attachment: 'depth-stencil', components: 2, c: 2, bpp: 5, bitsPerChannel: [32, 8, 0, 0], packed: true}, + 'depth32float-stencil8': {attachment: 'depth-stencil', components: 2, bitsPerChannel: [32, 8, 0, 0], packed: true}, // BC compressed formats: check device.features.has("texture-compression-bc"); - 'bc1-rgb-unorm-webgl': {f: texture_compression_bc}, - 'bc1-rgb-unorm-srgb-webgl': {f: texture_compression_bc}, - - 'bc1-rgba-unorm': {f: texture_compression_bc}, - 'bc1-rgba-unorm-srgb': {f: texture_compression_bc}, - 'bc2-rgba-unorm': {f: texture_compression_bc}, - 'bc2-rgba-unorm-srgb': {f: texture_compression_bc}, - 'bc3-rgba-unorm': {f: texture_compression_bc}, - 'bc3-rgba-unorm-srgb': {f: texture_compression_bc}, - 'bc4-r-unorm': {f: texture_compression_bc}, - 'bc4-r-snorm': {f: texture_compression_bc}, - 'bc5-rg-unorm': {f: texture_compression_bc}, - 'bc5-rg-snorm': {f: texture_compression_bc}, - 'bc6h-rgb-ufloat': {f: texture_compression_bc}, - 'bc6h-rgb-float': {f: texture_compression_bc}, - 'bc7-rgba-unorm': {f: texture_compression_bc}, - 'bc7-rgba-unorm-srgb': {f: texture_compression_bc}, + 'bc1-rgb-unorm-webgl': {create: 'texture-compression-bc'}, + 'bc1-rgb-unorm-srgb-webgl': {create: 'texture-compression-bc'}, + + 'bc1-rgba-unorm': {create: 'texture-compression-bc'}, + 'bc1-rgba-unorm-srgb': {create: 'texture-compression-bc'}, + 'bc2-rgba-unorm': {create: 'texture-compression-bc'}, + 'bc2-rgba-unorm-srgb': {create: 'texture-compression-bc'}, + 'bc3-rgba-unorm': {create: 'texture-compression-bc'}, + 'bc3-rgba-unorm-srgb': {create: 'texture-compression-bc'}, + 'bc4-r-unorm': {create: 'texture-compression-bc'}, + 'bc4-r-snorm': {create: 'texture-compression-bc'}, + 'bc5-rg-unorm': {create: 'texture-compression-bc'}, + 'bc5-rg-snorm': {create: 'texture-compression-bc'}, + 'bc6h-rgb-ufloat': {create: 'texture-compression-bc'}, + 'bc6h-rgb-float': {create: 'texture-compression-bc'}, + 'bc7-rgba-unorm': {create: 'texture-compression-bc'}, + 'bc7-rgba-unorm-srgb': {create: 'texture-compression-bc'}, // WEBGL_compressed_texture_etc: device.features.has("texture-compression-etc2") // Note: Supposedly guaranteed availability compressed formats in WebGL2, but through CPU decompression - 'etc2-rgb8unorm': {f: texture_compression_etc2}, - 'etc2-rgb8unorm-srgb': {f: texture_compression_etc2}, - 'etc2-rgb8a1unorm': {f: texture_compression_etc2}, - 'etc2-rgb8a1unorm-srgb': {f: texture_compression_etc2}, - 'etc2-rgba8unorm': {f: texture_compression_etc2}, - 'etc2-rgba8unorm-srgb': {f: texture_compression_etc2}, + 'etc2-rgb8unorm': {create: 'texture-compression-etc2'}, + 'etc2-rgb8unorm-srgb': {create: 'texture-compression-etc2'}, + 'etc2-rgb8a1unorm': {create: 'texture-compression-etc2'}, + 'etc2-rgb8a1unorm-srgb': {create: 'texture-compression-etc2'}, + 'etc2-rgba8unorm': {create: 'texture-compression-etc2'}, + 'etc2-rgba8unorm-srgb': {create: 'texture-compression-etc2'}, - 'eac-r11unorm': {f: texture_compression_etc2}, - 'eac-r11snorm': {f: texture_compression_etc2}, - 'eac-rg11unorm': {f: texture_compression_etc2}, - 'eac-rg11snorm': {f: texture_compression_etc2}, + 'eac-r11unorm': {create: 'texture-compression-etc2'}, + 'eac-r11snorm': {create: 'texture-compression-etc2'}, + 'eac-rg11unorm': {create: 'texture-compression-etc2'}, + 'eac-rg11snorm': {create: 'texture-compression-etc2'}, // X_ASTC compressed formats: device.features.has("texture-compression-astc") - 'astc-4x4-unorm': {f: texture_compression_astc}, - 'astc-4x4-unorm-srgb': {f: texture_compression_astc}, - 'astc-5x4-unorm': {f: texture_compression_astc}, - 'astc-5x4-unorm-srgb': {f: texture_compression_astc}, - 'astc-5x5-unorm': {f: texture_compression_astc}, - 'astc-5x5-unorm-srgb': {f: texture_compression_astc}, - 'astc-6x5-unorm': {f: texture_compression_astc}, - 'astc-6x5-unorm-srgb': {f: texture_compression_astc}, - 'astc-6x6-unorm': {f: texture_compression_astc}, - 'astc-6x6-unorm-srgb': {f: texture_compression_astc}, - 'astc-8x5-unorm': {f: texture_compression_astc}, - 'astc-8x5-unorm-srgb': {f: texture_compression_astc}, - 'astc-8x6-unorm': {f: texture_compression_astc}, - 'astc-8x6-unorm-srgb': {f: texture_compression_astc}, - 'astc-8x8-unorm': {f: texture_compression_astc}, - 'astc-8x8-unorm-srgb': {f: texture_compression_astc}, - 'astc-10x5-unorm': {f: texture_compression_astc}, - 'astc-10x5-unorm-srgb': {f: texture_compression_astc}, - 'astc-10x6-unorm': {f: texture_compression_astc}, - 'astc-10x6-unorm-srgb': {f: texture_compression_astc}, - 'astc-10x8-unorm': {f: texture_compression_astc}, - 'astc-10x8-unorm-srgb': {f: texture_compression_astc}, - 'astc-10x10-unorm': {f: texture_compression_astc}, - 'astc-10x10-unorm-srgb': {f: texture_compression_astc}, - 'astc-12x10-unorm': {f: texture_compression_astc}, - 'astc-12x10-unorm-srgb': {f: texture_compression_astc}, - 'astc-12x12-unorm': {f: texture_compression_astc}, - 'astc-12x12-unorm-srgb': {f: texture_compression_astc}, + 'astc-4x4-unorm': {create: 'texture-compression-astc'}, + 'astc-4x4-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-5x4-unorm': {create: 'texture-compression-astc'}, + 'astc-5x4-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-5x5-unorm': {create: 'texture-compression-astc'}, + 'astc-5x5-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-6x5-unorm': {create: 'texture-compression-astc'}, + 'astc-6x5-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-6x6-unorm': {create: 'texture-compression-astc'}, + 'astc-6x6-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-8x5-unorm': {create: 'texture-compression-astc'}, + 'astc-8x5-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-8x6-unorm': {create: 'texture-compression-astc'}, + 'astc-8x6-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-8x8-unorm': {create: 'texture-compression-astc'}, + 'astc-8x8-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-10x5-unorm': {create: 'texture-compression-astc'}, + 'astc-10x5-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-10x6-unorm': {create: 'texture-compression-astc'}, + 'astc-10x6-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-10x8-unorm': {create: 'texture-compression-astc'}, + 'astc-10x8-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-10x10-unorm': {create: 'texture-compression-astc'}, + 'astc-10x10-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-12x10-unorm': {create: 'texture-compression-astc'}, + 'astc-12x10-unorm-srgb': {create: 'texture-compression-astc'}, + 'astc-12x12-unorm': {create: 'texture-compression-astc'}, + 'astc-12x12-unorm-srgb': {create: 'texture-compression-astc'}, // WEBGL_compressed_texture_pvrtc - 'pvrtc-rgb4unorm-webgl': {f: texture_compression_pvrtc_webgl}, - 'pvrtc-rgba4unorm-webgl': {f: texture_compression_pvrtc_webgl}, - 'pvrtc-rbg2unorm-webgl': {f: texture_compression_pvrtc_webgl}, - 'pvrtc-rgba2unorm-webgl': {f: texture_compression_pvrtc_webgl}, + 'pvrtc-rgb4unorm-webgl': {create: 'texture-compression-pvrtc-webgl'}, + 'pvrtc-rgba4unorm-webgl': {create: 'texture-compression-pvrtc-webgl'}, + 'pvrtc-rbg2unorm-webgl': {create: 'texture-compression-pvrtc-webgl'}, + 'pvrtc-rgba2unorm-webgl': {create: 'texture-compression-pvrtc-webgl'}, // WEBGL_compressed_texture_etc1 - 'etc1-rbg-unorm-webgl': {f: texture_compression_etc1_webgl}, + 'etc1-rbg-unorm-webgl': {create: 'texture-compression-etc1-webgl'}, // WEBGL_compressed_texture_atc - 'atc-rgb-unorm-webgl': {f: texture_compression_atc_webgl}, - 'atc-rgba-unorm-webgl': {f: texture_compression_atc_webgl}, - 'atc-rgbai-unorm-webgl': {f: texture_compression_atc_webgl} + 'atc-rgb-unorm-webgl': {create: 'texture-compression-atc-webgl'}, + 'atc-rgba-unorm-webgl': {create: 'texture-compression-atc-webgl'}, + 'atc-rgbai-unorm-webgl': {create: 'texture-compression-atc-webgl'} }; diff --git a/modules/core/src/index.ts b/modules/core/src/index.ts index 7e767d6188..a13051975b 100644 --- a/modules/core/src/index.ts +++ b/modules/core/src/index.ts @@ -9,7 +9,12 @@ export {luma} from './adapter/luma'; // ADAPTER (DEVICE AND GPU RESOURCE INTERFACES) export {Adapter} from './adapter/adapter'; -export type {DeviceProps, DeviceInfo, DeviceFeature} from './adapter/device'; +export type { + DeviceProps, + DeviceInfo, + DeviceFeature, + DeviceTextureFormatCapabilities +} from './adapter/device'; export {Device, DeviceFeatures, DeviceLimits} from './adapter/device'; export type {CanvasContextProps} from './adapter/canvas-context'; @@ -144,17 +149,21 @@ export type { ColorTextureFormat, DepthStencilTextureFormat } from './gpu-type-utils/texture-formats'; +export type {TextureFormatInfo} from './gpu-type-utils/texture-format-info'; +export type {TextureFormatCapabilities} from './gpu-type-utils/texture-format-capabilities'; // GPU TYPE UTILS - GPU MEMORY LAYOUT HELPERS - CAN BE USED BY APPS BUT MOSTLY USED INTERNALLY export {decodeVertexFormat} from './gpu-type-utils/decode-vertex-format'; -export {decodeTextureFormat} from './gpu-type-utils/decode-texture-format'; export {decodeShaderUniformType} from './gpu-type-utils/decode-shader-types'; export {decodeShaderAttributeType} from './gpu-type-utils/decode-attribute-type'; export {getDataTypeFromTypedArray} from './gpu-type-utils/vertex-format-from-attribute'; export {getTypedArrayFromDataType} from './gpu-type-utils/vertex-format-from-attribute'; export {getVertexFormatFromAttribute} from './gpu-type-utils/vertex-format-from-attribute'; +export {decodeTextureFormat} from './gpu-type-utils/decode-texture-format'; +export {getTextureFormatCapabilities} from './gpu-type-utils/texture-format-capabilities'; + // GENERAL EXPORTS - FOR APPLICATIONS export type {StatsManager} from './utils/stats-manager'; // TODO - should this be moved to probe.gl? @@ -174,3 +183,6 @@ export {getScratchArray} from './utils/array-utils-flat'; export type {AttributeInfo} from './adapter-utils/get-attribute-from-layouts'; export {BufferLayoutHelper as _BufferLayoutHelper} from './adapter-utils/buffer-layout-helper'; export {getAttributeInfosFromLayouts} from './adapter-utils/get-attribute-from-layouts'; + +// TEST EXPORTS +export {TEXTURE_FORMAT_TABLE as TEXTURE_FORMATS} from './gpu-type-utils/texture-format-table'; diff --git a/modules/core/test/adapter/resources/framebuffer.spec.ts b/modules/core/test/adapter/resources/framebuffer.spec.ts index 7cd4b73c18..f81e5a5891 100644 --- a/modules/core/test/adapter/resources/framebuffer.spec.ts +++ b/modules/core/test/adapter/resources/framebuffer.spec.ts @@ -197,7 +197,6 @@ test.skip('Framebuffer#getDefaultFramebuffer', (t) => { */ /* -import {TEXTURE_FORMATS} from '@luma.gl/webgl/texture-formats'; const RGB_TO = { [GL.UNSIGNED_BYTE]: (r, g, b) => [r * 256, g * 256, b * 256], diff --git a/modules/core/test/adapter/resources/texture.spec.ts b/modules/core/test/adapter/resources/texture.spec.ts index 6bd585cb02..619621951d 100644 --- a/modules/core/test/adapter/resources/texture.spec.ts +++ b/modules/core/test/adapter/resources/texture.spec.ts @@ -10,10 +10,7 @@ import {Device, Texture, TextureFormat, decodeTextureFormat, VertexType} from '@ import {GL} from '@luma.gl/constants'; import {WebGLDevice} from '@luma.gl/webgl'; -import { - TEXTURE_FORMATS, - getTextureFormatWebGL -} from '../../../../webgl/src/adapter/converters/texture-formats'; +import {TEXTURE_FORMATS, getTextureFormatWebGL} from '@luma.gl/core'; import {SAMPLER_PARAMETERS} from './sampler.spec'; import {WEBGLTexture} from '@luma.gl/webgl/adapter/resources/webgl-texture'; @@ -146,19 +143,6 @@ test('Texture#format simple creation', async t => { }, `Texture(${device.type},${formatName}) creation OK`); t.equals(texture.format, formatName, `Texture(${device.type},${formatName}).format OK`); - if (device.type === 'webgl') { - // const formatInfo = getTextureFormatWebGL(forma) - const expectedInternalFormat = formatInfo.gl; - t.equals( - texture.glInternalFormat, - expectedInternalFormat, - `Texture(${device.type},${formatName}).glInternal OK` - ); - // const expectedType = formatInfo.types?.[0]; - // const expectedDataFormat = formatInfo.dataFormat; - // t.equals(texture.glType, expectedType, `Texture(${formatName}).type OK`); - // t.equals(texture.glFormat, expectedDataFormat, `Texture(${formatName}).glFormat OK`); - } texture.destroy(); } } @@ -201,18 +185,18 @@ function testFormatCreation(t, device: Device, withData: boolean = false) { // WebGPU texture can currently only be set from 8 bit data const notImplemented = device.type === 'webgpu' && bitsPerChannel !== 8; - console.log(formatName, bitsPerChannel); - + // console.log(formatName, bitsPerChannel); if (['stencil8'].includes(formatName) || notImplemented) { continue; } - if (device.isTextureFormatSupported(format) && !device.isTextureFormatCompressed(format)) { + const canGenerateMipmaps = format === 'rgba8unorm'; + // device.isTextureFormatSupported(format) && !device.isTextureFormatCompressed(format); + if (canGenerateMipmaps) { try { const data = withData && !packed ? TEXTURE_DATA[dataType] || DEFAULT_TEXTURE_DATA : null; - // TODO: for some reason mipmap generation failing for RGB32F format - const mipmaps = format === 'rgba8unorm'; - // device.isTextureFormatRenderable(format) && device.isTextureFormatFilterable(format); + const capabilities = device.getTextureFormatCapabilities(format); + const mipmaps = capabilities.render && capabilities.filter; const sampler = mipmaps ? { @@ -241,7 +225,7 @@ function testFormatCreation(t, device: Device, withData: boolean = false) { t.comment(`Texture(${device.type},${format}) creation FAILED ${error}`); } } else { - t.comment(`Texture(${device.type},${format}) not supported in ${device.type}`); + t.comment(`Texture(${device.type},${format}) not supported`); } } } diff --git a/modules/core/test/gpu-type-utils/decode-texture-format.spec.ts b/modules/core/test/gpu-type-utils/decode-texture-format.spec.ts index 773584d47a..cef46fd197 100644 --- a/modules/core/test/gpu-type-utils/decode-texture-format.spec.ts +++ b/modules/core/test/gpu-type-utils/decode-texture-format.spec.ts @@ -8,24 +8,25 @@ import {decodeTextureFormat, TextureFormat} from '@luma.gl/core'; // prettier-ignore const TEST_CASES: {format: TextureFormat, result: any}[] = [ // 8-bit formats - {format: 'r8unorm', result: {channels: 'r', components: 1, dataType: 'uint8', bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, integer: false, signed: false, normalized: true}}, - {format: 'r8snorm', result: {channels: 'r', components: 1, dataType: 'sint8', bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, integer: false, signed: true, normalized: true}}, - {format: 'r8uint', result: {channels: 'r', components: 1, dataType: 'uint8', bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, integer: true, signed: false, normalized: false}}, - {format: 'r8sint', result: {channels: 'r', components: 1, dataType: 'sint8', bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, integer: true, signed: true, normalized: false}}, + {format: 'r8unorm', result: {attachment: 'color', dataType: 'uint8', components: 1, channels: 'r', integer: false, signed: false, normalized: true, bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, packed: false, srgb: false}}, + {format: 'r8snorm', result: {attachment: 'color', dataType: 'sint8', components: 1, channels: 'r', integer: false, signed: true, normalized: true, bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, packed: false, srgb: false}}, + {format: 'r8uint', result: {attachment: 'color', dataType: 'uint8', components: 1, channels: 'r', integer: true, signed: false, normalized: false, bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, packed: false, srgb: false}}, + {format: 'r8sint', result: {attachment: 'color', dataType: 'sint8', components: 1, channels: 'r', integer: true, signed: true, normalized: false, bitsPerChannel: [8, 0, 0, 0], bytesPerPixel: 1, packed: false, srgb: false}}, // 16-bit formats - {format: 'r16uint', result: {channels: 'r', components: 1, dataType: 'uint16', bitsPerChannel: [16, 0, 0, 0], bytesPerPixel: 2, integer: true, signed: false, normalized: false}}, - {format: 'r16sint', result: {channels: 'r', components: 1, dataType: 'sint16', bitsPerChannel: [16, 0, 0, 0], bytesPerPixel: 2, integer: true, signed: true, normalized: false}}, - {format: 'r16float', result: {channels: 'r', components: 1, dataType: 'float16', bitsPerChannel: [16, 0, 0, 0], bytesPerPixel: 2, integer: false, signed: false, normalized: false }} + {format: 'r16uint', result: {attachment: 'color', dataType: 'uint16', components: 1, channels: 'r', integer: true, signed: false, normalized: false, bitsPerChannel: [16, 0, 0, 0], bytesPerPixel: 2, packed: false, srgb: false}}, + {format: 'r16sint', result: {attachment: 'color', dataType: 'sint16', components: 1, channels: 'r', integer: true, signed: true, normalized: false, bitsPerChannel: [16, 0, 0, 0], bytesPerPixel: 2, packed: false, srgb: false}}, + {format: 'r16float', result: {attachment: 'color', dataType: 'float16', components: 1, channels: 'r', integer: false, signed: false, normalized: false, bitsPerChannel: [16, 0, 0, 0], bytesPerPixel: 2, packed: false, srgb: false }} ]; test('shadertype#decodeTextureFormat', t => { for (const tc of TEST_CASES) { const decoded = decodeTextureFormat(tc.format); + t.deepEqual( decoded, {format: tc.format, ...tc.result}, - `decodeVertexFormat('${tc.format}') => ${JSON.stringify(decoded.dataType)}` + `decodeTextureFormat('${tc.format}') => ${JSON.stringify(decoded.dataType)}` ); } t.end(); diff --git a/modules/core/test/gpu-type-utils/get-texture-format-capabilities.spec.ts b/modules/core/test/gpu-type-utils/get-texture-format-capabilities.spec.ts new file mode 100644 index 0000000000..a5fcd8ab07 --- /dev/null +++ b/modules/core/test/gpu-type-utils/get-texture-format-capabilities.spec.ts @@ -0,0 +1,32 @@ +// luma.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import { + getTextureFormatCapabilities, + TextureFormat, + TextureFormatCapabilities +} from '@luma.gl/core'; + +// prettier-ignore +const TEST_CASES: {format: TextureFormat, result: Omit}[] = [ + // 8-bit formats + {format: 'r8unorm', result: {create: true, render: true, filter: true, blend: true, store: true}}, + + // 16-bit formats + {format: 'r16uint', result: {create: true, render: true, filter: false, blend: true, store: true}}, +]; + +test('shadertype#getTextureFormatCapabilities', t => { + for (const tc of TEST_CASES) { + const decoded = getTextureFormatCapabilities(tc.format); + + t.deepEqual( + decoded, + {format: tc.format, ...tc.result}, + `decodeVertexFormat('${tc.format}') => ${JSON.stringify(decoded)}` + ); + } + t.end(); +}); diff --git a/modules/core/test/index.ts b/modules/core/test/index.ts index 8d5ef983e3..a3939afc8d 100644 --- a/modules/core/test/index.ts +++ b/modules/core/test/index.ts @@ -4,9 +4,11 @@ import './utils/uid.spec'; // type utils import './gpu-type-utils/decode-attribute-type.spec'; import './gpu-type-utils/decode-vertex-format.spec'; -import './gpu-type-utils/decode-texture-format.spec'; import './gpu-type-utils/vertex-format-from-attribute.spec'; +import './gpu-type-utils/decode-texture-format.spec'; +import './gpu-type-utils/get-texture-format-capabilities.spec'; + // adapter utils import './adapter-utils/get-attribute-from-layout.spec'; import './adapter-utils/is-uniform-value.spec'; diff --git a/modules/test-utils/src/null-device/null-device.ts b/modules/test-utils/src/null-device/null-device.ts index d5a280783d..8c54b4500a 100644 --- a/modules/test-utils/src/null-device/null-device.ts +++ b/modules/test-utils/src/null-device/null-device.ts @@ -5,7 +5,6 @@ import type { DeviceProps, CanvasContextProps, - TextureFormat, VertexArray, VertexArrayProps, BufferProps, @@ -65,7 +64,7 @@ export class NullDevice extends Device { /** * Destroys the context - * @note Has no effect for WebGL browser contexts, there is no browser API for destroying contexts + * @note Has no effect for null contexts */ destroy(): void {} @@ -73,18 +72,6 @@ export class NullDevice extends Device { return false; } - isTextureFormatSupported(format: TextureFormat): boolean { - return true; - } - - isTextureFormatFilterable(format: TextureFormat): boolean { - return true; - } - - isTextureFormatRenderable(format: TextureFormat): boolean { - return true; - } - // IMPLEMENTATION OF ABSTRACT DEVICE createCanvasContext(props: CanvasContextProps): NullCanvasContext { @@ -173,4 +160,8 @@ export class NullDevice extends Device { } return value; } + + override _getDeviceSpecificTextureFormatCapabilities(format: any): any { + return format; + } } diff --git a/modules/webgl/src/adapter/converters/texture-formats.ts b/modules/webgl/src/adapter/converters/texture-formats.ts deleted file mode 100644 index 6ecfe55b22..0000000000 --- a/modules/webgl/src/adapter/converters/texture-formats.ts +++ /dev/null @@ -1,651 +0,0 @@ -// luma.gl -// SPDX-License-Identifier: MIT -// Copyright (c) vis.gl contributors - -import type {TextureFormat, DeviceFeature} from '@luma.gl/core'; -import {decodeTextureFormat} from '@luma.gl/core'; -import {GL, GLPixelType, GLExtensions, GLTexelDataFormat} from '@luma.gl/constants'; -import {getWebGLExtension} from '../../context/helpers/webgl-extensions'; -import {getGLFromVertexType} from './vertex-formats'; - -/* eslint-disable camelcase */ - -// TEXTURE FEATURES - -// Define local device feature strings to optimize minification -const texture_compression_bc: DeviceFeature = 'texture-compression-bc'; -const texture_compression_astc: DeviceFeature = 'texture-compression-astc'; -const texture_compression_etc2: DeviceFeature = 'texture-compression-etc2'; -const texture_compression_etc1_webgl: DeviceFeature = 'texture-compression-etc1-webgl'; -const texture_compression_pvrtc_webgl: DeviceFeature = 'texture-compression-pvrtc-webgl'; -const texture_compression_atc_webgl: DeviceFeature = 'texture-compression-atc-webgl'; - -const float32_renderable: DeviceFeature = 'float32-renderable-webgl'; -const float16_renderable: DeviceFeature = 'float16-renderable-webgl'; -const rgb9e5ufloat_renderable: DeviceFeature = 'rgb9e5ufloat_renderable-webgl'; -const snorm8_renderable: DeviceFeature = 'snorm8-renderable-webgl'; -const norm16_renderable: DeviceFeature = 'norm16-renderable-webgl'; -const snorm16_renderable: DeviceFeature = 'snorm16-renderable-webgl'; - -const float32_filterable: DeviceFeature = 'float32-filterable'; -const float16_filterable: DeviceFeature = 'float16-filterable-webgl'; - -// Define local webgl extension strings to optimize minification -const X_S3TC = 'WEBGL_compressed_texture_s3tc'; // BC1, BC2, BC3 -const X_S3TC_SRGB = 'WEBGL_compressed_texture_s3tc_srgb'; // BC1, BC2, BC3 -const X_RGTC = 'EXT_texture_compression_rgtc'; // BC4, BC5 -const X_BPTC = 'EXT_texture_compression_bptc'; // BC6, BC7 -const X_ETC2 = 'WEBGL_compressed_texture_etc'; // Renamed from 'WEBGL_compressed_texture_es3' -const X_ASTC = 'WEBGL_compressed_texture_astc'; -const X_ETC1 = 'WEBGL_compressed_texture_etc1'; -const X_PVRTC = 'WEBGL_compressed_texture_pvrtc'; -const X_ATC = 'WEBGL_compressed_texture_atc'; - -// Define local webgl extension strings to optimize minification -const EXT_texture_norm16 = 'EXT_texture_norm16'; -const EXT_render_snorm = 'EXT_render_snorm'; -const EXT_color_buffer_float = 'EXT_color_buffer_float'; - -// prettier-ignore -export const TEXTURE_FEATURES: Partial> = { - 'float32-renderable-webgl': ['EXT_color_buffer_float'], - 'float16-renderable-webgl': ['EXT_color_buffer_half_float'], - 'rgb9e5ufloat_renderable-webgl': ['WEBGL_render_shared_exponent'], - 'snorm8-renderable-webgl': [EXT_render_snorm], - 'norm16-renderable-webgl': [EXT_texture_norm16], - 'snorm16-renderable-webgl': [EXT_texture_norm16, EXT_render_snorm], - - 'float32-filterable': ['OES_texture_float_linear'], - 'float16-filterable-webgl': ['OES_texture_half_float_linear'], - 'texture-filterable-anisotropic-webgl': ['EXT_texture_filter_anisotropic'], - - 'texture-blend-float-webgl': ['EXT_float_blend'], - - 'texture-compression-bc': [X_S3TC, X_S3TC_SRGB, X_RGTC, X_BPTC], - // 'texture-compression-bc3-srgb-webgl': [X_S3TC_SRGB], - // 'texture-compression-bc3-webgl': [X_S3TC], - 'texture-compression-bc5-webgl': [X_RGTC], - 'texture-compression-bc7-webgl': [X_BPTC], - 'texture-compression-etc2': [X_ETC2], - 'texture-compression-astc': [X_ASTC], - 'texture-compression-etc1-webgl': [X_ETC1], - 'texture-compression-pvrtc-webgl': [X_PVRTC], - 'texture-compression-atc-webgl': [X_ATC] -}; - -export function isTextureFeature(feature: DeviceFeature): boolean { - return feature in TEXTURE_FEATURES; -} - -/** Checks a texture feature (for Device.features). Mainly compressed texture support */ -export function checkTextureFeature( - gl: WebGL2RenderingContext, - feature: DeviceFeature, - extensions: GLExtensions -): boolean { - const textureExtensions = TEXTURE_FEATURES[feature] || []; - return textureExtensions.every(extension => getWebGLExtension(gl, extension, extensions)); -} - -// TEXTURE FORMATS - -/** Map a format to webgl and constants */ -type Format = { - gl?: GL; - /** format requires WebGL2, when using a WebGL 1 context, color renderbuffer formats are limited */ - gl2ext?: string; - - /** (bytes per pixel), for memory usage calculations. */ - b?: number; - /** channels */ - c?: number; - bpp?: number; - /** packed */ - p?: number; - /** compressed */ - x?: string; - /** for compressed texture formats */ - f?: DeviceFeature; - /** renderable if feature is present. false means the spec does not support this format */ - render?: DeviceFeature | false; - /** filterable if feature is present. false means the spec does not support this format */ - filter?: DeviceFeature | false; - - /** If not supported on WebGPU */ - wgpu?: false; - - types?: GLPixelType[]; - dataFormat?: GLTexelDataFormat; - /** Depth and stencil format attachment points. If set, needs to be a Renderbuffer unless depthTexture is set */ - attachment?: GL.DEPTH_ATTACHMENT | GL.STENCIL_ATTACHMENT | GL.DEPTH_STENCIL_ATTACHMENT; - /** if depthTexture is set this is a depth/stencil format that can be set to a texture */ - depthTexture?: boolean; - /** @deprecated can this format be used with renderbuffers */ - rb?: boolean; -}; - -// TABLES - -/** - * Texture format data - - * Exported but can change without notice - */ -// prettier-ignore -export const TEXTURE_FORMATS: Record = { - // 8-bit formats - 'r8unorm': {gl: GL.R8, b: 1, c: 1, rb: true}, - 'r8snorm': {gl: GL.R8_SNORM, b: 1, c: 1, render: snorm8_renderable}, - 'r8uint': {gl: GL.R8UI, b: 1, c: 1, rb: true}, - 'r8sint': {gl: GL.R8I, b: 1, c: 1, rb: true}, - - // 16-bit formats - 'rg8unorm': {gl: GL.RG8, b: 2, c: 2, rb: true}, - 'rg8snorm': {gl: GL.RG8_SNORM, b: 2, c: 2, render: snorm8_renderable}, - 'rg8uint': {gl: GL.RG8UI, b: 2, c: 2, rb: true}, - 'rg8sint': {gl: GL.RG8I, b: 2, c: 2, rb: true}, - - 'r16uint': {gl: GL.R16UI, b: 2, c: 1, rb: true}, - 'r16sint': {gl: GL.R16I, b: 2, c: 1, rb: true}, - 'r16float': {gl: GL.R16F, b: 2, c: 1, render: float16_renderable, filter: 'float16-filterable-webgl', rb: true}, - 'r16unorm-webgl': {gl: GL.R16_EXT, b:2, c:1, f: norm16_renderable, rb: true}, - 'r16snorm-webgl': {gl: GL.R16_SNORM_EXT, b:2, c:1, f: snorm16_renderable}, - - // Packed 16-bit formats - 'rgba4unorm-webgl': {gl: GL.RGBA4, b: 2, c: 4, wgpu: false, rb: true}, - 'rgb565unorm-webgl': {gl: GL.RGB565, b: 2, c: 4, wgpu: false, rb: true}, - 'rgb5a1unorm-webgl': {gl: GL.RGB5_A1, b: 2, c: 4, wgpu: false, rb: true}, - - // 24-bit formats - 'rgb8unorm-webgl': {gl: GL.RGB8, b: 3, c: 3, wgpu: false}, - 'rgb8snorm-webgl': {gl: GL.RGB8_SNORM, b: 3, c: 3, wgpu: false}, - - // 32-bit formats - 'rgba8unorm': {gl: GL.RGBA8, b: 4, c: 2, bpp: 4}, - 'rgba8unorm-srgb': {gl: GL.SRGB8_ALPHA8, b: 4, c: 4, bpp: 4}, - 'rgba8snorm': {gl: GL.RGBA8_SNORM, b: 4, c: 4, render: snorm8_renderable}, - 'rgba8uint': {gl: GL.RGBA8UI, b: 4, c: 4, bpp: 4}, - 'rgba8sint': {gl: GL.RGBA8I, b: 4, c: 4, bpp: 4}, - // reverse colors, webgpu only - 'bgra8unorm': {b: 4, c: 4}, - 'bgra8unorm-srgb': {b: 4, c: 4}, - - 'rg16uint': {gl: GL.RG16UI, b: 4, c: 1, bpp: 4}, - 'rg16sint': {gl: GL.RG16I, b: 4, c: 2, bpp: 4}, - 'rg16float': {gl: GL.RG16F, bpp: 4, b: 4, c: 2, render: float16_renderable, filter: float16_filterable, rb: true}, - 'rg16unorm-webgl': {gl: GL.RG16_EXT, b:2, c:2, render: norm16_renderable}, - 'rg16snorm-webgl': {gl: GL.RG16_SNORM_EXT, b:2, c:2, render: snorm16_renderable}, - - 'r32uint': {gl: GL.R32UI, b: 4, c: 1, bpp: 4, rb: true}, - 'r32sint': {gl: GL.R32I, b: 4, c: 1, bpp: 4, rb: true}, - 'r32float': {gl: GL.R32F, bpp: 4, b: 4, c: 1, render: float32_renderable, filter: float32_filterable}, - - // Packed 32-bit formats - 'rgb9e5ufloat': {gl: GL.RGB9_E5, b: 4, c: 3, p: 1, render: rgb9e5ufloat_renderable}, // , filter: true}, - 'rg11b10ufloat': {gl: GL.R11F_G11F_B10F, b: 4, c: 3, p: 1,render: float32_renderable, rb: true}, - 'rgb10a2unorm': {gl: GL.RGB10_A2, b: 4, c: 4, p: 1, rb: true}, - 'rgb10a2uint-webgl': {b: 4, c: 4, gl: GL.RGB10_A2UI, p: 1, wgpu: false, bpp: 4, rb: true}, - - // 48-bit formats - 'rgb16unorm-webgl': {gl: GL.RGB16_EXT, b:2, c:3, f: norm16_renderable}, // rgb not renderable - 'rgb16snorm-webgl': {gl: GL.RGB16_SNORM_EXT, b:2, c:3, f: norm16_renderable}, // rgb not renderable - - // 64-bit formats - 'rg32uint': {gl: GL.RG32UI, b: 8, c: 2, rb: true}, - 'rg32sint': {gl: GL.RG32I, b: 8, c: 2, rb: true}, - 'rg32float': {gl: GL.RG32F, b: 8, c: 2, render: false, filter: float32_filterable, rb: true}, - 'rgba16uint': {gl: GL.RGBA16UI, b: 8, c: 4, rb: true}, - 'rgba16sint': {gl: GL.RGBA16I, b: 8, c: 4, rb: true}, - 'rgba16float': {gl: GL.RGBA16F, b: 8, c: 4, render: float16_renderable, filter: float16_filterable}, - 'rgba16unorm-webgl': {gl: GL.RGBA16_EXT, b:2, c:4, render: norm16_renderable, rb: true}, - 'rgba16snorm-webgl': {gl: GL.RGBA16_SNORM_EXT, b:2, c:4, render: snorm16_renderable}, - - // 96-bit formats (deprecated!) - 'rgb32float-webgl': {gl: GL.RGB32F, render: float32_renderable, filter: float32_filterable, - gl2ext: EXT_color_buffer_float, dataFormat: GL.RGB, types: [GL.FLOAT]}, - - // 128-bit formats - 'rgba32uint': {gl: GL.RGBA32UI, b: 16, c: 4, rb: true}, - 'rgba32sint': {gl: GL.RGBA32I, b: 16, c: 4, rb: true}, - 'rgba32float': {gl: GL.RGBA32F, b: 16, c: 4, render: float32_renderable, filter: float32_filterable, rb: true}, - - // Depth and stencil formats - 'stencil8': {gl: GL.STENCIL_INDEX8, b: 1, c: 1, attachment: GL.STENCIL_ATTACHMENT, rb: true}, // 8 stencil bits - - 'depth16unorm': {gl: GL.DEPTH_COMPONENT16, b: 2, c: 1, attachment: GL.DEPTH_ATTACHMENT, - dataFormat: GL.DEPTH_COMPONENT, types: [GL.UNSIGNED_SHORT], rb: true}, // 16 depth bits - 'depth24plus': {gl: GL.DEPTH_COMPONENT24, b: 3, c: 1, attachment: GL.DEPTH_ATTACHMENT, - dataFormat: GL.DEPTH_COMPONENT, types: [GL.UNSIGNED_INT]}, - 'depth32float': {gl: GL.DEPTH_COMPONENT32F, b: 4, c: 1, attachment: GL.DEPTH_ATTACHMENT, - dataFormat: GL.DEPTH_COMPONENT, types: [GL.FLOAT], rb: true}, - - // The depth component of the "depth24plus" and "depth24plus-stencil8" formats may be implemented as either a 24-bit depth value or a "depth32float" value. - 'depth24plus-stencil8': {gl: GL.DEPTH24_STENCIL8, b: 4, c: 2, p: 1, attachment: GL.DEPTH_STENCIL_ATTACHMENT, rb: true, depthTexture: true, - dataFormat: GL.DEPTH_STENCIL, types: [GL.UNSIGNED_INT_24_8]}, - // "depth32float-stencil8" feature - TODO below is render buffer only? - 'depth32float-stencil8': {gl: GL.DEPTH32F_STENCIL8, b: 5, c: 2, p: 1, attachment: GL.DEPTH_STENCIL_ATTACHMENT, - dataFormat: GL.DEPTH_STENCIL, types: [GL.FLOAT_32_UNSIGNED_INT_24_8_REV], rb: true}, - - // BC compressed formats: check device.features.has("texture-compression-bc"); - - 'bc1-rgb-unorm-webgl': {gl: GL.COMPRESSED_RGB_S3TC_DXT1_EXT, x: X_S3TC, f: texture_compression_bc}, - 'bc1-rgb-unorm-srgb-webgl': {gl: GL.COMPRESSED_SRGB_S3TC_DXT1_EXT, x: X_S3TC_SRGB, f: texture_compression_bc}, - - 'bc1-rgba-unorm': {gl: GL.COMPRESSED_RGBA_S3TC_DXT1_EXT, x: X_S3TC, f: texture_compression_bc}, - 'bc1-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_S3TC_DXT1_EXT, x: X_S3TC_SRGB, f: texture_compression_bc}, - 'bc2-rgba-unorm': {gl: GL.COMPRESSED_RGBA_S3TC_DXT3_EXT, x: X_S3TC, f: texture_compression_bc}, - 'bc2-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, x: X_S3TC_SRGB, f: texture_compression_bc}, - 'bc3-rgba-unorm': {gl: GL.COMPRESSED_RGBA_S3TC_DXT5_EXT, x: X_S3TC, f: texture_compression_bc}, - 'bc3-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, x: X_S3TC_SRGB, f: texture_compression_bc}, - 'bc4-r-unorm': {gl: GL.COMPRESSED_RED_RGTC1_EXT, x: X_RGTC, f: texture_compression_bc}, - 'bc4-r-snorm': {gl: GL.COMPRESSED_SIGNED_RED_RGTC1_EXT, x: X_RGTC, f: texture_compression_bc}, - 'bc5-rg-unorm': {gl: GL.COMPRESSED_RED_GREEN_RGTC2_EXT, x: X_RGTC, f: texture_compression_bc}, - 'bc5-rg-snorm': {gl: GL.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, x: X_RGTC, f: texture_compression_bc}, - 'bc6h-rgb-ufloat': {gl: GL.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, x: X_BPTC, f: texture_compression_bc}, - 'bc6h-rgb-float': {gl: GL.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, x: X_BPTC, f: texture_compression_bc}, - 'bc7-rgba-unorm': {gl: GL.COMPRESSED_RGBA_BPTC_UNORM_EXT, x: X_BPTC, f: texture_compression_bc}, - 'bc7-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT, x: X_BPTC, f: texture_compression_bc}, - - // WEBGL_compressed_texture_etc: device.features.has("texture-compression-etc2") - // Note: Supposedly guaranteed availability compressed formats in WebGL2, but through CPU decompression - - 'etc2-rgb8unorm': {gl: GL.COMPRESSED_RGB8_ETC2, f: texture_compression_etc2}, - 'etc2-rgb8unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ETC2, f: texture_compression_etc2}, - 'etc2-rgb8a1unorm': {gl: GL.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, f: texture_compression_etc2}, - 'etc2-rgb8a1unorm-srgb': {gl: GL.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, f: texture_compression_etc2}, - 'etc2-rgba8unorm': {gl: GL.COMPRESSED_RGBA8_ETC2_EAC, f: texture_compression_etc2}, - 'etc2-rgba8unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, f: texture_compression_etc2}, - - 'eac-r11unorm': {gl: GL.COMPRESSED_R11_EAC, f: texture_compression_etc2}, - 'eac-r11snorm': {gl: GL.COMPRESSED_SIGNED_R11_EAC, f: texture_compression_etc2}, - 'eac-rg11unorm': {gl: GL.COMPRESSED_RG11_EAC, f: texture_compression_etc2}, - 'eac-rg11snorm': {gl: GL.COMPRESSED_SIGNED_RG11_EAC, f: texture_compression_etc2}, - - // X_ASTC compressed formats: device.features.has("texture-compression-astc") - - 'astc-4x4-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_4x4_KHR, f: texture_compression_astc}, - 'astc-4x4-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, f: texture_compression_astc}, - 'astc-5x4-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_5x4_KHR, f: texture_compression_astc}, - 'astc-5x4-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, f: texture_compression_astc}, - 'astc-5x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_5x5_KHR, f: texture_compression_astc}, - 'astc-5x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, f: texture_compression_astc}, - 'astc-6x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_6x5_KHR, f: texture_compression_astc}, - 'astc-6x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, f: texture_compression_astc}, - 'astc-6x6-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_6x6_KHR, f: texture_compression_astc}, - 'astc-6x6-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, f: texture_compression_astc}, - 'astc-8x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_8x5_KHR, f: texture_compression_astc}, - 'astc-8x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, f: texture_compression_astc}, - 'astc-8x6-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_8x6_KHR, f: texture_compression_astc}, - 'astc-8x6-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, f: texture_compression_astc}, - 'astc-8x8-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_8x8_KHR, f: texture_compression_astc}, - 'astc-8x8-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, f: texture_compression_astc}, - 'astc-10x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x10_KHR, f: texture_compression_astc}, - 'astc-10x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, f: texture_compression_astc}, - 'astc-10x6-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x6_KHR, f: texture_compression_astc}, - 'astc-10x6-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, f: texture_compression_astc}, - 'astc-10x8-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x8_KHR, f: texture_compression_astc}, - 'astc-10x8-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, f: texture_compression_astc}, - 'astc-10x10-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x10_KHR, f: texture_compression_astc}, - 'astc-10x10-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, f: texture_compression_astc}, - 'astc-12x10-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_12x10_KHR, f: texture_compression_astc}, - 'astc-12x10-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, f: texture_compression_astc}, - 'astc-12x12-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_12x12_KHR, f: texture_compression_astc}, - 'astc-12x12-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, f: texture_compression_astc}, - - // WEBGL_compressed_texture_pvrtc - - 'pvrtc-rgb4unorm-webgl': {gl: GL.COMPRESSED_RGB_PVRTC_4BPPV1_IMG, f: texture_compression_pvrtc_webgl}, - 'pvrtc-rgba4unorm-webgl': {gl: GL.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, f: texture_compression_pvrtc_webgl}, - 'pvrtc-rbg2unorm-webgl': {gl: GL.COMPRESSED_RGB_PVRTC_2BPPV1_IMG, f: texture_compression_pvrtc_webgl}, - 'pvrtc-rgba2unorm-webgl': {gl: GL.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, f: texture_compression_pvrtc_webgl}, - - // WEBGL_compressed_texture_etc1 - - 'etc1-rbg-unorm-webgl': {gl: GL.COMPRESSED_RGB_ETC1_WEBGL, f: texture_compression_etc1_webgl}, - - // WEBGL_compressed_texture_atc - - 'atc-rgb-unorm-webgl': {gl: GL.COMPRESSED_RGB_ATC_WEBGL, f: texture_compression_atc_webgl}, - 'atc-rgba-unorm-webgl': {gl: GL.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL, f: texture_compression_atc_webgl}, - 'atc-rgbai-unorm-webgl': {gl: GL.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL, f: texture_compression_atc_webgl} -}; - -/** @deprecated should be removed */ -const DATA_FORMAT_CHANNELS = { - [GL.RED]: 1, - [GL.RED_INTEGER]: 1, - [GL.RG]: 2, - [GL.RG_INTEGER]: 2, - [GL.RGB]: 3, - [GL.RGB_INTEGER]: 3, - [GL.RGBA]: 4, - [GL.RGBA_INTEGER]: 4, - [GL.DEPTH_COMPONENT]: 1, - [GL.DEPTH_STENCIL]: 1, - [GL.ALPHA]: 1, - [GL.LUMINANCE]: 1, - [GL.LUMINANCE_ALPHA]: 2 -}; - -/** @deprecated should be removed */ -const TYPE_SIZES = { - [GL.FLOAT]: 4, - [GL.UNSIGNED_INT]: 4, - [GL.INT]: 4, - [GL.UNSIGNED_SHORT]: 2, - [GL.SHORT]: 2, - [GL.HALF_FLOAT]: 2, - [GL.BYTE]: 1, - [GL.UNSIGNED_BYTE]: 1 -}; - -// FUNCTIONS - -/** Checks if a texture format is supported */ -export function isTextureFormatSupported( - gl: WebGL2RenderingContext, - format: TextureFormat, - extensions: GLExtensions -): boolean { - const info = TEXTURE_FORMATS[format]; - if (!info) { - return false; - } - // Check that we have a GL constant - if (info.gl === undefined) { - return false; - } - const feature = info.f; - if (feature) { - return checkTextureFeature(gl, feature, extensions); - } - // Check extensions - const extension = info.x || info.gl2ext; - if (extension) { - return Boolean(getWebGLExtension(gl, extension, extensions)); - } - return true; -} - -/** Checks whether linear filtering (interpolated sampling) is available for floating point textures */ -export function isTextureFormatFilterable( - gl: WebGL2RenderingContext, - format: TextureFormat, - extensions: GLExtensions -): boolean { - return getTextureFormatSupportWebGL(gl, format, extensions).filterable || false; -} - -export function isTextureFormatRenderable( - gl: WebGL2RenderingContext, - format: TextureFormat, - extensions: GLExtensions -): boolean { - return getTextureFormatSupportWebGL(gl, format, extensions).renderable || false; -} - -/** Checks if a texture format is supported, renderable, filterable etc */ -export function getTextureFormatSupportWebGL( - gl: WebGL2RenderingContext, - format: TextureFormat, - extensions: GLExtensions -): { - supported: boolean; - filterable?: boolean; - renderable?: boolean; - blendable?: boolean; - storable?: boolean; -} { - const formatInto = decodeTextureFormat(format); - if (!formatInto) { - return {supported: false}; - } - - const webglFormatInfo = TEXTURE_FORMATS[format]; - if (!webglFormatInfo) { - return {supported: false}; - } - - // Support Check that we have a GL constant - let supported = webglFormatInfo.gl !== undefined; - supported = supported && checkTextureFeature(gl, webglFormatInfo.f, extensions); - - const isDepthStencil = format.startsWith('depth') || format.startsWith('stencil'); - const isSigned = formatInto?.signed; - - const renderable = - supported && - !isSigned && - webglFormatInfo.render && - checkTextureFeature(gl, webglFormatInfo.render, extensions); - - const filterable = - supported && - !isDepthStencil && - !isSigned && - webglFormatInfo.filter && - checkTextureFeature(gl, webglFormatInfo.filter, extensions); - - // if (format.endsWith('32float')) { - // return Boolean(getWebGLExtension(gl, 'OES_texture_float_linear, extensions', extensions)); - // } - // if (format.endsWith('16float')) { - // return Boolean(getWebGLExtension(gl, 'OES_texture_half_float_linear, extensions', extensions)); - // } - - return { - supported, - renderable, - filterable, - blendable: false, // TODO, - storable: false // TODO - }; -} - -/** Get parameters necessary to work with format in WebGL: internalFormat, dataFormat, type, compressed, */ -export function getTextureFormatWebGL(format: TextureFormat): { - internalFormat: GL; - format: GLTexelDataFormat; - type: GLPixelType; - compressed: boolean; -} { - const formatData = TEXTURE_FORMATS[format]; - const webglFormat = convertTextureFormatToGL(format); - const decoded = decodeTextureFormat(format); - return { - internalFormat: webglFormat, - format: - formatData?.dataFormat || - getWebGLPixelDataFormat(decoded.channels, decoded.integer, decoded.normalized, webglFormat), - // depth formats don't have a type - type: decoded.dataType - ? getGLFromVertexType(decoded.dataType) - : formatData?.types?.[0] || GL.UNSIGNED_BYTE, - compressed: decoded.compressed || false - }; -} - -export function getDepthStencilAttachmentWebGL( - format: TextureFormat -): GL.DEPTH_ATTACHMENT | GL.STENCIL_ATTACHMENT | GL.DEPTH_STENCIL_ATTACHMENT { - const info = TEXTURE_FORMATS[format]; - if (!info?.attachment) { - throw new Error(`${format} is not a depth stencil format`); - } - return info.attachment; -} - -/** TODO - VERY roundabout legacy way of calculating bytes per pixel */ -export function getTextureFormatBytesPerPixel(format: TextureFormat): number { - // TODO remove webgl1 support - const params = getTextureFormatWebGL(format); - // NOTE(Tarek): Default to RGBA bytes - const channels = DATA_FORMAT_CHANNELS[params.format] || 4; - const channelSize = TYPE_SIZES[params.type] || 1; - return channels * channelSize; -} - -// DATA TYPE HELPERS - -export function getWebGLPixelDataFormat( - channels: 'r' | 'rg' | 'rgb' | 'rgba' | 'bgra', - integer: boolean, - normalized: boolean, - format: GL -): GLTexelDataFormat { - // WebGL1 formats use same internalFormat - if (format === GL.RGBA || format === GL.RGB) { - return format; - } - // prettier-ignore - switch (channels) { - case 'r': return integer && !normalized ? GL.RED_INTEGER : GL.RED; - case 'rg': return integer && !normalized ? GL.RG_INTEGER : GL.RG; - case 'rgb': return integer && !normalized ? GL.RGB_INTEGER : GL.RGB; - case 'rgba': return integer && !normalized ? GL.RGBA_INTEGER : GL.RGBA; - case 'bgra': throw new Error('bgra pixels not supported by WebGL'); - default: return GL.RGBA; - } -} - -/** - * Map WebGPU style texture format strings to GL constants - */ -function convertTextureFormatToGL(format: TextureFormat): GL | undefined { - const formatInfo = TEXTURE_FORMATS[format]; - const webglFormat = formatInfo?.gl; - if (webglFormat === undefined) { - throw new Error(`Unsupported texture format ${format}`); - } - return webglFormat; -} - -// LEGACY CODE -// TODO - remove when confident in above implementation - -/** luma.gl v9 does not user renderbufers -export function isRenderbufferFormatSupported( - gl: WebGL2RenderingContext, - format: TextureFormat, - extensions: GLExtensions -): boolean { - // Note: Order is important since the function call initializes extensions. - return isTextureFormatSupported(gl, format, extensions) && Boolean(TEXTURE_FORMATS[format]?.rb); -} -*/ - -/** Return a list of texture feature strings (for Device.features). Mainly compressed texture support */ -// export function getTextureFeatures( -// gl: WebGL2RenderingContext, -// extensions: GLExtensions -// ): DeviceFeature[] { -// const textureFeatures = Object.keys(TEXTURE_FEATURES) as DeviceFeature[]; -// return textureFeatures.filter(feature => checkTextureFeature(gl, feature, extensions)); -// } - -/** - * Map WebGL texture formats (GL constants) to WebGPU-style TextureFormat strings -export function convertGLToTextureFormat(format: GL | TextureFormat): TextureFormat { - if (typeof format === 'string') { - return format; - } - const entry = Object.entries(TEXTURE_FORMATS).find(([, entry]) => entry.gl === format); - if (!entry) { - throw new Error(`Unknown texture format ${format}`); - } - return entry[0] as TextureFormat; -} - */ - -/** Legal combinations for internalFormat, format and type * -// [GL.DEPTH_COMPONENT]: {types: [GL.UNSIGNED_SHORT, GL.UNSIGNED_INT, GL.UNSIGNED_INT_24_8]}, -// [GL.DEPTH_STENCIL]: , -// Sized texture format -// R -[GL.R8]: {dataFormat: GL.RED, types: [GL.UNSIGNED_BYTE], gl2: true}, -[GL.R16F]: {dataFormat: GL.RED, types: [GL.HALF_FLOAT, GL.FLOAT], gl2: true}, -[GL.R8UI]: {dataFormat: GL.RED_INTEGER, types: [GL.UNSIGNED_BYTE], gl2: true}, -// // RG -[GL.RG8]: {dataFormat: GL.RG, types: [GL.UNSIGNED_BYTE], gl2: true}, -[GL.RG16F]: {dataFormat: GL.RG, types: [GL.HALF_FLOAT, GL.FLOAT], gl2: true}, -[GL.RG8UI]: {dataFormat: GL.RG_INTEGER, types: [GL.UNSIGNED_BYTE], gl2: true}, -// // RGB -[GL.RGB8]: {dataFormat: GL.RGB, types: [GL.UNSIGNED_BYTE], gl2: true}, -[GL.SRGB8]: {dataFormat: GL.RGB, types: [GL.UNSIGNED_BYTE], gl2: true}, -[GL.RGB16F]: {dataFormat: GL.RGB, types: [GL.HALF_FLOAT, GL.FLOAT], gl2: true}, -[GL.RGB8UI]: {dataFormat: GL.RGB_INTEGER, types: [GL.UNSIGNED_BYTE], gl2: true}, -// // RGBA - -[GL.RGB565]: {dataFormat: GL.RGB, types: [GL.UNSIGNED_BYTE, GL.UNSIGNED_SHORT_5_6_5], gl2: true}, -[GL.R11F_G11F_B10F]: {dataFormat: GL.RGB, types: [GL.UNSIGNED_INT_10F_11F_11F_REV, GL.HALF_FLOAT, GL.FLOAT], gl2: true}, -[GL.RGB9_E5]: {dataFormat: GL.RGB, types: [GL.HALF_FLOAT, GL.FLOAT], gl2: true}, -[GL.RGBA8]: {dataFormat: GL.RGBA, types: [GL.UNSIGNED_BYTE], gl2: true}, -[GL.SRGB8_ALPHA8]: {dataFormat: GL.RGBA, types: [GL.UNSIGNED_BYTE], gl2: true, gl1ext: EXT_SRGB}, -[GL.RGB5_A1]: {dataFormat: GL.RGBA, types: [GL.UNSIGNED_BYTE, GL.UNSIGNED_SHORT_5_5_5_1], gl2: true}, -[GL.RGBA4]: {dataFormat: GL.RGBA, types: [GL.UNSIGNED_BYTE, GL.UNSIGNED_SHORT_4_4_4_4], gl2: true}, -[GL.RGBA16F]: {dataFormat: GL.RGBA, types: [GL.HALF_FLOAT, GL.FLOAT], gl2: true}, -[GL.RGBA8UI]: {dataFormat: GL.RGBA_INTEGER, types: [GL.UNSIGNED_BYTE], gl2: true} -*/ - -/* This table is now baked into the above table -type RenderbufferFormat = { - bpp: number; - gl2?: boolean; - ext?: string; -}; - -export const RENDERBUFFER_FORMATS: Record = { - [GL.DEPTH_COMPONENT16]: {bpp: 2}, // 16 depth bits. - // TODO - Not clear which webgpu value to map this to. - // [GL.DEPTH_COMPONENT24]: {gl2: true, bpp: 3}, - [GL.DEPTH_COMPONENT32F]: {gl2: true, bpp: 4}, - - [GL.STENCIL_INDEX8]: {bpp: 1}, // 8 stencil bits. - - [GL.DEPTH_STENCIL]: {bpp: 4}, - [GL.DEPTH24_STENCIL8]: {gl2: true, bpp: 4}, - [GL.DEPTH32F_STENCIL8]: {gl2: true, bpp: 5}, - - // When using a WebGL 1 context, color renderbuffer formats are limited - [GL.RGBA4]: {gl2: true, bpp: 2}, - [GL.RGB565]: {gl2: true, bpp: 2}, - [GL.RGB5_A1]: {gl2: true, bpp: 2}, - - // When using a WebGL 2 context, the following values are available additionally: - [GL.R8]: {gl2: true, bpp: 1}, - [GL.R8UI]: {gl2: true, bpp: 1}, - [GL.R8I]: {gl2: true, bpp: 1}, - [GL.R16UI]: {gl2: true, bpp: 2}, - [GL.R16I]: {gl2: true, bpp: 2}, - [GL.R32UI]: {gl2: true, bpp: 4}, - [GL.R32I]: {gl2: true, bpp: 4}, - [GL.RG8]: {gl2: true, bpp: 2}, - [GL.RG8UI]: {gl2: true, bpp: 2}, - [GL.RG8I]: {gl2: true, bpp: 2}, - [GL.RG16UI]: {gl2: true, bpp: 4}, - [GL.RG16I]: {gl2: true, bpp: 4}, - [GL.RG32UI]: {gl2: true, bpp: 8}, - [GL.RG32I]: {gl2: true, bpp: 8}, - [GL.RGB8]: {gl2: true, bpp: 3}, - [GL.RGBA8]: {gl2: true, bpp: 4}, - // [GL.SRGB8_ALPHA8]: {gl2: true, gl1: SRGB}, // When using the EXT_sRGB WebGL1 extension - [GL.RGB10_A2]: {gl2: true, bpp: 4}, - [GL.RGBA8UI]: {gl2: true, bpp: 4}, - [GL.RGBA8I]: {gl2: true, bpp: 4}, - [GL.RGB10_A2UI]: {gl2: true, bpp: 4}, - [GL.RGBA16UI]: {gl2: true, bpp: 8}, - [GL.RGBA16I]: {gl2: true, bpp: 8}, - [GL.RGBA32I]: {gl2: true, bpp: 16}, - [GL.RGBA32UI]: {gl2: true, bpp: 16}, - - // When using a WebGL 2 context and the EXT_color_buffer_float WebGL2 extension - [GL.R16F]: {ext: EXT_FLOAT_WEBGL2, bpp: 2}, - [GL.RG16F]: {ext: EXT_FLOAT_WEBGL2, bpp: 4}, - [GL.RGBA16F]: {ext: EXT_FLOAT_WEBGL2, bpp: 8}, - [GL.R32F]: {ext: EXT_FLOAT_WEBGL2, bpp: 4}, - [GL.RG32F]: {ext: EXT_FLOAT_WEBGL2, bpp: 8}, - // TODO - can't get WEBGL_color_buffer_float to work on renderbuffers - [GL.RGBA32F]: {ext: EXT_FLOAT_WEBGL2, bpp: 16}, - // [GL.RGBA32F]: {ext: EXT_FLOAT_WEBGL2}, - [GL.R11F_G11F_B10F]: {ext: EXT_FLOAT_WEBGL2, bpp: 4} -}; -*/ diff --git a/modules/webgl/src/adapter/converters/webgl-texture-table.ts b/modules/webgl/src/adapter/converters/webgl-texture-table.ts new file mode 100644 index 0000000000..98433c8f6a --- /dev/null +++ b/modules/webgl/src/adapter/converters/webgl-texture-table.ts @@ -0,0 +1,404 @@ +// luma.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import type { + DeviceFeature, + TextureFormat, + TextureFormatCapabilities, + DeviceTextureFormatCapabilities +} from '@luma.gl/core'; +import {decodeTextureFormat} from '@luma.gl/core'; +import {GL, GLPixelType, GLExtensions, GLTexelDataFormat} from '@luma.gl/constants'; +import {getWebGLExtension} from '../../context/helpers/webgl-extensions'; +import {getGLFromVertexType} from './vertex-formats'; + +/* eslint-disable camelcase */ + +// TEXTURE FEATURES + +// Define local webgl extension strings to optimize minification +const X_S3TC = 'WEBGL_compressed_texture_s3tc'; // BC1, BC2, BC3 +const X_S3TC_SRGB = 'WEBGL_compressed_texture_s3tc_srgb'; // BC1, BC2, BC3 +const X_RGTC = 'EXT_texture_compression_rgtc'; // BC4, BC5 +const X_BPTC = 'EXT_texture_compression_bptc'; // BC6, BC7 +const X_ETC2 = 'WEBGL_compressed_texture_etc'; // Renamed from 'WEBGL_compressed_texture_es3' +const X_ASTC = 'WEBGL_compressed_texture_astc'; +const X_ETC1 = 'WEBGL_compressed_texture_etc1'; +const X_PVRTC = 'WEBGL_compressed_texture_pvrtc'; +const X_ATC = 'WEBGL_compressed_texture_atc'; + +// Define local webgl extension strings to optimize minification +const EXT_texture_norm16 = 'EXT_texture_norm16'; +const EXT_render_snorm = 'EXT_render_snorm'; +const EXT_color_buffer_float = 'EXT_color_buffer_float'; + +// prettier-ignore +export const TEXTURE_FEATURES: Partial> = { + 'float32-renderable-webgl': ['EXT_color_buffer_float'], + 'float16-renderable-webgl': ['EXT_color_buffer_half_float'], + 'rgb9e5ufloat-renderable-webgl': ['WEBGL_render_shared_exponent'], + 'snorm8-renderable-webgl': [EXT_render_snorm], + 'norm16-renderable-webgl': [EXT_texture_norm16], + 'snorm16-renderable-webgl': [EXT_texture_norm16, EXT_render_snorm], + + 'float32-filterable': ['OES_texture_float_linear'], + 'float16-filterable-webgl': ['OES_texture_half_float_linear'], + 'texture-filterable-anisotropic-webgl': ['EXT_texture_filter_anisotropic'], + + 'texture-blend-float-webgl': ['EXT_float_blend'], + + 'texture-compression-bc': [X_S3TC, X_S3TC_SRGB, X_RGTC, X_BPTC], + // 'texture-compression-bc3-srgb-webgl': [X_S3TC_SRGB], + // 'texture-compression-bc3-webgl': [X_S3TC], + 'texture-compression-bc5-webgl': [X_RGTC], + 'texture-compression-bc7-webgl': [X_BPTC], + 'texture-compression-etc2': [X_ETC2], + 'texture-compression-astc': [X_ASTC], + 'texture-compression-etc1-webgl': [X_ETC1], + 'texture-compression-pvrtc-webgl': [X_PVRTC], + 'texture-compression-atc-webgl': [X_ATC] +}; + +export function isTextureFeature(feature: DeviceFeature): boolean { + return feature in TEXTURE_FEATURES; +} + +/** Checks a texture feature (for Device.features). Mainly compressed texture support */ +export function checkTextureFeature( + gl: WebGL2RenderingContext, + feature: DeviceFeature, + extensions: GLExtensions +): boolean { + const textureExtensions = TEXTURE_FEATURES[feature] || []; + return textureExtensions.every(extension => getWebGLExtension(gl, extension, extensions)); +} + +// TEXTURE FORMATS + +/** Map a format to webgl and constants */ +type WebGLFormatInfo = { + gl?: GL; + /** compressed */ + x?: string; + types?: GLPixelType[]; + dataFormat?: GLTexelDataFormat; + /** if depthTexture is set this is a depth/stencil format that can be set to a texture */ + depthTexture?: boolean; + /** @deprecated can this format be used with renderbuffers */ + rb?: boolean; +}; + +// TABLES + +/** + * Texture format data - + * Exported but can change without notice + */ +// prettier-ignore +export const WEBGL_TEXTURE_FORMATS: Record = { + // 8-bit formats + 'r8unorm': {gl: GL.R8, rb: true}, + 'r8snorm': {gl: GL.R8_SNORM}, + 'r8uint': {gl: GL.R8UI, rb: true}, + 'r8sint': {gl: GL.R8I, rb: true}, + + // 16-bit formats + 'rg8unorm': {gl: GL.RG8, rb: true}, + 'rg8snorm': {gl: GL.RG8_SNORM}, + 'rg8uint': {gl: GL.RG8UI, rb: true}, + 'rg8sint': {gl: GL.RG8I, rb: true}, + + 'r16uint': {gl: GL.R16UI, rb: true}, + 'r16sint': {gl: GL.R16I, rb: true}, + 'r16float': {gl: GL.R16F, rb: true}, + 'r16unorm-webgl': {gl: GL.R16_EXT, rb: true}, + 'r16snorm-webgl': {gl: GL.R16_SNORM_EXT}, + + // Packed 16-bit formats + 'rgba4unorm-webgl': {gl: GL.RGBA4, rb: true}, + 'rgb565unorm-webgl': {gl: GL.RGB565, rb: true}, + 'rgb5a1unorm-webgl': {gl: GL.RGB5_A1, rb: true}, + + // 24-bit formats + 'rgb8unorm-webgl': {gl: GL.RGB8}, + 'rgb8snorm-webgl': {gl: GL.RGB8_SNORM}, + + // 32-bit formats + 'rgba8unorm': {gl: GL.RGBA8}, + 'rgba8unorm-srgb': {gl: GL.SRGB8_ALPHA8}, + 'rgba8snorm': {gl: GL.RGBA8_SNORM}, + 'rgba8uint': {gl: GL.RGBA8UI}, + 'rgba8sint': {gl: GL.RGBA8I}, + // reverse colors, webgpu only + 'bgra8unorm': {}, + 'bgra8unorm-srgb': {}, + + 'rg16uint': {gl: GL.RG16UI}, + 'rg16sint': {gl: GL.RG16I}, + 'rg16float': {gl: GL.RG16F, rb: true}, + 'rg16unorm-webgl': {gl: GL.RG16_EXT}, + 'rg16snorm-webgl': {gl: GL.RG16_SNORM_EXT}, + + 'r32uint': {gl: GL.R32UI, rb: true}, + 'r32sint': {gl: GL.R32I, rb: true}, + 'r32float': {gl: GL.R32F}, + + // Packed 32-bit formats + 'rgb9e5ufloat': {gl: GL.RGB9_E5}, // , filter: true}, + 'rg11b10ufloat': {gl: GL.R11F_G11F_B10F, rb: true}, + 'rgb10a2unorm': {gl: GL.RGB10_A2, rb: true}, + 'rgb10a2uint-webgl': {gl: GL.RGB10_A2UI, rb: true}, + + // 48-bit formats + 'rgb16unorm-webgl': {gl: GL.RGB16_EXT}, // rgb not renderable + 'rgb16snorm-webgl': {gl: GL.RGB16_SNORM_EXT}, // rgb not renderable + + // 64-bit formats + 'rg32uint': {gl: GL.RG32UI, rb: true}, + 'rg32sint': {gl: GL.RG32I, rb: true}, + 'rg32float': {gl: GL.RG32F, rb: true}, + 'rgba16uint': {gl: GL.RGBA16UI, rb: true}, + 'rgba16sint': {gl: GL.RGBA16I, rb: true}, + 'rgba16float': {gl: GL.RGBA16F}, + 'rgba16unorm-webgl': {gl: GL.RGBA16_EXT, rb: true}, + 'rgba16snorm-webgl': {gl: GL.RGBA16_SNORM_EXT}, + + // 96-bit formats (deprecated!) + 'rgb32float-webgl': {gl: GL.RGB32F, x: EXT_color_buffer_float, dataFormat: GL.RGB, types: [GL.FLOAT]}, + + // 128-bit formats + 'rgba32uint': {gl: GL.RGBA32UI, rb: true}, + 'rgba32sint': {gl: GL.RGBA32I, rb: true}, + 'rgba32float': {gl: GL.RGBA32F, rb: true}, + + // Depth and stencil formats + 'stencil8': {gl: GL.STENCIL_INDEX8, rb: true}, // 8 stencil bits + + 'depth16unorm': {gl: GL.DEPTH_COMPONENT16, dataFormat: GL.DEPTH_COMPONENT, types: [GL.UNSIGNED_SHORT], rb: true}, // 16 depth bits + 'depth24plus': {gl: GL.DEPTH_COMPONENT24, dataFormat: GL.DEPTH_COMPONENT, types: [GL.UNSIGNED_INT]}, + 'depth32float': {gl: GL.DEPTH_COMPONENT32F, dataFormat: GL.DEPTH_COMPONENT, types: [GL.FLOAT], rb: true}, + + // The depth component of the "depth24plus" and "depth24plus-stencil8" formats may be implemented as either a 24-bit depth value or a "depth32float" value. + 'depth24plus-stencil8': {gl: GL.DEPTH24_STENCIL8, rb: true, depthTexture: true, dataFormat: GL.DEPTH_STENCIL, types: [GL.UNSIGNED_INT_24_8]}, + // "depth32float-stencil8" feature - TODO below is render buffer only? + 'depth32float-stencil8': {gl: GL.DEPTH32F_STENCIL8, dataFormat: GL.DEPTH_STENCIL, types: [GL.FLOAT_32_UNSIGNED_INT_24_8_REV], rb: true}, + + // BC compressed formats: check device.features.has("texture-compression-bc"); + + 'bc1-rgb-unorm-webgl': {gl: GL.COMPRESSED_RGB_S3TC_DXT1_EXT, x: X_S3TC}, + 'bc1-rgb-unorm-srgb-webgl': {gl: GL.COMPRESSED_SRGB_S3TC_DXT1_EXT, x: X_S3TC_SRGB}, + + 'bc1-rgba-unorm': {gl: GL.COMPRESSED_RGBA_S3TC_DXT1_EXT, x: X_S3TC}, + 'bc1-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_S3TC_DXT1_EXT, x: X_S3TC_SRGB}, + 'bc2-rgba-unorm': {gl: GL.COMPRESSED_RGBA_S3TC_DXT3_EXT, x: X_S3TC}, + 'bc2-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, x: X_S3TC_SRGB}, + 'bc3-rgba-unorm': {gl: GL.COMPRESSED_RGBA_S3TC_DXT5_EXT, x: X_S3TC}, + 'bc3-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, x: X_S3TC_SRGB}, + 'bc4-r-unorm': {gl: GL.COMPRESSED_RED_RGTC1_EXT, x: X_RGTC}, + 'bc4-r-snorm': {gl: GL.COMPRESSED_SIGNED_RED_RGTC1_EXT, x: X_RGTC}, + 'bc5-rg-unorm': {gl: GL.COMPRESSED_RED_GREEN_RGTC2_EXT, x: X_RGTC}, + 'bc5-rg-snorm': {gl: GL.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, x: X_RGTC}, + 'bc6h-rgb-ufloat': {gl: GL.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, x: X_BPTC}, + 'bc6h-rgb-float': {gl: GL.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, x: X_BPTC}, + 'bc7-rgba-unorm': {gl: GL.COMPRESSED_RGBA_BPTC_UNORM_EXT, x: X_BPTC}, + 'bc7-rgba-unorm-srgb': {gl: GL.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT, x: X_BPTC}, + + // WEBGL_compressed_texture_etc: device.features.has("texture-compression-etc2") + // Note: Supposedly guaranteed availability compressed formats in WebGL2, but through CPU decompression + + 'etc2-rgb8unorm': {gl: GL.COMPRESSED_RGB8_ETC2}, + 'etc2-rgb8unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ETC2}, + 'etc2-rgb8a1unorm': {gl: GL.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2}, + 'etc2-rgb8a1unorm-srgb': {gl: GL.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2}, + 'etc2-rgba8unorm': {gl: GL.COMPRESSED_RGBA8_ETC2_EAC}, + 'etc2-rgba8unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC}, + + 'eac-r11unorm': {gl: GL.COMPRESSED_R11_EAC}, + 'eac-r11snorm': {gl: GL.COMPRESSED_SIGNED_R11_EAC}, + 'eac-rg11unorm': {gl: GL.COMPRESSED_RG11_EAC}, + 'eac-rg11snorm': {gl: GL.COMPRESSED_SIGNED_RG11_EAC}, + + // X_ASTC compressed formats: device.features.has("texture-compression-astc") + + 'astc-4x4-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_4x4_KHR}, + 'astc-4x4-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, + 'astc-5x4-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_5x4_KHR}, + 'astc-5x4-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, + 'astc-5x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_5x5_KHR}, + 'astc-5x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, + 'astc-6x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_6x5_KHR}, + 'astc-6x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, + 'astc-6x6-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_6x6_KHR}, + 'astc-6x6-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, + 'astc-8x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_8x5_KHR}, + 'astc-8x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, + 'astc-8x6-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_8x6_KHR}, + 'astc-8x6-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, + 'astc-8x8-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_8x8_KHR}, + 'astc-8x8-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, + 'astc-10x5-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x10_KHR}, + 'astc-10x5-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, + 'astc-10x6-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x6_KHR}, + 'astc-10x6-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}, + 'astc-10x8-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x8_KHR}, + 'astc-10x8-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, + 'astc-10x10-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_10x10_KHR}, + 'astc-10x10-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, + 'astc-12x10-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_12x10_KHR}, + 'astc-12x10-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}, + 'astc-12x12-unorm': {gl: GL.COMPRESSED_RGBA_ASTC_12x12_KHR}, + 'astc-12x12-unorm-srgb': {gl: GL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, + + // WEBGL_compressed_texture_pvrtc + + 'pvrtc-rgb4unorm-webgl': {gl: GL.COMPRESSED_RGB_PVRTC_4BPPV1_IMG}, + 'pvrtc-rgba4unorm-webgl': {gl: GL.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG}, + 'pvrtc-rbg2unorm-webgl': {gl: GL.COMPRESSED_RGB_PVRTC_2BPPV1_IMG}, + 'pvrtc-rgba2unorm-webgl': {gl: GL.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}, + + // WEBGL_compressed_texture_etc1 + + 'etc1-rbg-unorm-webgl': {gl: GL.COMPRESSED_RGB_ETC1_WEBGL}, + + // WEBGL_compressed_texture_atc + + 'atc-rgb-unorm-webgl': {gl: GL.COMPRESSED_RGB_ATC_WEBGL}, + 'atc-rgba-unorm-webgl': {gl: GL.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL}, + 'atc-rgbai-unorm-webgl': {gl: GL.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL} +}; + +// FUNCTIONS + +/** Checks if a texture format is supported */ +export function isWebGLTextureFormatCapabilitiesed( + gl: WebGL2RenderingContext, + format: TextureFormat, + extensions: GLExtensions +): boolean { + const webglTextureInfo = WEBGL_TEXTURE_FORMATS[format]; + // Check that we have a GL constant + if (!webglTextureInfo?.gl) { + return false; + } + + // Check extensions + const extension = webglTextureInfo.x; + if (extension) { + return Boolean(getWebGLExtension(gl, extension, extensions)); + } + return true; +} + +/** Checks if a texture format is supported, renderable, filterable etc */ +export function getTextureFormatCapabilitiesWebGL( + gl: WebGL2RenderingContext, + formatSupport: TextureFormatCapabilities, + extensions: GLExtensions +): DeviceTextureFormatCapabilities { + let supported = formatSupport.create; + const webglFormatInfo = WEBGL_TEXTURE_FORMATS[formatSupport.format]; + + // Support Check that we have a GL constant + if (webglFormatInfo?.gl === undefined) { + supported = false; + } + + if (webglFormatInfo?.x) { + supported = supported && Boolean(getWebGLExtension(gl, webglFormatInfo.x, extensions)); + } + + return { + format: formatSupport.format, + // @ts-ignore + create: supported && formatSupport.create, + // @ts-ignore + render: supported && formatSupport.render, + // @ts-ignore + filter: supported && formatSupport.filter, + // @ts-ignore + blend: supported && formatSupport.blend, + // @ts-ignore + store: supported && formatSupport.store + }; +} + +/** Get parameters necessary to work with format in WebGL: internalFormat, dataFormat, type, compressed, */ +export function getTextureFormatWebGL(format: TextureFormat): { + internalFormat: GL; + format: GLTexelDataFormat; + type: GLPixelType; + compressed: boolean; +} { + const formatData = WEBGL_TEXTURE_FORMATS[format]; + const webglFormat = convertTextureFormatToGL(format); + const decoded = decodeTextureFormat(format); + return { + internalFormat: webglFormat, + format: + formatData?.dataFormat || + getWebGLPixelDataFormat(decoded.channels, decoded.integer, decoded.normalized, webglFormat), + // depth formats don't have a type + type: decoded.dataType + ? getGLFromVertexType(decoded.dataType) + : formatData?.types?.[0] || GL.UNSIGNED_BYTE, + compressed: decoded.compressed || false + }; +} + +export function getDepthStencilAttachmentWebGL( + format: TextureFormat +): GL.DEPTH_ATTACHMENT | GL.STENCIL_ATTACHMENT | GL.DEPTH_STENCIL_ATTACHMENT { + const formatInfo = decodeTextureFormat(format); + switch (formatInfo.attachment) { + case 'depth': + return GL.DEPTH_ATTACHMENT; + case 'stencil': + return GL.STENCIL_ATTACHMENT; + case 'depth-stencil': + return GL.DEPTH_STENCIL_ATTACHMENT; + default: + throw new Error(`Not a depth stencil format: ${format}`); + } +} + +/** TODO - VERY roundabout legacy way of calculating bytes per pixel */ +export function getTextureFormatBytesPerPixel(format: TextureFormat): number { + const formatInfo = decodeTextureFormat(format); + return formatInfo.bytesPerPixel; +} + +// DATA TYPE HELPERS + +export function getWebGLPixelDataFormat( + channels: 'r' | 'rg' | 'rgb' | 'rgba' | 'bgra', + integer: boolean, + normalized: boolean, + format: GL +): GLTexelDataFormat { + // WebGL1 formats use same internalFormat + if (format === GL.RGBA || format === GL.RGB) { + return format; + } + // prettier-ignore + switch (channels) { + case 'r': return integer && !normalized ? GL.RED_INTEGER : GL.RED; + case 'rg': return integer && !normalized ? GL.RG_INTEGER : GL.RG; + case 'rgb': return integer && !normalized ? GL.RGB_INTEGER : GL.RGB; + case 'rgba': return integer && !normalized ? GL.RGBA_INTEGER : GL.RGBA; + case 'bgra': throw new Error('bgra pixels not supported by WebGL'); + default: return GL.RGBA; + } +} + +/** + * Map WebGPU style texture format strings to GL constants + */ +function convertTextureFormatToGL(format: TextureFormat): GL | undefined { + const formatInfo = WEBGL_TEXTURE_FORMATS[format]; + const webglFormat = formatInfo?.gl; + if (webglFormat === undefined) { + throw new Error(`Unsupported texture format ${format}`); + } + return webglFormat; +} diff --git a/modules/webgl/src/adapter/device-helpers/webgl-device-features.ts b/modules/webgl/src/adapter/device-helpers/webgl-device-features.ts index 0505a2e0dd..f8bbf5f278 100644 --- a/modules/webgl/src/adapter/device-helpers/webgl-device-features.ts +++ b/modules/webgl/src/adapter/device-helpers/webgl-device-features.ts @@ -12,7 +12,7 @@ import { isTextureFeature, checkTextureFeature, TEXTURE_FEATURES -} from '../converters/texture-formats'; +} from '../converters/webgl-texture-table'; /** * Defines luma.gl "feature" names and semantics diff --git a/modules/webgl/src/adapter/resources/webgl-command-buffer.ts b/modules/webgl/src/adapter/resources/webgl-command-buffer.ts index 72adbc8bc2..29124cac22 100644 --- a/modules/webgl/src/adapter/resources/webgl-command-buffer.ts +++ b/modules/webgl/src/adapter/resources/webgl-command-buffer.ts @@ -24,7 +24,7 @@ import {WebGLDevice} from '../webgl-device'; import {WEBGLBuffer} from './webgl-buffer'; import {WEBGLTexture} from './webgl-texture'; import {WEBGLFramebuffer} from './webgl-framebuffer'; -import {getTextureFormatWebGL} from '../converters/texture-formats'; +import {getTextureFormatWebGL} from '../converters/webgl-texture-table'; type CopyBufferToBufferCommand = { name: 'copy-buffer-to-buffer'; diff --git a/modules/webgl/src/adapter/resources/webgl-framebuffer.ts b/modules/webgl/src/adapter/resources/webgl-framebuffer.ts index 5a64c24b10..d61c499af5 100644 --- a/modules/webgl/src/adapter/resources/webgl-framebuffer.ts +++ b/modules/webgl/src/adapter/resources/webgl-framebuffer.ts @@ -8,7 +8,7 @@ import {GL} from '@luma.gl/constants'; import {WebGLDevice} from '../webgl-device'; import {WEBGLTexture} from './webgl-texture'; import {WEBGLTextureView} from './webgl-texture-view'; -import {getDepthStencilAttachmentWebGL} from '../converters/texture-formats'; +import {getDepthStencilAttachmentWebGL} from '../converters/webgl-texture-table'; export type Attachment = WEBGLTextureView | WEBGLTexture; // | WEBGLRenderbuffer; diff --git a/modules/webgl/src/adapter/resources/webgl-texture.ts b/modules/webgl/src/adapter/resources/webgl-texture.ts index f80487ab03..b1967eec0c 100644 --- a/modules/webgl/src/adapter/resources/webgl-texture.ts +++ b/modules/webgl/src/adapter/resources/webgl-texture.ts @@ -2,14 +2,6 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -// @todo texture refactor -// - [ ] cube texture init params P1 -// - [ ] 3d texture init params P1 -// - [ ] GPU memory tracking -// - [ ] raw data inputs -// - [ ] video (external) textures - -// import {TypedArray} from '@math.gl/types'; import type { Device, TextureProps, @@ -26,8 +18,6 @@ import type { TextureArrayData, TextureCubeArrayData } from '@luma.gl/core'; -// import {decodeTextureFormat} from '@luma.gl/core'; -// import {Buffer, Texture, log} from '@luma.gl/core'; import {Texture, log} from '@luma.gl/core'; import { GL, @@ -36,17 +26,12 @@ import { GLTexelDataFormat, GLTextureTarget } from '@luma.gl/constants'; -// import {GLPixelDataType} from '@luma.gl/constants'; -import {withGLParameters} from '../../context/state-tracker/with-parameters'; -// getTextureFormatBytesPerPixel -import {getTextureFormatWebGL} from '../converters/texture-formats'; +import {getTextureFormatWebGL} from '../converters/webgl-texture-table'; import {convertSamplerParametersToWebGL} from '../converters/sampler-parameters'; import {WebGLDevice} from '../webgl-device'; -// import {WEBGLBuffer} from './webgl-buffer'; import {WEBGLSampler} from './webgl-sampler'; import {WEBGLTextureView} from './webgl-texture-view'; -// import type {WebGLSetTextureOptions, WebGLCopyTextureOptions} from '../helpers/webgl-texture-utils'; import { initializeTextureStorage, // clearMipLevel, @@ -121,9 +106,7 @@ export class WEBGLTexture extends Texture { Object.seal(this); } - /** - * Initialize texture with supplied props - */ + /** Initialize texture with supplied props */ // eslint-disable-next-line max-statements _initialize(propsWithData: TextureProps): void { this.handle = this.props.handle || this.gl.createTexture(); @@ -203,19 +186,20 @@ export class WEBGLTexture extends Texture { } // Call to regenerate mipmaps after modifying texture(s) - generateMipmap(params = {}): void { - if ( - !this.device.isTextureFormatRenderable(this.props.format) || - !this.device.isTextureFormatFilterable(this.props.format) - ) { + generateMipmap(options?: {force?: boolean}): void { + const isFilterableAndRenderable = + this.device.isTextureFormatRenderable(this.props.format) && + this.device.isTextureFormatFilterable(this.props.format); + if (!isFilterableAndRenderable) { log.warn(`${this} is not renderable or filterable, may not be able to generate mipmaps`)(); + if (!options?.force) { + return; + } } try { this.gl.bindTexture(this.glTarget, this.handle); - withGLParameters(this.gl, params, () => { - this.gl.generateMipmap(this.glTarget); - }); + this.gl.generateMipmap(this.glTarget); } catch (error) { log.warn(`Error generating mipmap for ${this}: ${(error as Error).message}`)(); } finally { diff --git a/modules/webgl/src/adapter/webgl-device.ts b/modules/webgl/src/adapter/webgl-device.ts index ff68e000cb..152c5974e3 100644 --- a/modules/webgl/src/adapter/webgl-device.ts +++ b/modules/webgl/src/adapter/webgl-device.ts @@ -6,8 +6,8 @@ import type {TypedArray} from '@math.gl/types'; import type { DeviceProps, DeviceInfo, + DeviceTextureFormatCapabilities, CanvasContextProps, - TextureFormat, Buffer, Texture, Framebuffer, @@ -46,11 +46,7 @@ import {WebGLCanvasContext} from './webgl-canvas-context'; import type {Spector} from '../context/debug/spector-types'; import {initializeSpectorJS} from '../context/debug/spector'; import {makeDebugContext} from '../context/debug/webgl-developer-tools'; -import { - isTextureFormatSupported, - isTextureFormatRenderable, - isTextureFormatFilterable -} from './converters/texture-formats'; +import {getTextureFormatCapabilitiesWebGL} from './converters/webgl-texture-table'; import {uid} from '../utils/uid'; import {WEBGLBuffer} from './resources/webgl-buffer'; @@ -222,18 +218,6 @@ export class WebGLDevice extends Device { return this.gl.isContextLost(); } - isTextureFormatSupported(format: TextureFormat): boolean { - return isTextureFormatSupported(this.gl, format, this._extensions); - } - - isTextureFormatFilterable(format: TextureFormat): boolean { - return isTextureFormatFilterable(this.gl, format, this._extensions); - } - - isTextureFormatRenderable(format: TextureFormat): boolean { - return isTextureFormatRenderable(this.gl, format, this._extensions); - } - // IMPLEMENTATION OF ABSTRACT DEVICE createCanvasContext(props?: CanvasContextProps): CanvasContext { @@ -376,6 +360,12 @@ export class WebGLDevice extends Device { resetGLParameters(this.gl); } + override _getDeviceSpecificTextureFormatCapabilities( + capabilities: DeviceTextureFormatCapabilities + ): DeviceTextureFormatCapabilities { + return getTextureFormatCapabilitiesWebGL(this.gl, capabilities, this._extensions); + } + // // WebGL-only API (not part of `Device` API) // diff --git a/modules/webgl/src/index.ts b/modules/webgl/src/index.ts index c58b292931..be99c794fd 100644 --- a/modules/webgl/src/index.ts +++ b/modules/webgl/src/index.ts @@ -49,9 +49,6 @@ export {setDeviceParameters, withDeviceParameters} from './adapter/converters/de export {getShaderLayoutFromGLSL} from './adapter/helpers/get-shader-layout'; export {WebGLStateTracker} from './context/state-tracker/webgl-state-tracker'; -// TEST EXPORTS -export {TEXTURE_FORMATS as _TEXTURE_FORMATS} from './adapter/converters/texture-formats'; - // DEPRECATED TEST EXPORTS export { resetGLParameters, diff --git a/modules/webgpu/src/adapter/webgpu-device.ts b/modules/webgpu/src/adapter/webgpu-device.ts index 7a6d6c1eee..0195b840f6 100644 --- a/modules/webgpu/src/adapter/webgpu-device.ts +++ b/modules/webgpu/src/adapter/webgpu-device.ts @@ -9,13 +9,13 @@ import type { DeviceInfo, DeviceLimits, DeviceFeature, + DeviceTextureFormatCapabilities, CanvasContextProps, BufferProps, SamplerProps, ShaderProps, Texture, TextureProps, - TextureFormat, ExternalTextureProps, FramebufferProps, RenderPipelineProps, @@ -122,24 +122,6 @@ export class WebGPUDevice extends Device { this.handle.destroy(); } - isTextureFormatSupported(format: TextureFormat): boolean { - return !format.includes('webgl'); - } - - /** @todo implement proper check? */ - isTextureFormatFilterable(format: TextureFormat): boolean { - return ( - this.isTextureFormatSupported(format) && - !format.startsWith('depth') && - !format.startsWith('stencil') - ); - } - - /** @todo implement proper check? */ - isTextureFormatRenderable(format: TextureFormat): boolean { - return this.isTextureFormatSupported(format); - } - get isLost(): boolean { return this._isLost; } @@ -289,6 +271,19 @@ export class WebGPUDevice extends Device { return new DeviceFeatures(Array.from(features), this.props._disabledFeatures); } + override _getDeviceSpecificTextureFormatCapabilities( + capabilities: DeviceTextureFormatCapabilities + ): DeviceTextureFormatCapabilities { + const {format} = capabilities; + if (format.includes('webgl')) { + return {format, create: false, render: false, filter: false, blend: false, store: false}; + } + return capabilities; + } + + // DEPRECATED METHODS + + // @deprecated copyExternalImageToTexture(options: { texture: Texture; mipLevel?: number;