UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

121 lines (120 loc) 4.13 kB
import { hashCode } from "../../core/hash.js"; import { math } from "../../core/math/math.js"; import { StringIds } from "../../core/string-ids.js"; import { SEMANTIC_TEXCOORD0, SEMANTIC_TEXCOORD1, SEMANTIC_ATTR12, SEMANTIC_ATTR11, SEMANTIC_ATTR14, SEMANTIC_ATTR15, SEMANTIC_COLOR, SEMANTIC_TANGENT, TYPE_FLOAT32, typedArrayTypesByteSize, vertexTypesNames } from "./constants.js"; import { DeviceCache } from "./device-cache.js"; const stringIds = new StringIds(); const webgpuValidElementSizes = [2, 4, 8, 12, 16]; const deviceCache = new DeviceCache(); class VertexFormat { constructor(graphicsDevice, description, vertexCount) { this.device = graphicsDevice; this._elements = []; this.hasUv0 = false; this.hasUv1 = false; this.hasColor = false; this.hasTangents = false; this.verticesByteSize = 0; this.vertexCount = vertexCount; this.interleaved = vertexCount === void 0; this.instancing = false; this.size = description.reduce((total, desc) => { return total + Math.ceil(desc.components * typedArrayTypesByteSize[desc.type] / 4) * 4; }, 0); let offset = 0, elementSize; for (let i = 0, len = description.length; i < len; i++) { const elementDesc = description[i]; elementSize = elementDesc.components * typedArrayTypesByteSize[elementDesc.type]; if (vertexCount) { offset = math.roundUp(offset, elementSize); } const asInt = elementDesc.asInt ?? false; const normalize = asInt ? false : elementDesc.normalize ?? false; const element = { name: elementDesc.semantic, offset: vertexCount ? offset : elementDesc.hasOwnProperty("offset") ? elementDesc.offset : offset, stride: vertexCount ? elementSize : elementDesc.hasOwnProperty("stride") ? elementDesc.stride : this.size, dataType: elementDesc.type, numComponents: elementDesc.components, normalize, size: elementSize, asInt }; this._elements.push(element); if (vertexCount) { offset += elementSize * vertexCount; } else { offset += Math.ceil(elementSize / 4) * 4; } if (elementDesc.semantic === SEMANTIC_TEXCOORD0) { this.hasUv0 = true; } else if (elementDesc.semantic === SEMANTIC_TEXCOORD1) { this.hasUv1 = true; } else if (elementDesc.semantic === SEMANTIC_COLOR) { this.hasColor = true; } else if (elementDesc.semantic === SEMANTIC_TANGENT) { this.hasTangents = true; } } if (vertexCount) { this.verticesByteSize = offset; } this._evaluateHash(); } get elements() { return this._elements; } static getDefaultInstancingFormat(graphicsDevice) { return deviceCache.get(graphicsDevice, () => { return new VertexFormat(graphicsDevice, [ { semantic: SEMANTIC_ATTR11, components: 4, type: TYPE_FLOAT32 }, { semantic: SEMANTIC_ATTR12, components: 4, type: TYPE_FLOAT32 }, { semantic: SEMANTIC_ATTR14, components: 4, type: TYPE_FLOAT32 }, { semantic: SEMANTIC_ATTR15, components: 4, type: TYPE_FLOAT32 } ]); }); } static isElementValid(graphicsDevice, elementDesc) { const elementSize = elementDesc.components * typedArrayTypesByteSize[elementDesc.type]; if (graphicsDevice.isWebGPU && !webgpuValidElementSizes.includes(elementSize)) { return false; } return true; } update() { this._evaluateHash(); } _evaluateHash() { const stringElementsBatch = []; const stringElementsRender = []; const len = this._elements.length; for (let i = 0; i < len; i++) { const { name, dataType, numComponents, normalize, offset, stride, size, asInt } = this._elements[i]; const stringElementBatch = name + dataType + numComponents + normalize + asInt; stringElementsBatch.push(stringElementBatch); const stringElementRender = stringElementBatch + offset + stride + size; stringElementsRender.push(stringElementRender); } stringElementsBatch.sort(); const batchingString = stringElementsBatch.join(); this.batchingHash = hashCode(batchingString); this.shaderProcessingHashString = batchingString; this.renderingHashString = stringElementsRender.join("_"); this.renderingHash = stringIds.get(this.renderingHashString); } } export { VertexFormat };