UNPKG

@deck.gl/core

Version:

deck.gl core library

144 lines 6.3 kB
// deck.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import { padArray } from "../utils/array-utils.js"; import Attribute from "../lib/attribute/attribute.js"; import { GL } from '@luma.gl/constants'; /** Create a new empty attribute with the same settings: type, shader layout etc. */ export function cloneAttribute(attribute) { // `attribute.settings` is the original options passed when constructing the attribute. // This ensures that we set the proper `doublePrecision` flag and shader attributes. const { device, settings, value } = attribute; const newAttribute = new Attribute(device, settings); // Placeholder value - necessary for generating the correct buffer layout newAttribute.setData({ value: value instanceof Float64Array ? new Float64Array(0) : new Float32Array(0), normalized: settings.normalized }); return newAttribute; } /** Returns the GLSL attribute type for the given number of float32 components. */ export function getAttributeTypeFromSize(size) { switch (size) { case 1: return 'float'; case 2: return 'vec2'; case 3: return 'vec3'; case 4: return 'vec4'; default: throw new Error(`No defined attribute type for size "${size}"`); } } /** Returns the {@link VertexFormat} for the given number of float32 components. */ export function getFloat32VertexFormat(size) { switch (size) { case 1: return 'float32'; case 2: return 'float32x2'; case 3: return 'float32x3'; case 4: return 'float32x4'; default: throw new Error('invalid type size'); } } export function cycleBuffers(buffers) { buffers.push(buffers.shift()); } export function getAttributeBufferLength(attribute, numInstances) { const { doublePrecision, settings, value, size } = attribute; const multiplier = doublePrecision && value instanceof Float64Array ? 2 : 1; let maxVertexOffset = 0; const { shaderAttributes } = attribute.settings; if (shaderAttributes) { for (const shaderAttribute of Object.values(shaderAttributes)) { maxVertexOffset = Math.max(maxVertexOffset, shaderAttribute.vertexOffset ?? 0); } } return ((settings.noAlloc ? value.length : (numInstances + maxVertexOffset) * size) * multiplier); } export function matchBuffer({ device, source, target }) { if (!target || target.byteLength < source.byteLength) { target?.destroy(); target = device.createBuffer({ byteLength: source.byteLength, usage: source.usage }); } return target; } /* eslint-disable complexity */ // This helper is used when transitioning attributes from a set of values in one buffer layout // to a set of values in a different buffer layout. (Buffer layouts are used when attribute values // within a buffer should be grouped for drawElements, like the Polygon layer.) For example, a // buffer layout of [3, 4] might have data [A1, A2, A3, B1, B2, B3, B4]. If it needs to transition // to a buffer layout of [4, 2], it should produce a buffer, using the transition setting's `enter` // function, that looks like this: [A1, A2, A3, A4 (user `enter` fn), B1, B2, 0]. Note: the final // 0 in this buffer is because we never shrink buffers, only grow them, for performance reasons. // // padBuffer may return either the original buffer, or a new buffer if the size of the original // was insufficient. Callers are responsible for disposing of the original buffer if needed. export function padBuffer({ device, buffer, attribute, fromLength, toLength, fromStartIndices, getData = x => x }) { // TODO: move the precisionMultiplier logic to the attribute when retrieving // its `size` and `elementOffset`? const precisionMultiplier = attribute.doublePrecision && attribute.value instanceof Float64Array ? 2 : 1; const size = attribute.size * precisionMultiplier; const byteOffset = attribute.byteOffset; // Transform feedback can only write to float varyings // Attributes of format unorm8/uint8 (1 byte per element) etc will be padded to float32 (4 bytes per element) const targetByteOffset = attribute.settings.bytesPerElement < 4 ? (byteOffset / attribute.settings.bytesPerElement) * 4 : byteOffset; const toStartIndices = attribute.startIndices; const hasStartIndices = fromStartIndices && toStartIndices; const isConstant = attribute.isConstant; // check if buffer needs to be padded if (!hasStartIndices && buffer && fromLength >= toLength) { return buffer; } const ArrayType = attribute.value instanceof Float64Array ? Float32Array : attribute.value.constructor; const toData = isConstant ? attribute.value : // TODO(v9.1): Avoid non-portable synchronous reads. new ArrayType(attribute .getBuffer() .readSyncWebGL(byteOffset, toLength * ArrayType.BYTES_PER_ELEMENT).buffer); if (attribute.settings.normalized && !isConstant) { const getter = getData; getData = (value, chunk) => attribute.normalizeConstant(getter(value, chunk)); } const getMissingData = isConstant ? (i, chunk) => getData(toData, chunk) : (i, chunk) => getData(toData.subarray(i + byteOffset, i + byteOffset + size), chunk); // TODO(v9.1): Avoid non-portable synchronous reads. const source = buffer ? new Float32Array(buffer.readSyncWebGL(targetByteOffset, fromLength * 4).buffer) : new Float32Array(0); const target = new Float32Array(toLength); padArray({ source, target, sourceStartIndices: fromStartIndices, targetStartIndices: toStartIndices, size, getData: getMissingData }); if (!buffer || buffer.byteLength < target.byteLength + targetByteOffset) { buffer?.destroy(); buffer = device.createBuffer({ byteLength: target.byteLength + targetByteOffset, usage: 35050 }); } buffer.write(target, targetByteOffset); return buffer; } //# sourceMappingURL=gpu-transition-utils.js.map