UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

398 lines (320 loc) 13.7 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _classCallCheck from '@babel/runtime/helpers/classCallCheck'; import _createClass from '@babel/runtime/helpers/createClass'; import macro from '../../macros.js'; import Constants from './BufferManager/Constants.js'; import vtkProperty from '../Core/Property.js'; import vtkWebGPUBuffer from './Buffer.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var Representation = vtkProperty.Representation; var PrimitiveTypes = Constants.PrimitiveTypes; // Simulate a small map of pointId to flatId for a cell. The original code // used a map and was 2.6x slower (4.7 to 1.9 seconds). Using two fixed // length arrays with a count is so much faster even with the required for // loops and if statements. This only works as we know the usage is // restricted to clear(), set() get() and has() so the count is always // incrmenting except for clear where it goes back to 0. Performance // improvement is probably due to this appoach not hitting the heap but wow // it is so much faster. Code that adds to these vectors checks against 9 to // make sure there is room. Switching to test against vec.length -1 results // in a small performance hit, so if you change 10, search for 9 in this // small class and change those as well. var _LimitedMap = /*#__PURE__*/function () { function _LimitedMap() { _classCallCheck(this, _LimitedMap); this.keys = new Uint32Array(10); this.values = new Uint32Array(10); this.count = 0; } _createClass(_LimitedMap, [{ key: "clear", value: function clear() { this.count = 0; } }, { key: "has", value: function has(key) { for (var i = 0; i < this.count; i++) { if (this.keys[i] === key) { return true; } } return undefined; } }, { key: "get", value: function get(key) { for (var i = 0; i < this.count; i++) { if (this.keys[i] === key) { return this.values[i]; } } return undefined; } }, { key: "set", value: function set(key, value) { if (this.count < 9) { this.keys[this.count] = key; this.values[this.count++] = value; } } }]); return _LimitedMap; }(); function getPrimitiveName(primType) { switch (primType) { case PrimitiveTypes.Points: return 'points'; case PrimitiveTypes.Lines: return 'lines'; case PrimitiveTypes.Triangles: case PrimitiveTypes.TriangleEdges: return 'polys'; case PrimitiveTypes.TriangleStripEdges: case PrimitiveTypes.TriangleStrips: return 'strips'; default: return ''; } } function _getOrAddFlatId(state, ptId, cellId) { var flatId = state.pointIdToFlatId[ptId]; if (flatId < 0) { flatId = state.flatId; state.pointIdToFlatId[ptId] = flatId; state.flatIdToPointId[state.flatId] = ptId; state.flatIdToCellId[state.flatId] = cellId; state.flatId++; } return flatId; } function fillCell(ptIds, cellId, state) { var numPtIds = ptIds.length; // are any points already marked for this cell? If so use that as the provoking point for (var ptIdx = 0; ptIdx < numPtIds; ptIdx++) { var _ptId = ptIds[ptIdx]; if (state.cellProvokedMap.has(_ptId)) { state.ibo[state.iboId++] = state.cellProvokedMap.get(_ptId); // insert remaining ptIds (they do not need to provoke) for (var ptIdx2 = ptIdx + 1; ptIdx2 < ptIdx + numPtIds; ptIdx2++) { _ptId = ptIds[ptIdx2 % numPtIds]; var _flatId = _getOrAddFlatId(state, _ptId, cellId); // add to ibo state.ibo[state.iboId++] = _flatId; } // all done now return; } } // else have any of the points not been used yet? (not in provokedPointIds) for (var _ptIdx = 0; _ptIdx < numPtIds; _ptIdx++) { var _ptId2 = ptIds[_ptIdx]; if (!state.provokedPointIds[_ptId2]) { var _flatId2 = _getOrAddFlatId(state, _ptId2, cellId); // mark provoking and add to ibo state.provokedPointIds[_ptId2] = 1; state.cellProvokedMap.set(_ptId2, _flatId2); // when provoking always set the cellId as an original non-provoking value // will have been stored and we need to overwrite that state.flatIdToCellId[_flatId2] = cellId; state.ibo[state.iboId++] = _flatId2; // insert remaining ptIds (they do not need to provoke) for (var _ptIdx2 = _ptIdx + 1; _ptIdx2 < _ptIdx + numPtIds; _ptIdx2++) { _ptId2 = ptIds[_ptIdx2 % numPtIds]; _flatId2 = _getOrAddFlatId(state, _ptId2, cellId); // add to ibo state.ibo[state.iboId++] = _flatId2; } // all done now return; } } // if we got here then none of the ptIds could be used to provoke // so just duplicate the first one var ptId = ptIds[0]; var flatId = state.flatId; state.cellProvokedMap.set(ptId, flatId); state.flatIdToPointId[state.flatId] = ptId; state.flatIdToCellId[state.flatId] = cellId; state.flatId++; // add to ibo state.ibo[state.iboId++] = flatId; // insert remaining ptIds (they do not need to provoke) for (var _ptIdx3 = 1; _ptIdx3 < numPtIds; _ptIdx3++) { ptId = ptIds[_ptIdx3]; flatId = _getOrAddFlatId(state, ptId, cellId); // add to ibo state.ibo[state.iboId++] = flatId; } } function countCell(ptIds, cellId, state) { var numPtIds = ptIds.length; state.iboSize += numPtIds; // are any points already marked for this cell? If so use that as the provoking point for (var ptIdx = 0; ptIdx < numPtIds; ptIdx++) { var ptId = ptIds[ptIdx]; if (state.cellProvokedMap.has(ptId)) { return; } } // else have any of the points not been used yet? (not in provokedPointIds) for (var _ptIdx4 = 0; _ptIdx4 < numPtIds; _ptIdx4++) { var _ptId3 = ptIds[_ptIdx4]; if (!state.provokedPointIds[_ptId3]) { state.provokedPointIds[_ptId3] = 1; state.cellProvokedMap.set(_ptId3, 1); return; } } // if we got here then none of the ptIds could be used to provoke state.cellProvokedMap.set(ptIds[0], 1); state.extraPoints++; } var processCell; var _single = new Uint32Array(1); var _double = new Uint32Array(2); var _triple = new Uint32Array(3); var _indexCellBuilders = { // easy, every input point becomes an output point anythingToPoints: function anythingToPoints(numPoints, cellPts, offset, cellId, state) { for (var i = 0; i < numPoints; ++i) { _single[0] = cellPts[offset + i]; processCell(_single, cellId, state); } }, linesToWireframe: function linesToWireframe(numPoints, cellPts, offset, cellId, state) { // for lines we add a bunch of segments for (var i = 0; i < numPoints - 1; ++i) { _double[0] = cellPts[offset + i]; _double[1] = cellPts[offset + i + 1]; processCell(_double, cellId, state); } }, polysToWireframe: function polysToWireframe(numPoints, cellPts, offset, cellId, state) { // for polys we add a bunch of segments and close it if (numPoints > 2) { for (var i = 0; i < numPoints; ++i) { _double[0] = cellPts[offset + i]; _double[1] = cellPts[offset + (i + 1) % numPoints]; processCell(_double, cellId, state); } } }, stripsToWireframe: function stripsToWireframe(numPoints, cellPts, offset, cellId, state) { if (numPoints > 2) { // for strips we add a bunch of segments and close it for (var i = 0; i < numPoints - 1; ++i) { _double[0] = cellPts[offset + i]; _double[1] = cellPts[offset + i + 1]; processCell(_double, cellId, state); } for (var _i = 0; _i < numPoints - 2; _i++) { _double[0] = cellPts[offset + _i]; _double[1] = cellPts[offset + _i + 2]; processCell(_double, cellId, state); } } }, polysToSurface: function polysToSurface(npts, cellPts, offset, cellId, state) { for (var i = 0; i < npts - 2; i++) { _triple[0] = cellPts[offset]; _triple[1] = cellPts[offset + i + 1]; _triple[2] = cellPts[offset + i + 2]; processCell(_triple, cellId, state); } }, stripsToSurface: function stripsToSurface(npts, cellPts, offset, cellId, state) { for (var i = 0; i < npts - 2; i++) { _triple[0] = cellPts[offset + i]; _triple[1] = cellPts[offset + i + 1 + i % 2]; _triple[2] = cellPts[offset + i + 1 + (i + 1) % 2]; processCell(_triple, cellId, state); } } }; // ---------------------------------------------------------------------------- // vtkWebGPUIndexBufferManager methods // ---------------------------------------------------------------------------- function vtkWebGPUIndexBuffer(publicAPI, model) { // Set our className model.classHierarchy.push('vtkWebGPUIndexBuffer'); publicAPI.buildIndexBuffer = function (req) { var cellArray = req.cells; var primitiveType = req.primitiveType; var representation = req.representation; var cellOffset = req.cellOffset; var array = cellArray.getData(); var cellArraySize = array.length; var inRepName = getPrimitiveName(primitiveType); var numPts = req.numberOfPoints; var state = { provokedPointIds: new Uint8Array(numPts), // size is good extraPoints: 0, iboSize: 0, flatId: 0, iboId: 0, cellProvokedMap: new _LimitedMap() }; var func = null; if (representation === Representation.POINTS || primitiveType === PrimitiveTypes.Points) { func = _indexCellBuilders.anythingToPoints; } else if (representation === Representation.WIREFRAME || primitiveType === PrimitiveTypes.Lines) { func = _indexCellBuilders["".concat(inRepName, "ToWireframe")]; } else { func = _indexCellBuilders["".concat(inRepName, "ToSurface")]; } // first we count how many extra provoking points we need processCell = countCell; var cellId = cellOffset || 0; for (var cellArrayIndex = 0; cellArrayIndex < cellArraySize;) { state.cellProvokedMap.clear(); func(array[cellArrayIndex], array, cellArrayIndex + 1, cellId, state); cellArrayIndex += array[cellArrayIndex] + 1; cellId++; } // then we allocate the remaining structures // (we pick the best size to save space and transfer costs) if (numPts <= 0xffff) { state.flatIdToPointId = new Uint16Array(numPts + state.extraPoints); } else { state.flatIdToPointId = new Uint32Array(numPts + state.extraPoints); } if (numPts + state.extraPoints < 0x8fff) { state.pointIdToFlatId = new Int16Array(numPts); } else { state.pointIdToFlatId = new Int32Array(numPts); } if (numPts + state.extraPoints <= 0xffff) { state.ibo = new Uint16Array(state.iboSize); req.format = 'uint16'; } else { state.ibo = new Uint32Array(state.iboSize); req.format = 'uint32'; } if (cellId <= 0xffff) { state.flatIdToCellId = new Uint16Array(numPts + state.extraPoints); } else { state.flatIdToCellId = new Uint32Array(numPts + state.extraPoints); } state.pointIdToFlatId.fill(-1); state.provokedPointIds.fill(0); // and fill them in processCell = fillCell; cellId = cellOffset || 0; for (var _cellArrayIndex = 0; _cellArrayIndex < cellArraySize;) { state.cellProvokedMap.clear(); func(array[_cellArrayIndex], array, _cellArrayIndex + 1, cellId, state); _cellArrayIndex += array[_cellArrayIndex] + 1; cellId++; } delete state.provokedPointIds; delete state.pointIdToFlatId; // store the results we need req.nativeArray = state.ibo; model.flatIdToPointId = state.flatIdToPointId; model.flatIdToCellId = state.flatIdToCellId; model.flatSize = state.flatId; model.indexCount = state.iboId; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { flatIdToPointId: null, flatIdToCellId: null, flatSize: 0, indexCount: 0 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkWebGPUBuffer.extend(publicAPI, model, initialValues); macro.setGet(publicAPI, model, ['flatIdToPointId', 'flatIdToCellId', 'flatSize', 'indexCount']); vtkWebGPUIndexBuffer(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend); // ---------------------------------------------------------------------------- var vtkWebGPUIndexBuffer$1 = _objectSpread({ newInstance: newInstance, extend: extend }, Constants); export { vtkWebGPUIndexBuffer$1 as default, extend, newInstance };