@luma.gl/core
Version:
The luma.gl core Device API
4 lines • 415 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/index.ts", "../src/utils/stats-manager.ts", "../src/utils/log.ts", "../src/utils/uid.ts", "../src/adapter/resources/resource.ts", "../src/adapter/resources/buffer.ts", "../src/shadertypes/data-types/data-type-decoder.ts", "../src/shadertypes/vertex-types/vertex-format-decoder.ts", "../src/shadertypes/texture-types/texture-format-table.ts", "../src/shadertypes/texture-types/texture-format-decoder.ts", "../src/shadertypes/image-types/image-types.ts", "../src/adapter/device.ts", "../src/adapter/luma.ts", "../src/adapter/adapter.ts", "../src/adapter/canvas-surface.ts", "../src/adapter/canvas-observer.ts", "../src/utils/promise-utils.ts", "../src/utils/assert.ts", "../src/adapter/canvas-context.ts", "../src/adapter/presentation-context.ts", "../src/adapter/resources/sampler.ts", "../src/adapter/resources/texture.ts", "../src/adapter/resources/texture-view.ts", "../src/adapter/resources/external-texture.ts", "../src/adapter-utils/format-compiler-log.ts", "../src/adapter/resources/shader.ts", "../src/adapter/resources/framebuffer.ts", "../src/adapter/resources/render-pipeline.ts", "../src/adapter/resources/shared-render-pipeline.ts", "../src/adapter/resources/compute-pipeline.ts", "../src/factories/pipeline-factory.ts", "../src/factories/shader-factory.ts", "../src/adapter-utils/bind-groups.ts", "../src/factories/bind-group-factory.ts", "../src/adapter/resources/render-pass.ts", "../src/adapter/resources/compute-pass.ts", "../src/adapter/resources/command-encoder.ts", "../src/adapter/resources/command-buffer.ts", "../src/shadertypes/shader-types/shader-type-decoder.ts", "../src/adapter-utils/get-attribute-from-layouts.ts", "../src/adapter/resources/vertex-array.ts", "../src/adapter/resources/transform-feedback.ts", "../src/adapter/resources/query-set.ts", "../src/adapter/resources/fence.ts", "../src/adapter/resources/pipeline-layout.ts", "../src/shadertypes/data-types/decode-data-types.ts", "../src/shadertypes/shader-types/shader-block-layout.ts", "../src/utils/array-utils-flat.ts", "../src/utils/is-array.ts", "../src/portable/shader-block-writer.ts", "../src/utils/array-equal.ts", "../src/portable/uniform-block.ts", "../src/portable/uniform-store.ts", "../src/shadertypes/texture-types/texture-layout.ts", "../src/shadertypes/texture-types/pixel-utils.ts"],
"sourcesContent": ["// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// MAIN API ACCESS POINT\nexport type {AttachDeviceProps, CreateDeviceProps} from './adapter/luma';\nexport {luma} from './adapter/luma';\n\n// ADAPTER (DEVICE AND GPU RESOURCE INTERFACES)\nexport {Adapter} from './adapter/adapter';\n\nexport type {\n DeviceProps,\n DeviceInfo,\n DeviceFeature,\n DeviceTextureFormatCapabilities\n} from './adapter/device';\nexport {Device, DeviceFeatures, DeviceLimits} from './adapter/device';\n\nexport type {CanvasContextProps} from './adapter/canvas-context';\nexport {CanvasContext} from './adapter/canvas-context';\nexport type {PresentationContextProps} from './adapter/presentation-context';\nexport {PresentationContext} from './adapter/presentation-context';\n\n// GPU RESOURCES\nexport {Resource, type ResourceProps} from './adapter/resources/resource';\n\nexport {Buffer, type BufferProps, type BufferMapCallback} from './adapter/resources/buffer';\n\nexport {Texture, type TextureProps} from './adapter/resources/texture';\n\nexport {TextureView, type TextureViewProps} from './adapter/resources/texture-view';\n\nexport type {ExternalTextureProps} from './adapter/resources/external-texture';\nexport {ExternalTexture} from './adapter/resources/external-texture';\n\nexport type {ShaderProps} from './adapter/resources/shader';\nexport {Shader} from './adapter/resources/shader';\n\nexport type {SamplerProps, SamplerParameters} from './adapter/resources/sampler';\nexport {Sampler} from './adapter/resources/sampler';\n\nexport type {FramebufferProps} from './adapter/resources/framebuffer';\nexport {Framebuffer} from './adapter/resources/framebuffer';\n\nexport type {RenderPipelineProps} from './adapter/resources/render-pipeline';\nexport {RenderPipeline} from './adapter/resources/render-pipeline';\nexport {\n SharedRenderPipeline,\n type SharedRenderPipelineProps\n} from './adapter/resources/shared-render-pipeline';\nexport type {PipelineFactoryProps} from './factories/pipeline-factory';\nexport {PipelineFactory} from './factories/pipeline-factory';\nexport {ShaderFactory} from './factories/shader-factory';\nexport {_getDefaultBindGroupFactory} from './factories/bind-group-factory';\n\nexport type {RenderPassProps} from './adapter/resources/render-pass';\nexport {RenderPass} from './adapter/resources/render-pass';\n\nexport type {ComputePipelineProps} from './adapter/resources/compute-pipeline';\nexport {ComputePipeline} from './adapter/resources/compute-pipeline';\n\nexport type {ComputePassProps} from './adapter/resources/compute-pass';\nexport {ComputePass} from './adapter/resources/compute-pass';\n\nexport type {CommandEncoderProps} from './adapter/resources/command-encoder';\nexport {CommandEncoder} from './adapter/resources/command-encoder';\n\nexport type {CommandBufferProps} from './adapter/resources/command-buffer';\nexport {CommandBuffer} from './adapter/resources/command-buffer';\n\nexport type {VertexArrayProps} from './adapter/resources/vertex-array';\nexport {VertexArray} from './adapter/resources/vertex-array';\n\nexport type {TransformFeedbackProps, BufferRange} from './adapter/resources/transform-feedback';\nexport {TransformFeedback} from './adapter/resources/transform-feedback';\n\nexport type {QuerySetProps} from './adapter/resources/query-set';\nexport {QuerySet} from './adapter/resources/query-set';\n\nexport {Fence, type FenceProps} from './adapter/resources/fence';\n\nexport type {PipelineLayoutProps} from './adapter/resources/pipeline-layout';\nexport {PipelineLayout} from './adapter/resources/pipeline-layout';\n\n// PORTABLE API - UNIFORM BUFFERS\nexport {\n makeShaderBlockLayout,\n type ShaderBlockLayout,\n type ShaderBlockLayoutEntry,\n type ShaderBlockLayoutOptions\n} from './shadertypes/shader-types/shader-block-layout';\nexport {ShaderBlockWriter} from './portable/shader-block-writer';\nexport {UniformBlock} from './portable/uniform-block';\nexport {UniformStore} from './portable/uniform-store';\n// TEXTURE TYPES\n\n// API TYPES\nexport type {CompilerMessage} from './adapter/types/compiler-message';\n\nexport type {ExternalImage} from './shadertypes/image-types/image-types';\n\nexport {\n type CopyExternalImageOptions,\n type CopyImageDataOptions,\n type TextureReadOptions,\n type TextureWriteOptions\n} from './adapter/resources/texture';\n\nexport type {Parameters, PrimitiveTopology, IndexFormat} from './adapter/types/parameters';\n\nexport type {\n CullMode,\n FrontFace,\n RasterizationParameters,\n CompareFunction,\n StencilOperation,\n DepthStencilParameters,\n BlendFactor,\n BlendOperation,\n ColorParameters,\n MultisampleParameters,\n RenderPassParameters,\n RenderPipelineParameters,\n PolygonMode,\n ProvokingVertex\n} from './adapter/types/parameters';\n\nexport type {ColorAttachment, DepthStencilAttachment} from './adapter/types/attachments';\n\nexport type {\n ShaderLayout,\n ComputeShaderLayout,\n AttributeDeclaration,\n BindingDeclaration,\n Binding,\n Bindings,\n BindingsByGroup,\n UniformBufferBindingLayout,\n StorageBufferBindingLayout,\n TextureBindingLayout,\n SamplerBindingLayout,\n StorageTextureBindingLayout\n} from './adapter/types/shader-layout';\nexport type {BufferLayout, BufferAttributeLayout} from './adapter/types/buffer-layout';\nexport type {\n // Deprecated, todo\n AttributeBinding,\n UniformBinding,\n UniformBlockBinding,\n VaryingBinding\n} from './adapter/types/shader-layout';\n\nexport type {UniformValue} from './adapter/types/uniforms';\nexport type {\n CompositeUniformValue,\n CompositeUniformValueArray,\n CompositeUniformValueStruct\n} from './adapter/types/uniforms';\n\n// TYPED ARRAY TYPES\n\nexport type {\n NumberArray,\n TypedArray,\n TypedArrayConstructor,\n BigTypedArray,\n BigTypedArrayConstructor\n} from './types';\n\n// GPU TYPE UTILS - BASIC DATA TYPES\n\nexport {\n type PrimitiveDataType,\n type SignedDataType,\n type NormalizedDataType,\n type DataTypeInfo,\n type PrimitiveDataTypeT,\n type SignedDataTypeT,\n type TypedArrayConstructorT,\n type NormalizedTypedArrayConstructorT\n} from './shadertypes/data-types/data-types';\nexport {dataTypeDecoder} from './shadertypes/data-types/data-type-decoder';\nexport {getTypedArrayConstructor} from './shadertypes/data-types/decode-data-types';\n\nexport {\n type AttributeShaderTypeT,\n type AttributeShaderType,\n type ArrayShaderType,\n type CompositeShaderType,\n type StructShaderType,\n type VariableShaderTypeT,\n type VariableShaderType\n} from './shadertypes/shader-types/shader-types';\nexport {\n shaderTypeDecoder,\n getAttributeShaderTypeInfo,\n getVariableShaderTypeInfo,\n type AttributeShaderTypeInfo\n} from './shadertypes/shader-types/shader-type-decoder';\n\n// GPU TYPE UTILS - VERTEX ARRAYs\n\nexport {\n type VertexFormat,\n type VertexFormatDataTypeT\n} from './shadertypes/vertex-types/vertex-formats';\n\nexport {vertexFormatDecoder} from './shadertypes/vertex-types/vertex-format-decoder';\n\n// GPU TYPE UTILS - Texture Formats\n\nexport {\n type TextureFormat,\n type TextureFormatColor,\n type TextureFormatDepthStencil,\n type CompressedTextureFormat,\n type TextureCompression,\n type TextureFormatInfo,\n type TextureFormatCapabilities,\n type TextureMemoryLayout\n} from './shadertypes/texture-types/texture-formats';\nexport {type TextureFormatDataTypeT} from './shadertypes/texture-types/texture-format-generics';\n\nexport {\n type TextureFormatDecoder,\n textureFormatDecoder\n} from './shadertypes/texture-types/texture-format-decoder';\n\nexport {getTextureImageView, setTextureImageData} from './shadertypes/texture-types/texture-layout';\nexport {type PixelData, readPixel, writePixel} from './shadertypes/texture-types/pixel-utils';\n\nexport {isExternalImage, getExternalImageSize} from './shadertypes/image-types/image-types';\n\n// GENERAL EXPORTS - FOR APPLICATIONS\n\nexport type {StatsManager} from './utils/stats-manager'; // TODO - should this be moved to probe.gl?\n\n// ADAPTER UTILS - for implementing Device adapters (@luma.gl/webgl and @luma.gl/webgpu)\n\nexport type {\n CopyBufferToBufferOptions,\n CopyBufferToTextureOptions,\n CopyTextureToBufferOptions,\n CopyTextureToTextureOptions\n} from './adapter/resources/command-encoder';\n\n// INTERNAL UTILS - for use in other luma.gl modules only\nexport {log} from './utils/log';\nexport {\n getShaderLayoutBinding,\n normalizeBindingsByGroup,\n flattenBindingsByGroup\n} from './adapter-utils/bind-groups';\nexport {assert, assertDefined} from './utils/assert';\nexport {getScratchArray} from './utils/array-utils-flat';\nexport type {AttributeInfo} from './adapter-utils/get-attribute-from-layouts';\nexport {getAttributeInfosFromLayouts} from './adapter-utils/get-attribute-from-layouts';\n\n// TEST EXPORTS\nexport {\n getTextureFormatDefinition as _getTextureFormatDefinition,\n getTextureFormatTable as _getTextureFormatTable\n} from './shadertypes/texture-types/texture-format-table';\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Stat, Stats} from '@probe.gl/stats';\n\nconst GPU_TIME_AND_MEMORY_STATS = 'GPU Time and Memory';\nconst GPU_TIME_AND_MEMORY_STAT_ORDER = [\n 'Adapter',\n 'GPU',\n 'GPU Type',\n 'GPU Backend',\n 'Frame Rate',\n 'CPU Time',\n 'GPU Time',\n 'GPU Memory',\n 'Buffer Memory',\n 'Texture Memory',\n 'Referenced Buffer Memory',\n 'Referenced Texture Memory',\n 'Swap Chain Texture'\n] as const;\nconst ORDERED_STATS_CACHE = new WeakMap<\n Stats,\n {orderedStatNames: readonly string[]; statCount: number}\n>();\nconst ORDERED_STAT_NAME_SET_CACHE = new WeakMap<readonly string[], Set<string>>();\n\n/**\n * Helper class managing a collection of probe.gl stats objects\n */\nexport class StatsManager {\n stats = new Map();\n\n getStats(name: string): Stats {\n return this.get(name);\n }\n\n get(name: string): Stats {\n if (!this.stats.has(name)) {\n this.stats.set(name, new Stats({id: name}));\n }\n\n const stats = this.stats.get(name);\n if (name === GPU_TIME_AND_MEMORY_STATS) {\n initializeStats(stats, GPU_TIME_AND_MEMORY_STAT_ORDER);\n }\n\n return stats;\n }\n}\n\n/** Global stats for all luma.gl devices */\nexport const lumaStats: StatsManager = new StatsManager();\n\nfunction initializeStats(stats: Stats, orderedStatNames: readonly string[]): void {\n const statsMap = stats.stats;\n let addedOrderedStat = false;\n for (const statName of orderedStatNames) {\n if (!statsMap[statName]) {\n stats.get(statName);\n addedOrderedStat = true;\n }\n }\n\n const statCount = Object.keys(statsMap).length;\n const cachedStats = ORDERED_STATS_CACHE.get(stats);\n if (\n !addedOrderedStat &&\n cachedStats?.orderedStatNames === orderedStatNames &&\n cachedStats.statCount === statCount\n ) {\n return;\n }\n\n const reorderedStats: Record<string, Stat> = {};\n let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE.get(orderedStatNames);\n if (!orderedStatNamesSet) {\n orderedStatNamesSet = new Set(orderedStatNames);\n ORDERED_STAT_NAME_SET_CACHE.set(orderedStatNames, orderedStatNamesSet);\n }\n\n for (const statName of orderedStatNames) {\n if (statsMap[statName]) {\n reorderedStats[statName] = statsMap[statName];\n }\n }\n\n for (const [statName, stat] of Object.entries(statsMap)) {\n if (!orderedStatNamesSet.has(statName)) {\n reorderedStats[statName] = stat;\n }\n }\n\n for (const statName of Object.keys(statsMap)) {\n delete statsMap[statName];\n }\n\n Object.assign(statsMap, reorderedStats);\n ORDERED_STATS_CACHE.set(stats, {orderedStatNames, statCount});\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Log} from '@probe.gl/log';\n\n/** Global log instance */\nexport const log: Log = new Log({id: 'luma.gl'});\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nconst uidCounters: Record<string, number> = {};\n\n/**\n * Returns a UID.\n * @param id= - Identifier base name\n * @return uid\n **/\nexport function uid(id: string = 'id'): string {\n uidCounters[id] = uidCounters[id] || 1;\n const count = uidCounters[id]++;\n return `${id}-${count}`;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {Device} from '../device';\nimport type {Stat, Stats} from '@probe.gl/stats';\nimport {uid} from '../../utils/uid';\n\nconst CPU_HOTSPOT_PROFILER_MODULE = 'cpu-hotspot-profiler';\nconst RESOURCE_COUNTS_STATS = 'GPU Resource Counts';\nconst LEGACY_RESOURCE_COUNTS_STATS = 'Resource Counts';\nconst GPU_TIME_AND_MEMORY_STATS = 'GPU Time and Memory';\nconst BASE_RESOURCE_COUNT_ORDER = [\n 'Resources',\n 'Buffers',\n 'Textures',\n 'Samplers',\n 'TextureViews',\n 'Framebuffers',\n 'QuerySets',\n 'Shaders',\n 'RenderPipelines',\n 'ComputePipelines',\n 'PipelineLayouts',\n 'VertexArrays',\n 'RenderPasss',\n 'ComputePasss',\n 'CommandEncoders',\n 'CommandBuffers'\n] as const;\nconst WEBGL_RESOURCE_COUNT_ORDER = [\n 'Resources',\n 'Buffers',\n 'Textures',\n 'Samplers',\n 'TextureViews',\n 'Framebuffers',\n 'QuerySets',\n 'Shaders',\n 'RenderPipelines',\n 'SharedRenderPipelines',\n 'ComputePipelines',\n 'PipelineLayouts',\n 'VertexArrays',\n 'RenderPasss',\n 'ComputePasss',\n 'CommandEncoders',\n 'CommandBuffers'\n] as const;\nconst BASE_RESOURCE_COUNT_STAT_ORDER = BASE_RESOURCE_COUNT_ORDER.flatMap(resourceType => [\n `${resourceType} Created`,\n `${resourceType} Active`\n]);\nconst WEBGL_RESOURCE_COUNT_STAT_ORDER = WEBGL_RESOURCE_COUNT_ORDER.flatMap(resourceType => [\n `${resourceType} Created`,\n `${resourceType} Active`\n]);\nconst ORDERED_STATS_CACHE = new WeakMap<\n Stats,\n {orderedStatNames: readonly string[]; statCount: number}\n>();\nconst ORDERED_STAT_NAME_SET_CACHE = new WeakMap<readonly string[], Set<string>>();\n\ntype CpuHotspotProfiler = {\n enabled?: boolean;\n activeDefaultFramebufferAcquireDepth?: number;\n statsBookkeepingTimeMs?: number;\n statsBookkeepingCalls?: number;\n transientCanvasResourceCreates?: number;\n transientCanvasTextureCreates?: number;\n transientCanvasTextureViewCreates?: number;\n transientCanvasSamplerCreates?: number;\n transientCanvasFramebufferCreates?: number;\n};\n\nexport type ResourceProps = {\n /** Name of resource, mainly for debugging purposes. A unique name will be assigned if not provided */\n id?: string;\n /** Handle for the underlying resources (WebGL object or WebGPU handle) */\n handle?: any;\n /** User provided data stored on this resource */\n userData?: {[key: string]: any};\n};\n\n/**\n * Base class for GPU (WebGPU/WebGL) Resources\n */\nexport abstract class Resource<Props extends ResourceProps> {\n /** Default properties for resource */\n static defaultProps: Required<ResourceProps> = {\n id: 'undefined',\n handle: undefined,\n userData: undefined!\n };\n\n abstract get [Symbol.toStringTag](): string;\n\n toString(): string {\n return `${this[Symbol.toStringTag] || this.constructor.name}:\"${this.id}\"`;\n }\n\n /** props.id, for debugging. */\n id: string;\n /** The props that this resource was created with */\n readonly props: Required<Props>;\n /** User data object, reserved for the application */\n readonly userData: Record<string, unknown> = {};\n /** The device that this resource is associated with */\n abstract readonly device: Device;\n /** The handle for the underlying resource, e.g. WebGL object or WebGPU handle */\n abstract readonly handle: unknown;\n /** The device that this resource is associated with - TODO can we remove this dup? */\n private _device: Device;\n\n /** Whether this resource has been destroyed */\n destroyed: boolean = false;\n /** For resources that allocate GPU memory */\n private allocatedBytes: number = 0;\n /** Stats bucket currently holding the tracked allocation */\n private allocatedBytesName: string | null = null;\n /** Attached resources will be destroyed when this resource is destroyed. Tracks auto-created \"sub\" resources. */\n private _attachedResources = new Set<Resource<ResourceProps>>();\n\n /**\n * Create a new Resource. Called from Subclass\n */\n constructor(device: Device, props: Props, defaultProps: Required<Props>) {\n if (!device) {\n throw new Error('no device');\n }\n this._device = device;\n this.props = selectivelyMerge<Props>(props, defaultProps);\n\n const id =\n this.props.id !== 'undefined' ? (this.props.id as string) : uid(this[Symbol.toStringTag]);\n this.props.id = id;\n this.id = id;\n this.userData = this.props.userData || {};\n\n this.addStats();\n }\n\n /**\n * destroy can be called on any resource to release it before it is garbage collected.\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyResource();\n }\n\n /** @deprecated Use destroy() */\n delete(): this {\n this.destroy();\n return this;\n }\n\n /**\n * Combines a map of user props and default props, only including props from defaultProps\n * @returns returns a map of overridden default props\n */\n getProps(): object {\n return this.props;\n }\n\n // ATTACHED RESOURCES\n\n /**\n * Attaches a resource. Attached resources are auto destroyed when this resource is destroyed\n * Called automatically when sub resources are auto created but can be called by application\n */\n attachResource(resource: Resource<ResourceProps>): void {\n this._attachedResources.add(resource);\n }\n\n /**\n * Detach an attached resource. The resource will no longer be auto-destroyed when this resource is destroyed.\n */\n detachResource(resource: Resource<ResourceProps>): void {\n this._attachedResources.delete(resource);\n }\n\n /**\n * Destroys a resource (only if owned), and removes from the owned (auto-destroy) list for this resource.\n */\n destroyAttachedResource(resource: Resource<ResourceProps>): void {\n if (this._attachedResources.delete(resource)) {\n resource.destroy();\n }\n }\n\n /** Destroy all owned resources. Make sure the resources are no longer needed before calling. */\n destroyAttachedResources(): void {\n for (const resource of this._attachedResources) {\n resource.destroy();\n }\n // don't remove while we are iterating\n this._attachedResources = new Set<Resource<ResourceProps>>();\n }\n\n // PROTECTED METHODS\n\n /** Perform all destroy steps. Can be called by derived resources when overriding destroy() */\n protected destroyResource(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyAttachedResources();\n this.removeStats();\n this.destroyed = true;\n }\n\n /** Called by .destroy() to track object destruction. Subclass must call if overriding destroy() */\n protected removeStats(): void {\n const profiler = getCpuHotspotProfiler(this._device);\n const startTime = profiler ? getTimestamp() : 0;\n const statsObjects = [\n this._device.statsManager.getStats(RESOURCE_COUNTS_STATS),\n this._device.statsManager.getStats(LEGACY_RESOURCE_COUNTS_STATS)\n ];\n const orderedStatNames = getResourceCountStatOrder(this._device);\n for (const stats of statsObjects) {\n initializeStats(stats, orderedStatNames);\n }\n const name = this.getStatsName();\n for (const stats of statsObjects) {\n stats.get('Resources Active').decrementCount();\n stats.get(`${name}s Active`).decrementCount();\n }\n if (profiler) {\n profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;\n profiler.statsBookkeepingTimeMs =\n (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);\n }\n }\n\n /** Called by subclass to track memory allocations */\n protected trackAllocatedMemory(bytes: number, name = this.getStatsName()): void {\n const profiler = getCpuHotspotProfiler(this._device);\n const startTime = profiler ? getTimestamp() : 0;\n const stats = this._device.statsManager.getStats(GPU_TIME_AND_MEMORY_STATS);\n\n if (this.allocatedBytes > 0 && this.allocatedBytesName) {\n stats.get('GPU Memory').subtractCount(this.allocatedBytes);\n stats.get(`${this.allocatedBytesName} Memory`).subtractCount(this.allocatedBytes);\n }\n\n stats.get('GPU Memory').addCount(bytes);\n stats.get(`${name} Memory`).addCount(bytes);\n if (profiler) {\n profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;\n profiler.statsBookkeepingTimeMs =\n (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);\n }\n this.allocatedBytes = bytes;\n this.allocatedBytesName = name;\n }\n\n /** Called by subclass to track handle-backed memory allocations separately from owned allocations */\n protected trackReferencedMemory(bytes: number, name = this.getStatsName()): void {\n this.trackAllocatedMemory(bytes, `Referenced ${name}`);\n }\n\n /** Called by subclass to track memory deallocations */\n protected trackDeallocatedMemory(name = this.getStatsName()): void {\n if (this.allocatedBytes === 0) {\n this.allocatedBytesName = null;\n return;\n }\n\n const profiler = getCpuHotspotProfiler(this._device);\n const startTime = profiler ? getTimestamp() : 0;\n const stats = this._device.statsManager.getStats(GPU_TIME_AND_MEMORY_STATS);\n stats.get('GPU Memory').subtractCount(this.allocatedBytes);\n stats.get(`${this.allocatedBytesName || name} Memory`).subtractCount(this.allocatedBytes);\n if (profiler) {\n profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;\n profiler.statsBookkeepingTimeMs =\n (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);\n }\n this.allocatedBytes = 0;\n this.allocatedBytesName = null;\n }\n\n /** Called by subclass to deallocate handle-backed memory tracked via trackReferencedMemory() */\n protected trackDeallocatedReferencedMemory(name = this.getStatsName()): void {\n this.trackDeallocatedMemory(`Referenced ${name}`);\n }\n\n /** Called by resource constructor to track object creation */\n private addStats(): void {\n const name = this.getStatsName();\n const profiler = getCpuHotspotProfiler(this._device);\n const startTime = profiler ? getTimestamp() : 0;\n const statsObjects = [\n this._device.statsManager.getStats(RESOURCE_COUNTS_STATS),\n this._device.statsManager.getStats(LEGACY_RESOURCE_COUNTS_STATS)\n ];\n const orderedStatNames = getResourceCountStatOrder(this._device);\n for (const stats of statsObjects) {\n initializeStats(stats, orderedStatNames);\n }\n for (const stats of statsObjects) {\n stats.get('Resources Created').incrementCount();\n stats.get('Resources Active').incrementCount();\n stats.get(`${name}s Created`).incrementCount();\n stats.get(`${name}s Active`).incrementCount();\n }\n if (profiler) {\n profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;\n profiler.statsBookkeepingTimeMs =\n (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);\n }\n recordTransientCanvasResourceCreate(this._device, name);\n }\n\n /** Canonical resource name used for stats buckets. */\n protected getStatsName(): string {\n return getCanonicalResourceName(this);\n }\n}\n\n/**\n * Combines a map of user props and default props, only including props from defaultProps\n * @param props\n * @param defaultProps\n * @returns returns a map of overridden default props\n */\nfunction selectivelyMerge<Props>(props: Props, defaultProps: Required<Props>): Required<Props> {\n const mergedProps = {...defaultProps};\n for (const key in props) {\n if (props[key] !== undefined) {\n mergedProps[key] = props[key];\n }\n }\n return mergedProps;\n}\n\nfunction initializeStats(stats: Stats, orderedStatNames: readonly string[]): void {\n const statsMap = stats.stats;\n let addedOrderedStat = false;\n for (const statName of orderedStatNames) {\n if (!statsMap[statName]) {\n stats.get(statName);\n addedOrderedStat = true;\n }\n }\n\n const statCount = Object.keys(statsMap).length;\n const cachedStats = ORDERED_STATS_CACHE.get(stats);\n if (\n !addedOrderedStat &&\n cachedStats?.orderedStatNames === orderedStatNames &&\n cachedStats.statCount === statCount\n ) {\n return;\n }\n\n const reorderedStats: Record<string, Stat> = {};\n let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE.get(orderedStatNames);\n if (!orderedStatNamesSet) {\n orderedStatNamesSet = new Set(orderedStatNames);\n ORDERED_STAT_NAME_SET_CACHE.set(orderedStatNames, orderedStatNamesSet);\n }\n\n for (const statName of orderedStatNames) {\n if (statsMap[statName]) {\n reorderedStats[statName] = statsMap[statName];\n }\n }\n\n for (const [statName, stat] of Object.entries(statsMap)) {\n if (!orderedStatNamesSet.has(statName)) {\n reorderedStats[statName] = stat;\n }\n }\n\n for (const statName of Object.keys(statsMap)) {\n delete statsMap[statName];\n }\n\n Object.assign(statsMap, reorderedStats);\n ORDERED_STATS_CACHE.set(stats, {orderedStatNames, statCount});\n}\n\nfunction getResourceCountStatOrder(device: Device): readonly string[] {\n return device.type === 'webgl' ? WEBGL_RESOURCE_COUNT_STAT_ORDER : BASE_RESOURCE_COUNT_STAT_ORDER;\n}\n\nfunction getCpuHotspotProfiler(device: Device): CpuHotspotProfiler | null {\n const profiler = device.userData[CPU_HOTSPOT_PROFILER_MODULE] as CpuHotspotProfiler | undefined;\n return profiler?.enabled ? profiler : null;\n}\n\nfunction getTimestamp(): number {\n return globalThis.performance?.now?.() ?? Date.now();\n}\n\nfunction recordTransientCanvasResourceCreate(device: Device, name: string): void {\n const profiler = getCpuHotspotProfiler(device);\n if (!profiler || !profiler.activeDefaultFramebufferAcquireDepth) {\n return;\n }\n\n profiler.transientCanvasResourceCreates = (profiler.transientCanvasResourceCreates || 0) + 1;\n\n switch (name) {\n case 'Texture':\n profiler.transientCanvasTextureCreates = (profiler.transientCanvasTextureCreates || 0) + 1;\n break;\n case 'TextureView':\n profiler.transientCanvasTextureViewCreates =\n (profiler.transientCanvasTextureViewCreates || 0) + 1;\n break;\n case 'Sampler':\n profiler.transientCanvasSamplerCreates = (profiler.transientCanvasSamplerCreates || 0) + 1;\n break;\n case 'Framebuffer':\n profiler.transientCanvasFramebufferCreates =\n (profiler.transientCanvasFramebufferCreates || 0) + 1;\n break;\n default:\n break;\n }\n}\n\nfunction getCanonicalResourceName(resource: Resource<any>): string {\n let prototype = Object.getPrototypeOf(resource);\n\n while (prototype) {\n const parentPrototype = Object.getPrototypeOf(prototype);\n if (!parentPrototype || parentPrototype === Resource.prototype) {\n return (\n getPrototypeToStringTag(prototype) ||\n resource[Symbol.toStringTag] ||\n resource.constructor.name\n );\n }\n prototype = parentPrototype;\n }\n\n return resource[Symbol.toStringTag] || resource.constructor.name;\n}\n\nfunction getPrototypeToStringTag(prototype: object): string | null {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, Symbol.toStringTag);\n if (typeof descriptor?.get === 'function') {\n return descriptor.get.call(prototype);\n }\n if (typeof descriptor?.value === 'string') {\n return descriptor.value;\n }\n return null;\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {Device} from '../device';\nimport {Resource, ResourceProps} from './resource';\n\n/** Callback for Buffer.mapAndReadAsync */\nexport type BufferMapCallback<T> = (arrayBuffer: ArrayBuffer, lifetime: 'mapped' | 'copied') => T;\n\nexport type BufferProps = ResourceProps & {\n /** Supply a handle to connect to an existing device-specific buffer */\n handle?: WebGLBuffer;\n /** Specifies how this buffer can be used */\n usage?: number;\n /** Length in bytes of memory to be allocated. If not specified, `byteLength` of `props.data` will be used. */\n byteLength?: number;\n /** Byte offset into the newly created Buffer to store data at */\n byteOffset?: number;\n /** If props.usage includes Buffer.INDEX. Note: uint8 indices are automatically converted to uint16 for WebGPU compatibility */\n indexType?: 'uint8' | 'uint16' | 'uint32';\n /** Data to initialize the buffer with. */\n data?: ArrayBuffer | ArrayBufferView | null;\n /** Callback to initialize data without copy */\n onMapped?: BufferMapCallback<void>;\n};\n\n/** Abstract GPU buffer */\nexport abstract class Buffer extends Resource<BufferProps> {\n /** Index buffer */\n static INDEX = 0x0010;\n /** Vertex buffer */\n static VERTEX = 0x0020;\n /** Uniform buffer */\n static UNIFORM = 0x0040;\n /** Storage buffer */\n static STORAGE = 0x0080;\n static INDIRECT = 0x0100;\n static QUERY_RESOLVE = 0x0200;\n\n // Usage Flags\n static MAP_READ = 0x01;\n static MAP_WRITE = 0x02;\n static COPY_SRC = 0x0004;\n static COPY_DST = 0x0008;\n\n override get [Symbol.toStringTag](): string {\n return 'Buffer';\n }\n\n /** The usage with which this buffer was created */\n readonly usage: number;\n /** For index buffers, whether indices are 8, 16 or 32 bit. Note: uint8 indices are automatically converted to uint16 for WebGPU compatibility */\n readonly indexType?: 'uint8' | 'uint16' | 'uint32';\n /** Length of buffer in bytes */\n abstract byteLength: number;\n /** \"Time\" of last update, can be used to check if redraw is needed */\n updateTimestamp: number;\n\n constructor(device: Device, props: BufferProps) {\n const deducedProps = {...props};\n\n // Deduce indexType\n if ((props.usage || 0) & Buffer.INDEX && !props.indexType) {\n if (props.data instanceof Uint32Array) {\n deducedProps.indexType = 'uint32';\n } else if (props.data instanceof Uint16Array) {\n deducedProps.indexType = 'uint16';\n } else if (props.data instanceof Uint8Array) {\n deducedProps.indexType = 'uint8';\n }\n }\n\n // Remove data from props before storing, we don't want to hold on to a big chunk of memory\n delete deducedProps.data;\n\n super(device, deducedProps, Buffer.defaultProps);\n\n this.usage = deducedProps.usage || 0;\n this.indexType = deducedProps.indexType;\n\n // TODO - perhaps this should be set on async write completion?\n this.updateTimestamp = device.incrementTimestamp();\n }\n\n /**\n * Create a copy of this Buffer with new byteLength, with same props but of the specified size.\n * @note Does not copy contents of the cloned Buffer.\n */\n clone(props: {byteLength: number}): Buffer {\n return this.device.createBuffer({...this.props, ...props});\n }\n\n /** Write data to buffer */\n abstract write(\n data: ArrayBufferLike | ArrayBufferView | SharedArrayBuffer,\n byteOffset?: number\n ): void;\n\n abstract mapAndWriteAsync(\n onMapped: BufferMapCallback<void | Promise<void>>,\n byteOffset?: number,\n byteLength?: number\n ): Promise<void>;\n\n /** Reads data asynchronously, returns a copy of the buffer data */\n abstract readAsync(byteOffset?: number, byteLength?: number): Promise<Uint8Array>;\n\n /** Maps buffer data to CPU memory. Mapped memory is only accessible in the callback */\n abstract mapAndReadAsync<T>(\n onMapped: BufferMapCallback<T>,\n byteOffset?: number,\n byteLength?: number\n ): Promise<T>;\n\n /** Read data synchronously. @note WebGL2 only */\n abstract readSyncWebGL(byteOffset?: number, byteLength?: number): Uint8Array;\n\n // PROTECTED METHODS (INTENDED FOR USE BY OTHER FRAMEWORK CODE ONLY)\n\n /** Max amount of debug data saved. Two vec4's */\n static DEBUG_DATA_MAX_LENGTH = 32;\n\n /** A partial CPU-side copy of the data in this buffer, for debugging purposes */\n debugData: ArrayBuffer = new ArrayBuffer(0);\n\n /** This doesn't handle partial non-zero offset updates correctly */\n protected _setDebugData(\n data: ArrayBufferView | ArrayBufferLike | null,\n _byteOffset: number,\n byteLength: number\n ): void {\n let arrayBufferView: ArrayBufferView | null = null;\n let arrayBuffer: ArrayBufferLike | null;\n if (ArrayBuffer.isView(data)) {\n arrayBufferView = data;\n arrayBuffer = data.buffer;\n } else {\n arrayBuffer = data;\n }\n const debugDataLength = Math.min(\n data ? data.byteLength : byteLength,\n Buffer.DEBUG_DATA_MAX_LENGTH\n );\n if (arrayBuffer === null) {\n this.debugData = new ArrayBuffer(debugDataLength);\n } else {\n const sourceByteOffset = Math.min(arrayBufferView?.byteOffset || 0, arrayBuffer.byteLength);\n const availableByteLength = Math.max(0, arrayBuffer.byteLength - sourceByteOffset);\n const copyByteLength = Math.min(debugDataLength, availableByteLength);\n this.debugData = new Uint8Array(arrayBuffer, sourceByteOffset, copyByteLength).slice().buffer;\n }\n }\n\n static override defaultProps: Required<BufferProps> = {\n ...Resource.defaultProps,\n usage: 0, // Buffer.COPY_DST | Buffer.COPY_SRC\n byteLength: 0,\n byteOffset: 0,\n data: null,\n indexType: 'uint16',\n onMapped: undefined!\n };\n}\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {TypedArray, TypedArrayConstructor} from '../../types';\nimport {\n PrimitiveDataType,\n SignedDataType,\n NormalizedDataType,\n DataTypeInfo,\n TypedArrayConstructorT\n} from './data-types';\n\nexport class DataTypeDecoder {\n /**\n * Gets info about a data type constant (signed or normalized)\n * @returns underlying primitive / signed types, byte length, normalization, integer, signed flags\n */\n getDataTypeInfo<T extends NormalizedDataType = NormalizedDataType>(type: T): DataTypeInfo<T> {\n const [signedType, primitiveType, byteLength] = NORMALIZED_TYPE_MAP[type];\n const normalized: boolean = type.includes('norm');\n const integer: boolean = !normalized && !type.startsWith('float');\n const signed: boolean = type.startsWith('s');\n return {\n signedType: signedType as DataTypeInfo<T>['signedType'],\n primitiveType: primitiveType as DataTypeInfo<T>['primitiveType'],\n byteLength: byteLength as DataTypeInfo<T>['byteLength'],\n normalized: normalized as DataTypeInfo<T>['normalized'],\n integer: integer as DataTypeInfo<T>['integer'],\n signed: signed as DataTypeInfo<T>['signed']\n // TODO - add webglOnly flag\n };\n }\n\n /** Build a vertex format from a signed data type and a component */\n getNormalizedDataType(signedDataType: SignedDataType): NormalizedDataType {\n const dataType: NormalizedDataType = signedDataType;\n // biome-ignore format: preserve layout\n switch (dataType) {\n case 'uint8': return 'unorm8';\n case 'sint8': return 'snorm8';\n case 'uint16': return 'unorm16';\n case 'sint16': return 'snorm16';\n default: return dataType;\n }\n }\n\n /** Align offset to 1, 2 or 4 elements (4, 8 or 16 bytes) */\n alignTo(size: number, count: number): number {\n // biome-ignore format: preserve layout\n switch (count) {\n case 1: return size; // Pad upwards to even multiple of 2\n case 2: return size + (size % 2); // Pad upwards to even multiple of 2\n default: return size + ((4 - (size % 4)) % 4); // Pad upwards to even multiple of 4\n }\n }\n\n /** Returns the VariableShaderType that corresponds to a typed array */\n getDataType(arrayOrType: TypedArray | TypedArrayConstructor): SignedDataType {\n const Constructor = ArrayBuffer.isView(arrayOrType) ? arrayOrType.constructor : arrayOrType;\n if (Constructor === Uint8ClampedArray) {\n return 'uint8';\n }\n const info = Object.values(NORMALIZED_TYPE_MAP).find(entry => Constructor === entry[4]);\n if (!info) {\n throw new Error(Constructor.name);\n }\n return info[0];\n }\n\n /** Returns the TypedArray that corresponds to a shader data type */\n getTypedArrayConstructor<T extends NormalizedDataType>(\n type: NormalizedDataType\n ): TypedArrayConstructorT<T> {\n const [, , , , Constructor] = NORMALIZED_TYPE_MAP[type];\n return Constructor as unknown as TypedArrayConstructorT<T>;\n }\n}\n\n/** Entry point for decoding luma.gl data types */\nexport const dataTypeDecoder = new DataTypeDecoder();\n\nconst NORMALIZED_TYPE_MAP = {\n uint8: ['uint8', 'u32', 1, false, Uint8Array],\n sint8: ['sint8', 'i32', 1, false, Int8Array],\n unorm8: ['uint8', 'f32', 1, true, Uint8Array],\n snorm8: ['sint8', 'f32', 1, true, Int8Array],\n uint16: ['uint16', 'u32', 2, false, Uint16Array],\n sint16: ['sint16', 'i32', 2, false, Int16Array],\n unorm16: ['uint16', 'u32', 2, true, Uint16Array],\n snorm16: ['sint16', 'i32', 2, true, Int16Array],\n float16: ['float16', 'f16', 2, false, Uint16Array],\n float32: ['float32', 'f32', 4, false, Float32Array],\n uint32: ['uint32', 'u32', 4, false, Uint32Array],\n sint32: ['sint32', 'i32', 4, false, Int32Array]\n} satisfies Record<\n NormalizedDataType,\n [\n SignedDataType,\n PrimitiveDataType,\n bytes: 1 | 2 | 4,\n normalized: boolean,\n arrayConstructor: TypedArrayConstructor\n ]\n>;\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {TypedArray} from '../../types';\nimport type {NormalizedDataType, PrimitiveDataType, SignedDataType} from '../data-types/data-types';\nimport type {VertexFormat, VertexFormatInfo} from './vertex-formats';\nimport {dataTypeDecoder} from '../data-types/data-type-decoder';\n\nexport class VertexFormatDecoder {\n /**\n * Decodes a vertex format, returning type, components, byte length and flags (integer, signed, normalized)\n */\n getVertexFormatInfo<T extends VertexFormat = VertexFormat>(format: T): VertexFormatInfo<T> {\n // Strip the -webgl ending if present\n let webglOnly: boolean | undefined;\n if (format.endsWith('-webgl')) {\n format.replace('-webgl', '');\n webglOnly = true;\n }\n // split components from type\n const [type_, count] = format.split('x');\n const type = type_ as NormalizedDataType;\n const components = (count ? parseInt(count) : 1) as 1 | 2 | 3 | 4;\n // decode the type\n const decodedType = dataTypeDecoder.getDataTypeInfo(type);\n const result: VertexFormatInfo = {\n type,\n components,\n byteLength: decodedType.byteLength * components,\n integer: decodedType.integer,\n signed: decodedType.signed,\n normalized: decodedType.normalized\n };\n if (webglOnly) {\n result.webglOnly = true;\n }\n return result;\n }\n\n /** Build a vertex format from a signed data type and a component */\n makeVertexFormat(\n signedDataType: SignedDataType,\n components: 1 | 2 | 3 | 4,\n normalized?: boolean\n ): VertexFormat {\n const dataType: NormalizedDataType = normalized\n ? dataTypeDecoder.getNormalizedDataType(signedDataType)\n : signedDataType;\n\n switch (dataType) {\n // Special cases for WebGL-only x3 formats that WebGPU does not support.\n case 'unorm8':\n if (components === 1) {\n return 'unorm8';\n }\n if (components === 3) {\n return 'unorm8x3-webgl';\n }\n return `${dataType}x${components}`;\n\n case 'snorm8':\n if (components === 1) {\n return 'snorm8';\n }\n if (components === 3) {\n return 'snorm8x3-webgl';\n }\n return `${dataType}x${components}`;\n\n case 'uint8':\n case 'sint8':\n // WebGPU 8 bit formats must be aligned to 16 bit boundaries.\n if (components === 1 || components === 3) {\n throw new Error(`size: ${components}`);\n }\n return `${dataType}x${components}`;\n\n case 'uint16':\n if (components === 1) {\n return 'uint16';\n }\n if (components === 3) {\n return 'uint16x3-webgl';\n }\n return `${dataType}x${components}`;\n\n case 'sint16':\n if (components === 1) {\n return 'sint16';\n }\n if (components === 3) {\n return 'sint16x3-webgl';\n }\n return `${dataType}x${components}`;\n\n case 'unorm16':\n if (components === 1) {\n return 'unorm16';\n }\n if (components === 3) {\n return 'unorm16x3-webgl';\n }\n return `${dataType}x${components}`;\n\n case 'snorm16':\n if (components === 1) {\n return 'snorm16';\n }\n if (components === 3) {\n return 'snorm16x3-webgl';\n }\n return `${dataType}x${components}`;\n\n case 'float16':\n // WebGPU 16 bit formats must be aligned to 32 bit boundaries\n if (components === 1 || components === 3) {\n throw new Error(`size: ${components}`);\n }\n return `${dataType}x${components}`;\n\n default:\n return components === 1 ? dataType : `${dataType}x${components}`;\n }\n }\n\n /** Get the vertex format for an attribute with TypedArray and size */\n getVertexFormatFromAttribute(\n typedArray: TypedArray,\n size: number,\n normalized?: boolean\n ): VertexFormat {\n if (!size || size > 4) {\n throw new Error(`size ${size}`);\n }\n\n const components = size as 1 | 2 | 3 | 4;\n const signedDataType = dataTypeDecoder.getDataType(typedArray);\n return this.makeVertexFormat(signedDataType, components, normalized);\n }\n\n /**\n * Return a \"default\" vertex format for a certain shader data type\n * The simplest vertex format that matches the shader attribute's data type\n */\n\n getCompatibleVertexFormat(opts: {\n primitiveType: PrimitiveDataType;\n components: 1 | 2 | 3 | 4;\n }): VertexFormat {\n let vertexType: NormalizedDataType;\n switch (opts.primitiveType) {\n case 'f32':\n vertexType = 'float32';\n break;\n case 'i32':\n vertexType = 'sint32';\n break;\n case 'u32':\n vertexType = 'uint32';\n break;\n case 'f16':\n return opts.components <= 2 ? 'float16x2' : 'float16x4';\n }\n\n // TODO logic does not work for float16\n if (opts.components === 1) {\n return vertexType;\n }\n return `${vertexType}x${opts.components}`;\n }\n}\n\n/** Decoder for luma.gl vertex types */\nexport const vertexFormatDecoder = new VertexFormatDecoder();\n", "// luma.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {\n TextureFormat,\n TextureFormatColorUncompressed,\n TextureFormatDepthStencil,\n TextureFeature,\n TextureFormatInfo,\n TextureFormatCompressed\n} from './texture-formats';\n/* eslint-disable camelcase */\n\n// Define local device feature strings to optimize minification\nconst texture_compression_bc: TextureFeature = 'texture-compression-bc';\nconst texture_compression_astc: TextureFeature = 'texture-compression-astc';\nconst texture_compression_etc2: TextureFeature = 'texture-compression-etc2';\nconst texture_compression_etc1_webgl: TextureFeature = 'texture-compression-etc1-webgl';\nconst texture_compression_pvrtc_webgl: TextureFeature = 'texture-compression-pvrtc-webgl';\nconst texture_compression_atc_webgl: TextureFeature = 'texture-compression-atc-webgl';\n\nconst float32_renderable: TextureFeature = 'float32-renderable-webgl';\nconst float16_renderable: TextureFeature = 'float16-renderable-webgl';\nconst rgb9e5ufloat_renderable: TextureFeature = 'rgb9e5ufloat-renderable-webgl';\nconst snorm8_renderable: TextureFeature = 'snorm8-renderable-webgl';\nconst norm16_webgl: TextureFeature = 'norm16-webgl';\nconst norm16_renderable: TextureFeature = 'norm16-renderable-webgl';\nconst snorm16_renderable: TextureFeature = 'snorm16-renderable-webgl';\n\nconst float32_filterable: TextureFeature = 'float32-filterable';\nconst float16_filterable: TextureFeature = 'float16-filterable-webgl';\n\n/** https://www.w3.org/TR/webgpu/#texture-format-caps */\n\n/** Internal type representing texture capabilities */\ntype TextureFormatDefinition = Partial<TextureFormatInfo> & {\n /** for compressed texture formats */\n f?: TextureFeature;\n /** renderable if feature is present. false means the spec does not support this format */\n render?: TextureFeature | false;\n /** filterable if feature is present. false means the spec does not support this format */\n filter?: TextureFeature | false;\n blend?: TextureFeature | false;\n store?: TextureFeature | false;\n\n /** (bytes per pixel), for memory usage calculations. */\n b?: number;\n /** channels */\n c?: number;\n bpp?: number;\n /** packed */\n p?: number;\n\n /** If not supported on WebGPU */\n wgpu?: false;\n};\n\nexport function getTextureFormatDefinition(format: TextureFormat): TextureFormatDefinition {\n const info = TEXTURE_FORMAT_TABLE[format];\n if (!info) {\n throw new Error(`Unsupported texture format ${format}`);\n }\n return info;\n}\n\nexport function getTextureFormatTable(): Readonly<Record<TextureFormat, TextureFormatDefinition>> {\n return TEXTURE_FORMAT_TABLE;\n}\n\n// biome-ignore format: preserve layout\nconst TEXTURE_FORMAT_COLOR_DEPTH_TABLE: Readonly<Record<TextureFormatColorUncompressed | TextureFormatDepthStencil, TextureFormatDefinition>> = {\n // 8-bit formats\n 'r8unorm': {},\n 'rg8unorm': {},\n 'rgb8unorm-webgl': {},\n 'rgba8unorm': {},\n 'rgba8unorm-srgb': {},\n\n 'r8snorm': {render: snorm8_renderable},\n 'rg8snorm': {render: snorm8_renderable},\n 'rgb8snorm-webgl': {},\n 'rgba8snorm': {render: snorm8_renderable},\n\n 'r8uint': {},\n 'rg8uint': {},\n 'rgba8uint': {},\n\n 'r8sint': {},\n 'rg8sint': {},\n 'rgba8sint': {},\n\n 'bgra8unorm': {},\n 'bgra8unorm-srgb': {},\n\n\n 'r16unorm': {f: norm16_webgl, render: norm16_renderable},\n 'rg16unorm': {f: norm16_webgl, render: norm16_renderable},\n 'rgb16unorm-webgl': {f: norm16_webgl, render: false}, // rgb not renderable\n 'rgba16unorm': {f: norm16_webgl, render: norm16_renderable},\n\n 'r16snorm': {f: norm16_webgl, render: snorm16_renderable},\n 'rg16snorm': {f: norm16_webgl, render: snorm16_renderable},\n 'rgb16snorm-webgl': {f: norm16_webgl, render: false}, // rgb not renderable\n 'rgba16snorm': {f: norm16_webgl, render: snorm16_renderable},\n\n 'r16uint': {},\n 'rg16uint': {},\n 'rgba16uint': {},\n\n 'r16sint': {},\n 'rg16sint': {},\n 'rgba16sint': {},\n\n 'r16float': {render: float16_renderable, filter: 'float16-filterable-webgl'},\n 'rg16float': {render: float16_renderable, filter: float16_filterable},\n 'rgba16float': {render: float16_renderable, filter: float16_filterable},\n\n 'r32uint': {},\n 'rg32uint': {},\n 'rgba32uint': {},\n\n 'r32sint': {},\n 'rg32sint': {},\n 'rgba32sint': {},\n\n 'r32float': {render: float32_renderable, filter: float32_filterable},\n 'rg32float': {render: false, filter: float32_filterable},\n 'rgb32float-webgl': {render: float32_renderable, filter: float32_filterable},\n 'rgba32float': {render: float32_renderable, filter: float32_filterable},\n\n