UNPKG

@luma.gl/core

Version:

The luma.gl core Device API

1,321 lines (1,309 loc) 184 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // dist/index.js var dist_exports = {}; __export(dist_exports, { Adapter: () => Adapter, Buffer: () => Buffer2, CanvasContext: () => CanvasContext, CommandBuffer: () => CommandBuffer, CommandEncoder: () => CommandEncoder, ComputePass: () => ComputePass, ComputePipeline: () => ComputePipeline, Device: () => Device, DeviceFeatures: () => DeviceFeatures, DeviceLimits: () => DeviceLimits, ExternalTexture: () => ExternalTexture, Fence: () => Fence, Framebuffer: () => Framebuffer, PipelineFactory: () => PipelineFactory, PipelineLayout: () => PipelineLayout, PresentationContext: () => PresentationContext, QuerySet: () => QuerySet, RenderPass: () => RenderPass, RenderPipeline: () => RenderPipeline, Resource: () => Resource, Sampler: () => Sampler, Shader: () => Shader, ShaderBlockWriter: () => ShaderBlockWriter, ShaderFactory: () => ShaderFactory, SharedRenderPipeline: () => SharedRenderPipeline, Texture: () => Texture, TextureView: () => TextureView, TransformFeedback: () => TransformFeedback, UniformBlock: () => UniformBlock, UniformStore: () => UniformStore, VertexArray: () => VertexArray, _getDefaultBindGroupFactory: () => _getDefaultBindGroupFactory, _getTextureFormatDefinition: () => getTextureFormatDefinition, _getTextureFormatTable: () => getTextureFormatTable, assert: () => assert, assertDefined: () => assertDefined, dataTypeDecoder: () => dataTypeDecoder, flattenBindingsByGroup: () => flattenBindingsByGroup, getAttributeInfosFromLayouts: () => getAttributeInfosFromLayouts, getAttributeShaderTypeInfo: () => getAttributeShaderTypeInfo, getExternalImageSize: () => getExternalImageSize, getScratchArray: () => getScratchArray, getShaderLayoutBinding: () => getShaderLayoutBinding, getTextureImageView: () => getTextureImageView, getTypedArrayConstructor: () => getTypedArrayConstructor, getVariableShaderTypeInfo: () => getVariableShaderTypeInfo, isExternalImage: () => isExternalImage, log: () => log, luma: () => luma, makeShaderBlockLayout: () => makeShaderBlockLayout, normalizeBindingsByGroup: () => normalizeBindingsByGroup, readPixel: () => readPixel, setTextureImageData: () => setTextureImageData, shaderTypeDecoder: () => shaderTypeDecoder, textureFormatDecoder: () => textureFormatDecoder, vertexFormatDecoder: () => vertexFormatDecoder, writePixel: () => writePixel }); module.exports = __toCommonJS(dist_exports); // dist/utils/stats-manager.js var import_stats = require("@probe.gl/stats"); var GPU_TIME_AND_MEMORY_STATS = "GPU Time and Memory"; var GPU_TIME_AND_MEMORY_STAT_ORDER = [ "Adapter", "GPU", "GPU Type", "GPU Backend", "Frame Rate", "CPU Time", "GPU Time", "GPU Memory", "Buffer Memory", "Texture Memory", "Referenced Buffer Memory", "Referenced Texture Memory", "Swap Chain Texture" ]; var ORDERED_STATS_CACHE = /* @__PURE__ */ new WeakMap(); var ORDERED_STAT_NAME_SET_CACHE = /* @__PURE__ */ new WeakMap(); var StatsManager = class { stats = /* @__PURE__ */ new Map(); getStats(name2) { return this.get(name2); } get(name2) { if (!this.stats.has(name2)) { this.stats.set(name2, new import_stats.Stats({ id: name2 })); } const stats = this.stats.get(name2); if (name2 === GPU_TIME_AND_MEMORY_STATS) { initializeStats(stats, GPU_TIME_AND_MEMORY_STAT_ORDER); } return stats; } }; var lumaStats = new StatsManager(); function initializeStats(stats, orderedStatNames) { const statsMap = stats.stats; let addedOrderedStat = false; for (const statName of orderedStatNames) { if (!statsMap[statName]) { stats.get(statName); addedOrderedStat = true; } } const statCount = Object.keys(statsMap).length; const cachedStats = ORDERED_STATS_CACHE.get(stats); if (!addedOrderedStat && (cachedStats == null ? void 0 : cachedStats.orderedStatNames) === orderedStatNames && cachedStats.statCount === statCount) { return; } const reorderedStats = {}; let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE.get(orderedStatNames); if (!orderedStatNamesSet) { orderedStatNamesSet = new Set(orderedStatNames); ORDERED_STAT_NAME_SET_CACHE.set(orderedStatNames, orderedStatNamesSet); } for (const statName of orderedStatNames) { if (statsMap[statName]) { reorderedStats[statName] = statsMap[statName]; } } for (const [statName, stat] of Object.entries(statsMap)) { if (!orderedStatNamesSet.has(statName)) { reorderedStats[statName] = stat; } } for (const statName of Object.keys(statsMap)) { delete statsMap[statName]; } Object.assign(statsMap, reorderedStats); ORDERED_STATS_CACHE.set(stats, { orderedStatNames, statCount }); } // dist/utils/log.js var import_log = require("@probe.gl/log"); var log = new import_log.Log({ id: "luma.gl" }); // dist/utils/uid.js var uidCounters = {}; function uid(id = "id") { uidCounters[id] = uidCounters[id] || 1; const count = uidCounters[id]++; return `${id}-${count}`; } // dist/adapter/resources/resource.js var CPU_HOTSPOT_PROFILER_MODULE = "cpu-hotspot-profiler"; var RESOURCE_COUNTS_STATS = "GPU Resource Counts"; var LEGACY_RESOURCE_COUNTS_STATS = "Resource Counts"; var GPU_TIME_AND_MEMORY_STATS2 = "GPU Time and Memory"; var BASE_RESOURCE_COUNT_ORDER = [ "Resources", "Buffers", "Textures", "Samplers", "TextureViews", "Framebuffers", "QuerySets", "Shaders", "RenderPipelines", "ComputePipelines", "PipelineLayouts", "VertexArrays", "RenderPasss", "ComputePasss", "CommandEncoders", "CommandBuffers" ]; var WEBGL_RESOURCE_COUNT_ORDER = [ "Resources", "Buffers", "Textures", "Samplers", "TextureViews", "Framebuffers", "QuerySets", "Shaders", "RenderPipelines", "SharedRenderPipelines", "ComputePipelines", "PipelineLayouts", "VertexArrays", "RenderPasss", "ComputePasss", "CommandEncoders", "CommandBuffers" ]; var BASE_RESOURCE_COUNT_STAT_ORDER = BASE_RESOURCE_COUNT_ORDER.flatMap((resourceType) => [ `${resourceType} Created`, `${resourceType} Active` ]); var WEBGL_RESOURCE_COUNT_STAT_ORDER = WEBGL_RESOURCE_COUNT_ORDER.flatMap((resourceType) => [ `${resourceType} Created`, `${resourceType} Active` ]); var ORDERED_STATS_CACHE2 = /* @__PURE__ */ new WeakMap(); var ORDERED_STAT_NAME_SET_CACHE2 = /* @__PURE__ */ new WeakMap(); var Resource = class { toString() { return `${this[Symbol.toStringTag] || this.constructor.name}:"${this.id}"`; } /** props.id, for debugging. */ id; /** The props that this resource was created with */ props; /** User data object, reserved for the application */ userData = {}; /** The device that this resource is associated with - TODO can we remove this dup? */ _device; /** Whether this resource has been destroyed */ destroyed = false; /** For resources that allocate GPU memory */ allocatedBytes = 0; /** Stats bucket currently holding the tracked allocation */ allocatedBytesName = null; /** Attached resources will be destroyed when this resource is destroyed. Tracks auto-created "sub" resources. */ _attachedResources = /* @__PURE__ */ new Set(); /** * Create a new Resource. Called from Subclass */ constructor(device, props, defaultProps) { if (!device) { throw new Error("no device"); } this._device = device; this.props = selectivelyMerge(props, defaultProps); const id = this.props.id !== "undefined" ? this.props.id : uid(this[Symbol.toStringTag]); this.props.id = id; this.id = id; this.userData = this.props.userData || {}; this.addStats(); } /** * destroy can be called on any resource to release it before it is garbage collected. */ destroy() { if (this.destroyed) { return; } this.destroyResource(); } /** @deprecated Use destroy() */ delete() { this.destroy(); return this; } /** * Combines a map of user props and default props, only including props from defaultProps * @returns returns a map of overridden default props */ getProps() { return this.props; } // ATTACHED RESOURCES /** * Attaches a resource. Attached resources are auto destroyed when this resource is destroyed * Called automatically when sub resources are auto created but can be called by application */ attachResource(resource) { this._attachedResources.add(resource); } /** * Detach an attached resource. The resource will no longer be auto-destroyed when this resource is destroyed. */ detachResource(resource) { this._attachedResources.delete(resource); } /** * Destroys a resource (only if owned), and removes from the owned (auto-destroy) list for this resource. */ destroyAttachedResource(resource) { if (this._attachedResources.delete(resource)) { resource.destroy(); } } /** Destroy all owned resources. Make sure the resources are no longer needed before calling. */ destroyAttachedResources() { for (const resource of this._attachedResources) { resource.destroy(); } this._attachedResources = /* @__PURE__ */ new Set(); } // PROTECTED METHODS /** Perform all destroy steps. Can be called by derived resources when overriding destroy() */ destroyResource() { if (this.destroyed) { return; } this.destroyAttachedResources(); this.removeStats(); this.destroyed = true; } /** Called by .destroy() to track object destruction. Subclass must call if overriding destroy() */ removeStats() { const profiler = getCpuHotspotProfiler(this._device); const startTime = profiler ? getTimestamp() : 0; const statsObjects = [ this._device.statsManager.getStats(RESOURCE_COUNTS_STATS), this._device.statsManager.getStats(LEGACY_RESOURCE_COUNTS_STATS) ]; const orderedStatNames = getResourceCountStatOrder(this._device); for (const stats of statsObjects) { initializeStats2(stats, orderedStatNames); } const name2 = this.getStatsName(); for (const stats of statsObjects) { stats.get("Resources Active").decrementCount(); stats.get(`${name2}s Active`).decrementCount(); } if (profiler) { profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1; profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime); } } /** Called by subclass to track memory allocations */ trackAllocatedMemory(bytes, name2 = this.getStatsName()) { const profiler = getCpuHotspotProfiler(this._device); const startTime = profiler ? getTimestamp() : 0; const stats = this._device.statsManager.getStats(GPU_TIME_AND_MEMORY_STATS2); if (this.allocatedBytes > 0 && this.allocatedBytesName) { stats.get("GPU Memory").subtractCount(this.allocatedBytes); stats.get(`${this.allocatedBytesName} Memory`).subtractCount(this.allocatedBytes); } stats.get("GPU Memory").addCount(bytes); stats.get(`${name2} Memory`).addCount(bytes); if (profiler) { profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1; profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime); } this.allocatedBytes = bytes; this.allocatedBytesName = name2; } /** Called by subclass to track handle-backed memory allocations separately from owned allocations */ trackReferencedMemory(bytes, name2 = this.getStatsName()) { this.trackAllocatedMemory(bytes, `Referenced ${name2}`); } /** Called by subclass to track memory deallocations */ trackDeallocatedMemory(name2 = this.getStatsName()) { if (this.allocatedBytes === 0) { this.allocatedBytesName = null; return; } const profiler = getCpuHotspotProfiler(this._device); const startTime = profiler ? getTimestamp() : 0; const stats = this._device.statsManager.getStats(GPU_TIME_AND_MEMORY_STATS2); stats.get("GPU Memory").subtractCount(this.allocatedBytes); stats.get(`${this.allocatedBytesName || name2} Memory`).subtractCount(this.allocatedBytes); if (profiler) { profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1; profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime); } this.allocatedBytes = 0; this.allocatedBytesName = null; } /** Called by subclass to deallocate handle-backed memory tracked via trackReferencedMemory() */ trackDeallocatedReferencedMemory(name2 = this.getStatsName()) { this.trackDeallocatedMemory(`Referenced ${name2}`); } /** Called by resource constructor to track object creation */ addStats() { const name2 = this.getStatsName(); const profiler = getCpuHotspotProfiler(this._device); const startTime = profiler ? getTimestamp() : 0; const statsObjects = [ this._device.statsManager.getStats(RESOURCE_COUNTS_STATS), this._device.statsManager.getStats(LEGACY_RESOURCE_COUNTS_STATS) ]; const orderedStatNames = getResourceCountStatOrder(this._device); for (const stats of statsObjects) { initializeStats2(stats, orderedStatNames); } for (const stats of statsObjects) { stats.get("Resources Created").incrementCount(); stats.get("Resources Active").incrementCount(); stats.get(`${name2}s Created`).incrementCount(); stats.get(`${name2}s Active`).incrementCount(); } if (profiler) { profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1; profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime); } recordTransientCanvasResourceCreate(this._device, name2); } /** Canonical resource name used for stats buckets. */ getStatsName() { return getCanonicalResourceName(this); } }; /** Default properties for resource */ __publicField(Resource, "defaultProps", { id: "undefined", handle: void 0, userData: void 0 }); function selectivelyMerge(props, defaultProps) { const mergedProps = { ...defaultProps }; for (const key in props) { if (props[key] !== void 0) { mergedProps[key] = props[key]; } } return mergedProps; } function initializeStats2(stats, orderedStatNames) { const statsMap = stats.stats; let addedOrderedStat = false; for (const statName of orderedStatNames) { if (!statsMap[statName]) { stats.get(statName); addedOrderedStat = true; } } const statCount = Object.keys(statsMap).length; const cachedStats = ORDERED_STATS_CACHE2.get(stats); if (!addedOrderedStat && (cachedStats == null ? void 0 : cachedStats.orderedStatNames) === orderedStatNames && cachedStats.statCount === statCount) { return; } const reorderedStats = {}; let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE2.get(orderedStatNames); if (!orderedStatNamesSet) { orderedStatNamesSet = new Set(orderedStatNames); ORDERED_STAT_NAME_SET_CACHE2.set(orderedStatNames, orderedStatNamesSet); } for (const statName of orderedStatNames) { if (statsMap[statName]) { reorderedStats[statName] = statsMap[statName]; } } for (const [statName, stat] of Object.entries(statsMap)) { if (!orderedStatNamesSet.has(statName)) { reorderedStats[statName] = stat; } } for (const statName of Object.keys(statsMap)) { delete statsMap[statName]; } Object.assign(statsMap, reorderedStats); ORDERED_STATS_CACHE2.set(stats, { orderedStatNames, statCount }); } function getResourceCountStatOrder(device) { return device.type === "webgl" ? WEBGL_RESOURCE_COUNT_STAT_ORDER : BASE_RESOURCE_COUNT_STAT_ORDER; } function getCpuHotspotProfiler(device) { const profiler = device.userData[CPU_HOTSPOT_PROFILER_MODULE]; return (profiler == null ? void 0 : profiler.enabled) ? profiler : null; } function getTimestamp() { var _a, _b; return ((_b = (_a = globalThis.performance) == null ? void 0 : _a.now) == null ? void 0 : _b.call(_a)) ?? Date.now(); } function recordTransientCanvasResourceCreate(device, name2) { const profiler = getCpuHotspotProfiler(device); if (!profiler || !profiler.activeDefaultFramebufferAcquireDepth) { return; } profiler.transientCanvasResourceCreates = (profiler.transientCanvasResourceCreates || 0) + 1; switch (name2) { case "Texture": profiler.transientCanvasTextureCreates = (profiler.transientCanvasTextureCreates || 0) + 1; break; case "TextureView": profiler.transientCanvasTextureViewCreates = (profiler.transientCanvasTextureViewCreates || 0) + 1; break; case "Sampler": profiler.transientCanvasSamplerCreates = (profiler.transientCanvasSamplerCreates || 0) + 1; break; case "Framebuffer": profiler.transientCanvasFramebufferCreates = (profiler.transientCanvasFramebufferCreates || 0) + 1; break; default: break; } } function getCanonicalResourceName(resource) { let prototype = Object.getPrototypeOf(resource); while (prototype) { const parentPrototype = Object.getPrototypeOf(prototype); if (!parentPrototype || parentPrototype === Resource.prototype) { return getPrototypeToStringTag(prototype) || resource[Symbol.toStringTag] || resource.constructor.name; } prototype = parentPrototype; } return resource[Symbol.toStringTag] || resource.constructor.name; } function getPrototypeToStringTag(prototype) { const descriptor = Object.getOwnPropertyDescriptor(prototype, Symbol.toStringTag); if (typeof (descriptor == null ? void 0 : descriptor.get) === "function") { return descriptor.get.call(prototype); } if (typeof (descriptor == null ? void 0 : descriptor.value) === "string") { return descriptor.value; } return null; } // dist/adapter/resources/buffer.js var _Buffer = class extends Resource { get [Symbol.toStringTag]() { return "Buffer"; } /** The usage with which this buffer was created */ usage; /** For index buffers, whether indices are 8, 16 or 32 bit. Note: uint8 indices are automatically converted to uint16 for WebGPU compatibility */ indexType; /** "Time" of last update, can be used to check if redraw is needed */ updateTimestamp; constructor(device, props) { const deducedProps = { ...props }; if ((props.usage || 0) & _Buffer.INDEX && !props.indexType) { if (props.data instanceof Uint32Array) { deducedProps.indexType = "uint32"; } else if (props.data instanceof Uint16Array) { deducedProps.indexType = "uint16"; } else if (props.data instanceof Uint8Array) { deducedProps.indexType = "uint8"; } } delete deducedProps.data; super(device, deducedProps, _Buffer.defaultProps); this.usage = deducedProps.usage || 0; this.indexType = deducedProps.indexType; this.updateTimestamp = device.incrementTimestamp(); } /** * Create a copy of this Buffer with new byteLength, with same props but of the specified size. * @note Does not copy contents of the cloned Buffer. */ clone(props) { return this.device.createBuffer({ ...this.props, ...props }); } /** A partial CPU-side copy of the data in this buffer, for debugging purposes */ debugData = new ArrayBuffer(0); /** This doesn't handle partial non-zero offset updates correctly */ _setDebugData(data, _byteOffset, byteLength) { let arrayBufferView = null; let arrayBuffer2; if (ArrayBuffer.isView(data)) { arrayBufferView = data; arrayBuffer2 = data.buffer; } else { arrayBuffer2 = data; } const debugDataLength = Math.min(data ? data.byteLength : byteLength, _Buffer.DEBUG_DATA_MAX_LENGTH); if (arrayBuffer2 === null) { this.debugData = new ArrayBuffer(debugDataLength); } else { const sourceByteOffset = Math.min((arrayBufferView == null ? void 0 : arrayBufferView.byteOffset) || 0, arrayBuffer2.byteLength); const availableByteLength = Math.max(0, arrayBuffer2.byteLength - sourceByteOffset); const copyByteLength = Math.min(debugDataLength, availableByteLength); this.debugData = new Uint8Array(arrayBuffer2, sourceByteOffset, copyByteLength).slice().buffer; } } }; var Buffer2 = _Buffer; /** Index buffer */ __publicField(Buffer2, "INDEX", 16); /** Vertex buffer */ __publicField(Buffer2, "VERTEX", 32); /** Uniform buffer */ __publicField(Buffer2, "UNIFORM", 64); /** Storage buffer */ __publicField(Buffer2, "STORAGE", 128); __publicField(Buffer2, "INDIRECT", 256); __publicField(Buffer2, "QUERY_RESOLVE", 512); // Usage Flags __publicField(Buffer2, "MAP_READ", 1); __publicField(Buffer2, "MAP_WRITE", 2); __publicField(Buffer2, "COPY_SRC", 4); __publicField(Buffer2, "COPY_DST", 8); // PROTECTED METHODS (INTENDED FOR USE BY OTHER FRAMEWORK CODE ONLY) /** Max amount of debug data saved. Two vec4's */ __publicField(Buffer2, "DEBUG_DATA_MAX_LENGTH", 32); __publicField(Buffer2, "defaultProps", { ...Resource.defaultProps, usage: 0, // Buffer.COPY_DST | Buffer.COPY_SRC byteLength: 0, byteOffset: 0, data: null, indexType: "uint16", onMapped: void 0 }); // dist/shadertypes/data-types/data-type-decoder.js var DataTypeDecoder = class { /** * Gets info about a data type constant (signed or normalized) * @returns underlying primitive / signed types, byte length, normalization, integer, signed flags */ getDataTypeInfo(type) { const [signedType, primitiveType, byteLength] = NORMALIZED_TYPE_MAP[type]; const normalized = type.includes("norm"); const integer = !normalized && !type.startsWith("float"); const signed = type.startsWith("s"); return { signedType, primitiveType, byteLength, normalized, integer, signed // TODO - add webglOnly flag }; } /** Build a vertex format from a signed data type and a component */ getNormalizedDataType(signedDataType) { const dataType = signedDataType; switch (dataType) { case "uint8": return "unorm8"; case "sint8": return "snorm8"; case "uint16": return "unorm16"; case "sint16": return "snorm16"; default: return dataType; } } /** Align offset to 1, 2 or 4 elements (4, 8 or 16 bytes) */ alignTo(size, count) { switch (count) { case 1: return size; case 2: return size + size % 2; default: return size + (4 - size % 4) % 4; } } /** Returns the VariableShaderType that corresponds to a typed array */ getDataType(arrayOrType) { const Constructor = ArrayBuffer.isView(arrayOrType) ? arrayOrType.constructor : arrayOrType; if (Constructor === Uint8ClampedArray) { return "uint8"; } const info = Object.values(NORMALIZED_TYPE_MAP).find((entry) => Constructor === entry[4]); if (!info) { throw new Error(Constructor.name); } return info[0]; } /** Returns the TypedArray that corresponds to a shader data type */ getTypedArrayConstructor(type) { const [, , , , Constructor] = NORMALIZED_TYPE_MAP[type]; return Constructor; } }; var dataTypeDecoder = new DataTypeDecoder(); var NORMALIZED_TYPE_MAP = { uint8: ["uint8", "u32", 1, false, Uint8Array], sint8: ["sint8", "i32", 1, false, Int8Array], unorm8: ["uint8", "f32", 1, true, Uint8Array], snorm8: ["sint8", "f32", 1, true, Int8Array], uint16: ["uint16", "u32", 2, false, Uint16Array], sint16: ["sint16", "i32", 2, false, Int16Array], unorm16: ["uint16", "u32", 2, true, Uint16Array], snorm16: ["sint16", "i32", 2, true, Int16Array], float16: ["float16", "f16", 2, false, Uint16Array], float32: ["float32", "f32", 4, false, Float32Array], uint32: ["uint32", "u32", 4, false, Uint32Array], sint32: ["sint32", "i32", 4, false, Int32Array] }; // dist/shadertypes/vertex-types/vertex-format-decoder.js var VertexFormatDecoder = class { /** * Decodes a vertex format, returning type, components, byte length and flags (integer, signed, normalized) */ getVertexFormatInfo(format) { let webglOnly; if (format.endsWith("-webgl")) { format.replace("-webgl", ""); webglOnly = true; } const [type_, count] = format.split("x"); const type = type_; const components = count ? parseInt(count) : 1; const decodedType = dataTypeDecoder.getDataTypeInfo(type); const result = { type, components, byteLength: decodedType.byteLength * components, integer: decodedType.integer, signed: decodedType.signed, normalized: decodedType.normalized }; if (webglOnly) { result.webglOnly = true; } return result; } /** Build a vertex format from a signed data type and a component */ makeVertexFormat(signedDataType, components, normalized) { const dataType = normalized ? dataTypeDecoder.getNormalizedDataType(signedDataType) : signedDataType; switch (dataType) { case "unorm8": if (components === 1) { return "unorm8"; } if (components === 3) { return "unorm8x3-webgl"; } return `${dataType}x${components}`; case "snorm8": if (components === 1) { return "snorm8"; } if (components === 3) { return "snorm8x3-webgl"; } return `${dataType}x${components}`; case "uint8": case "sint8": if (components === 1 || components === 3) { throw new Error(`size: ${components}`); } return `${dataType}x${components}`; case "uint16": if (components === 1) { return "uint16"; } if (components === 3) { return "uint16x3-webgl"; } return `${dataType}x${components}`; case "sint16": if (components === 1) { return "sint16"; } if (components === 3) { return "sint16x3-webgl"; } return `${dataType}x${components}`; case "unorm16": if (components === 1) { return "unorm16"; } if (components === 3) { return "unorm16x3-webgl"; } return `${dataType}x${components}`; case "snorm16": if (components === 1) { return "snorm16"; } if (components === 3) { return "snorm16x3-webgl"; } return `${dataType}x${components}`; case "float16": if (components === 1 || components === 3) { throw new Error(`size: ${components}`); } return `${dataType}x${components}`; default: return components === 1 ? dataType : `${dataType}x${components}`; } } /** Get the vertex format for an attribute with TypedArray and size */ getVertexFormatFromAttribute(typedArray, size, normalized) { if (!size || size > 4) { throw new Error(`size ${size}`); } const components = size; const signedDataType = dataTypeDecoder.getDataType(typedArray); return this.makeVertexFormat(signedDataType, components, normalized); } /** * Return a "default" vertex format for a certain shader data type * The simplest vertex format that matches the shader attribute's data type */ getCompatibleVertexFormat(opts) { let vertexType; switch (opts.primitiveType) { case "f32": vertexType = "float32"; break; case "i32": vertexType = "sint32"; break; case "u32": vertexType = "uint32"; break; case "f16": return opts.components <= 2 ? "float16x2" : "float16x4"; } if (opts.components === 1) { return vertexType; } return `${vertexType}x${opts.components}`; } }; var vertexFormatDecoder = new VertexFormatDecoder(); // dist/shadertypes/texture-types/texture-format-table.js var texture_compression_bc = "texture-compression-bc"; var texture_compression_astc = "texture-compression-astc"; var texture_compression_etc2 = "texture-compression-etc2"; var texture_compression_etc1_webgl = "texture-compression-etc1-webgl"; var texture_compression_pvrtc_webgl = "texture-compression-pvrtc-webgl"; var texture_compression_atc_webgl = "texture-compression-atc-webgl"; var float32_renderable = "float32-renderable-webgl"; var float16_renderable = "float16-renderable-webgl"; var rgb9e5ufloat_renderable = "rgb9e5ufloat-renderable-webgl"; var snorm8_renderable = "snorm8-renderable-webgl"; var norm16_webgl = "norm16-webgl"; var norm16_renderable = "norm16-renderable-webgl"; var snorm16_renderable = "snorm16-renderable-webgl"; var float32_filterable = "float32-filterable"; var float16_filterable = "float16-filterable-webgl"; function getTextureFormatDefinition(format) { const info = TEXTURE_FORMAT_TABLE[format]; if (!info) { throw new Error(`Unsupported texture format ${format}`); } return info; } function getTextureFormatTable() { return TEXTURE_FORMAT_TABLE; } var TEXTURE_FORMAT_COLOR_DEPTH_TABLE = { // 8-bit formats "r8unorm": {}, "rg8unorm": {}, "rgb8unorm-webgl": {}, "rgba8unorm": {}, "rgba8unorm-srgb": {}, "r8snorm": { render: snorm8_renderable }, "rg8snorm": { render: snorm8_renderable }, "rgb8snorm-webgl": {}, "rgba8snorm": { render: snorm8_renderable }, "r8uint": {}, "rg8uint": {}, "rgba8uint": {}, "r8sint": {}, "rg8sint": {}, "rgba8sint": {}, "bgra8unorm": {}, "bgra8unorm-srgb": {}, "r16unorm": { f: norm16_webgl, render: norm16_renderable }, "rg16unorm": { f: norm16_webgl, render: norm16_renderable }, "rgb16unorm-webgl": { f: norm16_webgl, render: false }, // rgb not renderable "rgba16unorm": { f: norm16_webgl, render: norm16_renderable }, "r16snorm": { f: norm16_webgl, render: snorm16_renderable }, "rg16snorm": { f: norm16_webgl, render: snorm16_renderable }, "rgb16snorm-webgl": { f: norm16_webgl, render: false }, // rgb not renderable "rgba16snorm": { f: norm16_webgl, render: snorm16_renderable }, "r16uint": {}, "rg16uint": {}, "rgba16uint": {}, "r16sint": {}, "rg16sint": {}, "rgba16sint": {}, "r16float": { render: float16_renderable, filter: "float16-filterable-webgl" }, "rg16float": { render: float16_renderable, filter: float16_filterable }, "rgba16float": { render: float16_renderable, filter: float16_filterable }, "r32uint": {}, "rg32uint": {}, "rgba32uint": {}, "r32sint": {}, "rg32sint": {}, "rgba32sint": {}, "r32float": { render: float32_renderable, filter: float32_filterable }, "rg32float": { render: false, filter: float32_filterable }, "rgb32float-webgl": { render: float32_renderable, filter: float32_filterable }, "rgba32float": { render: float32_renderable, filter: float32_filterable }, // Packed 16-bit formats "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 }, // Packed 32 bit formats "rgb9e5ufloat": { channels: "rgb", packed: true, render: rgb9e5ufloat_renderable }, // , filter: true}, "rg11b10ufloat": { channels: "rgb", bitsPerChannel: [11, 11, 10, 0], packed: true, p: 1, render: float32_renderable }, "rgb10a2unorm": { channels: "rgba", bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1 }, "rgb10a2uint": { channels: "rgba", bitsPerChannel: [10, 10, 10, 2], packed: true, p: 1 }, // Depth/stencil Formats // Depth and stencil formats stencil8: { attachment: "stencil", bitsPerChannel: [8, 0, 0, 0], dataType: "uint8" }, "depth16unorm": { attachment: "depth", bitsPerChannel: [16, 0, 0, 0], dataType: "uint16" }, "depth24plus": { attachment: "depth", bitsPerChannel: [24, 0, 0, 0], dataType: "uint32" }, "depth32float": { attachment: "depth", 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", bitsPerChannel: [24, 8, 0, 0], packed: true }, // "depth32float-stencil8" feature "depth32float-stencil8": { attachment: "depth-stencil", bitsPerChannel: [32, 8, 0, 0], packed: true } }; var TEXTURE_FORMAT_COMPRESSED_TABLE = { // 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 }, // 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 }, "eac-r11unorm": { f: texture_compression_etc2 }, "eac-r11snorm": { f: texture_compression_etc2 }, "eac-rg11unorm": { f: texture_compression_etc2 }, "eac-rg11snorm": { f: 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 }, // WEBGL_compressed_texture_pvrtc "pvrtc-rgb4unorm-webgl": { f: texture_compression_pvrtc_webgl }, "pvrtc-rgba4unorm-webgl": { f: texture_compression_pvrtc_webgl }, "pvrtc-rgb2unorm-webgl": { f: texture_compression_pvrtc_webgl }, "pvrtc-rgba2unorm-webgl": { f: texture_compression_pvrtc_webgl }, // WEBGL_compressed_texture_etc1 "etc1-rbg-unorm-webgl": { f: 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 } }; var TEXTURE_FORMAT_TABLE = { ...TEXTURE_FORMAT_COLOR_DEPTH_TABLE, ...TEXTURE_FORMAT_COMPRESSED_TABLE }; // dist/shadertypes/texture-types/texture-format-decoder.js var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/; var COLOR_FORMAT_PREFIXES = ["rgb", "rgba", "bgra"]; var DEPTH_FORMAT_PREFIXES = ["depth", "stencil"]; var COMPRESSED_TEXTURE_FORMAT_PREFIXES = [ "bc1", "bc2", "bc3", "bc4", "bc5", "bc6", "bc7", "etc1", "etc2", "eac", "atc", "astc", "pvrtc" ]; var TextureFormatDecoder = class { /** Checks if a texture format is color */ isColor(format) { return COLOR_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix)); } /** Checks if a texture format is depth or stencil */ isDepthStencil(format) { return DEPTH_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix)); } /** Checks if a texture format is compressed */ isCompressed(format) { return COMPRESSED_TEXTURE_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix)); } /** Returns information about a texture format, e.g. attachment type, components, byte length and flags (integer, signed, normalized) */ getInfo(format) { return getTextureFormatInfo(format); } /** "static" capabilities of a texture format. @note Needs to be adjusted against current device */ getCapabilities(format) { return getTextureFormatCapabilities(format); } /** Computes the memory layout for a texture, in particular including row byte alignment */ computeMemoryLayout(opts) { return computeTextureMemoryLayout(opts); } }; var textureFormatDecoder = new TextureFormatDecoder(); function computeTextureMemoryLayout({ format, width, height, depth, byteAlignment }) { const formatInfo = textureFormatDecoder.getInfo(format); const { bytesPerPixel, bytesPerBlock = bytesPerPixel, blockWidth = 1, blockHeight = 1, compressed = false } = formatInfo; const blockColumns = compressed ? Math.ceil(width / blockWidth) : width; const blockRows = compressed ? Math.ceil(height / blockHeight) : height; const unpaddedBytesPerRow = blockColumns * bytesPerBlock; const bytesPerRow = Math.ceil(unpaddedBytesPerRow / byteAlignment) * byteAlignment; const rowsPerImage = blockRows; const byteLength = bytesPerRow * rowsPerImage * depth; return { bytesPerPixel, bytesPerRow, rowsPerImage, depthOrArrayLayers: depth, bytesPerImage: bytesPerRow * rowsPerImage, byteLength }; } function getTextureFormatCapabilities(format) { const info = getTextureFormatDefinition(format); const formatCapabilities = { format, create: info.f ?? true, render: info.render ?? true, filter: info.filter ?? true, blend: info.blend ?? true, store: info.store ?? true }; const formatInfo = getTextureFormatInfo(format); const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil"); const isSigned = formatInfo == null ? void 0 : formatInfo.signed; const isInteger = formatInfo == null ? void 0 : formatInfo.integer; const isWebGLSpecific = formatInfo == null ? void 0 : formatInfo.webgl; const isCompressed = Boolean(formatInfo == null ? void 0 : formatInfo.compressed); formatCapabilities.render &&= !isDepthStencil && !isCompressed; formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific; return formatCapabilities; } function getTextureFormatInfo(format) { let formatInfo = getTextureFormatInfoUsingTable(format); if (textureFormatDecoder.isCompressed(format)) { formatInfo.channels = "rgb"; formatInfo.components = 3; formatInfo.bytesPerPixel = 1; formatInfo.srgb = false; formatInfo.compressed = true; formatInfo.bytesPerBlock = getCompressedTextureBlockByteLength(format); const blockSize = getCompressedTextureBlockSize(format); if (blockSize) { formatInfo.blockWidth = blockSize.blockWidth; formatInfo.blockHeight = blockSize.blockHeight; } } const matches = !formatInfo.packed ? RGB_FORMAT_REGEX.exec(format) : null; if (matches) { const [, channels, length, type, srgb, suffix] = matches; const dataType = `${type}${length}`; const decodedType = dataTypeDecoder.getDataTypeInfo(dataType); const bits = decodedType.byteLength * 8; const components = (channels == null ? void 0 : channels.length) ?? 1; const bitsPerChannel = [ bits, components >= 2 ? bits : 0, components >= 3 ? bits : 0, components >= 4 ? bits : 0 ]; formatInfo = { format, attachment: formatInfo.attachment, dataType: decodedType.signedType, components, channels, integer: decodedType.integer, signed: decodedType.signed, normalized: decodedType.normalized, bitsPerChannel, bytesPerPixel: decodedType.byteLength * components, packed: formatInfo.packed, srgb: formatInfo.srgb }; if (suffix === "-webgl") { formatInfo.webgl = true; } if (srgb === "-srgb") { formatInfo.srgb = true; } } if (format.endsWith("-webgl")) { formatInfo.webgl = true; } if (format.endsWith("-srgb")) { formatInfo.srgb = true; } return formatInfo; } function getTextureFormatInfoUsingTable(format) { var _a; const info = getTextureFormatDefinition(format); const bytesPerPixel = info.bytesPerPixel || 1; const bitsPerChannel = info.bitsPerChannel || [8, 8, 8, 8]; delete info.bitsPerChannel; delete info.bytesPerPixel; delete info.f; delete info.render; delete info.filter; delete info.blend; delete info.store; const formatInfo = { ...info, format, attachment: info.attachment || "color", channels: info.channels || "r", components: info.components || ((_a = info.channels) == null ? void 0 : _a.length) || 1, 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; } function getCompressedTextureBlockSize(format) { const REGEX = /.*-(\d+)x(\d+)-.*/; const matches = REGEX.exec(format); if (matches) { const [, blockWidth, blockHeight] = matches; return { blockWidth: Number(blockWidth), blockHeight: Number(blockHeight) }; } if (format.startsWith("bc") || format.startsWith("etc1") || format.startsWith("etc2") || format.startsWith("eac") || format.startsWith("atc")) { return { blockWidth: 4, blockHeight: 4 }; } if (format.startsWith("pvrtc-rgb4") || format.startsWith("pvrtc-rgba4")) { return { blockWidth: 4, blockHeight: 4 }; } if (format.startsWith("pvrtc-rgb2") || format.startsWith("pvrtc-rgba2")) { return { blockWidth: 8, blockHeight: 4 }; } return null; } function getCompressedTextureBlockByteLength(format) { if (format.startsWith("bc1") || format.startsWith("bc4") || format.startsWith("etc1") || format.startsWith("etc2-rgb8") || format.startsWith("etc2-rgb8a1") || format.startsWith("eac-r11") || format === "atc-rgb-unorm-webgl") { return 8; } if (format.startsWith("bc2") || format.startsWith("bc3") || format.startsWith("bc5") || format.startsWith("bc6h") || format.startsWith("bc7") || format.startsWith("etc2-rgba8") || format.startsWith("eac-rg11") || format.startsWith("astc") || format === "atc-rgba-unorm-webgl" || format === "atc-rgbai-unorm-webgl") { return 16; } if (format.startsWith("pvrtc")) { return 8; } return 16; } // dist/shadertypes/image-types/image-types.js function isExternalImage(data) { return typeof ImageData !== "undefined" && data instanceof ImageData || typeof ImageBitmap !== "undefined" && data instanceof ImageBitmap || typeof HTMLImageElement !== "undefined" && data instanceof HTMLImageElement || typeof HTMLVideoElement !== "undefined" && data instanceof HTMLVideoElement || typeof VideoFrame !== "undefined" && data instanceof VideoFrame || typeof HTMLCanvasElement !== "undefined" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && data instanceof OffscreenCanvas; } function getExternalImageSize(data) { if (typeof ImageData !== "undefined" && data instanceof ImageData || typeof ImageBitmap !== "undefined" && data instanceof ImageBitmap || typeof HTMLCanvasElement !== "undefined" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && data instanceof OffscreenCanvas) { return { width: data.width, height: data.height }; } if (typeof HTMLImageElement !== "undefined" && data instanceof HTMLImageElement) { return { width: data.naturalWidth, height: data.naturalHeight }; } if (typeof HTMLVideoElement !== "undefined" && data instanceof HTMLVideoElement) { return { width: data.videoWidth, height: data.videoHeight }; } if (typeof VideoFrame !== "undefined" && data instanceof VideoFrame) { return { width: data.displayWidth, height: data.displayHeight }; } throw new Error("Unknown image type"); } // dist/adapter/device.js var DeviceLimits = class { }; function formatErrorLogArguments(context, args) { const formattedContext = formatErrorLogValue(context); const formattedArgs = args.map(formatErrorLogValue).filter((arg) => arg !== void 0); return [formattedContext, ...formattedArgs].filter((arg) => arg !== void 0); } function formatErrorLogValue(value) { var _a; if (value === void 0) { return void 0; } if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") { return value; } if (value instanceof Error) { return value.message; } if (Array.isArray(value)) { return value.map(formatErrorLogValue); } if (typeof value === "object") { if (hasCustomToString(value)) { const stringValue = String(value); if (stringValue !== "[object Object]") { return stringValue; } } if (looksLikeGPUCompilationMessage(value)) { return formatGPUCompilationMessage(value); } return ((_a = value.constructor) == null ? void 0 : _a.name) || "Object"; } return String(value); } function hasCustomToString(value) { return "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString; } function looksLikeGPUCompilationMessage(value) { return "message" in value && "type" in value; } function formatGPUCompilationMessage(value) { const type = typeof value.type === "string" ? value.type : "message"; const message = typeof value.message === "string" ? value.message : ""; const lineNum = typeof value.lineNum === "number" ? value.lineNum : null; const linePos = typeof value.linePos === "number" ? value.linePos : null; const location = lineNum !== null && linePos !== null ? ` @ ${lineNum}:${linePos}` : lineNum !== null ? ` @ ${lineNum}` : ""; return `${type}${location}: ${message}`.trim(); } var DeviceFeatures = class { features; disabledFeatures; constructor(features = [], disabledFeatures) { this.features = new Set(features); this.disabledFeatures = disabledFeatures || {}; } *[Symbol.iterator]() { yield* this.features; } has(feature) { var _a; return !((_a = this.disabledFeatures) == null ? void 0 : _a[feature]) && this.features.has(feature); } }; var _Device = class { get [Symbol.toStringTag]() { return "Device"; } toString() { return `Device(${this.id})`; } /** id of this device, primarily for debugging */ id; /** A copy of the device props */ props; /** Available for the application to store data on the device */ userData = {}; /** stats */ statsManager = lumaStats; /** Internal per-device factory storage */ _factories = {}; /** An abstract timestamp used for change tracking */ timestamp = 0; /** True if this device has been reused during device creation (app has multiple references) */ _reused = false; /** Used by other luma.gl modules to store data on the device */ _moduleData = {}; _textureCaps = {}; /** Internal timestamp query set used when GPU timing collection is enabled for this device. */ _debugGPUTimeQuery = null; constructor(props) { this.props = { ..._Device.defaultProps, ...props }; this.id = this.props.id || uid(this[Symbol.toStringTag].toLowerCase()); } // TODO - just expose the shadertypes decoders? getVertexFormatInfo(format) { return vertexFormatDecoder.getVertexFormatInfo(format); } isVertexFormatSupported(format) { return true; } /** R