123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- import defaultValue from "./defaultValue.js";
- import defined from "./defined.js";
- import DeveloperError from "./DeveloperError.js";
- /**
- * Encapsulates an algorithm to optimize triangles for the post
- * vertex-shader cache. This is based on the 2007 SIGGRAPH paper
- * 'Fast Triangle Reordering for Vertex Locality and Reduced Overdraw.'
- * The runtime is linear but several passes are made.
- *
- * @namespace Tipsify
- *
- * @see <a href='http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf'>
- * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw</a>
- * by Sander, Nehab, and Barczak
- *
- * @private
- */
- const Tipsify = {};
- /**
- * Calculates the average cache miss ratio (ACMR) for a given set of indices.
- *
- * @param {Object} options Object with the following properties:
- * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices
- * in the vertex buffer that define the geometry's triangles.
- * @param {Number} [options.maximumIndex] The maximum value of the elements in <code>args.indices</code>.
- * If not supplied, this value will be computed.
- * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time.
- * @returns {Number} The average cache miss ratio (ACMR).
- *
- * @exception {DeveloperError} indices length must be a multiple of three.
- * @exception {DeveloperError} cacheSize must be greater than two.
- *
- * @example
- * const indices = [0, 1, 2, 3, 4, 5];
- * const maxIndex = 5;
- * const cacheSize = 3;
- * const acmr = Cesium.Tipsify.calculateACMR({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize});
- */
- Tipsify.calculateACMR = function (options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- const indices = options.indices;
- let maximumIndex = options.maximumIndex;
- const cacheSize = defaultValue(options.cacheSize, 24);
- //>>includeStart('debug', pragmas.debug);
- if (!defined(indices)) {
- throw new DeveloperError("indices is required.");
- }
- //>>includeEnd('debug');
- const numIndices = indices.length;
- //>>includeStart('debug', pragmas.debug);
- if (numIndices < 3 || numIndices % 3 !== 0) {
- throw new DeveloperError("indices length must be a multiple of three.");
- }
- if (maximumIndex <= 0) {
- throw new DeveloperError("maximumIndex must be greater than zero.");
- }
- if (cacheSize < 3) {
- throw new DeveloperError("cacheSize must be greater than two.");
- }
- //>>includeEnd('debug');
- // Compute the maximumIndex if not given
- if (!defined(maximumIndex)) {
- maximumIndex = 0;
- let currentIndex = 0;
- let intoIndices = indices[currentIndex];
- while (currentIndex < numIndices) {
- if (intoIndices > maximumIndex) {
- maximumIndex = intoIndices;
- }
- ++currentIndex;
- intoIndices = indices[currentIndex];
- }
- }
- // Vertex time stamps
- const vertexTimeStamps = [];
- for (let i = 0; i < maximumIndex + 1; i++) {
- vertexTimeStamps[i] = 0;
- }
- // Cache processing
- let s = cacheSize + 1;
- for (let j = 0; j < numIndices; ++j) {
- if (s - vertexTimeStamps[indices[j]] > cacheSize) {
- vertexTimeStamps[indices[j]] = s;
- ++s;
- }
- }
- return (s - cacheSize + 1) / (numIndices / 3);
- };
- /**
- * Optimizes triangles for the post-vertex shader cache.
- *
- * @param {Object} options Object with the following properties:
- * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices
- * in the vertex buffer that define the geometry's triangles.
- * @param {Number} [options.maximumIndex] The maximum value of the elements in <code>args.indices</code>.
- * If not supplied, this value will be computed.
- * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time.
- * @returns {Number[]} A list of the input indices in an optimized order.
- *
- * @exception {DeveloperError} indices length must be a multiple of three.
- * @exception {DeveloperError} cacheSize must be greater than two.
- *
- * @example
- * const indices = [0, 1, 2, 3, 4, 5];
- * const maxIndex = 5;
- * const cacheSize = 3;
- * const reorderedIndices = Cesium.Tipsify.tipsify({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize});
- */
- Tipsify.tipsify = function (options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- const indices = options.indices;
- const maximumIndex = options.maximumIndex;
- const cacheSize = defaultValue(options.cacheSize, 24);
- let cursor;
- function skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne) {
- while (deadEnd.length >= 1) {
- // while the stack is not empty
- const d = deadEnd[deadEnd.length - 1]; // top of the stack
- deadEnd.splice(deadEnd.length - 1, 1); // pop the stack
- if (vertices[d].numLiveTriangles > 0) {
- return d;
- }
- }
- while (cursor < maximumIndexPlusOne) {
- if (vertices[cursor].numLiveTriangles > 0) {
- ++cursor;
- return cursor - 1;
- }
- ++cursor;
- }
- return -1;
- }
- function getNextVertex(
- indices,
- cacheSize,
- oneRing,
- vertices,
- s,
- deadEnd,
- maximumIndexPlusOne
- ) {
- let n = -1;
- let p;
- let m = -1;
- let itOneRing = 0;
- while (itOneRing < oneRing.length) {
- const index = oneRing[itOneRing];
- if (vertices[index].numLiveTriangles) {
- p = 0;
- if (
- s -
- vertices[index].timeStamp +
- 2 * vertices[index].numLiveTriangles <=
- cacheSize
- ) {
- p = s - vertices[index].timeStamp;
- }
- if (p > m || m === -1) {
- m = p;
- n = index;
- }
- }
- ++itOneRing;
- }
- if (n === -1) {
- return skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne);
- }
- return n;
- }
- //>>includeStart('debug', pragmas.debug);
- if (!defined(indices)) {
- throw new DeveloperError("indices is required.");
- }
- //>>includeEnd('debug');
- const numIndices = indices.length;
- //>>includeStart('debug', pragmas.debug);
- if (numIndices < 3 || numIndices % 3 !== 0) {
- throw new DeveloperError("indices length must be a multiple of three.");
- }
- if (maximumIndex <= 0) {
- throw new DeveloperError("maximumIndex must be greater than zero.");
- }
- if (cacheSize < 3) {
- throw new DeveloperError("cacheSize must be greater than two.");
- }
- //>>includeEnd('debug');
- // Determine maximum index
- let maximumIndexPlusOne = 0;
- let currentIndex = 0;
- let intoIndices = indices[currentIndex];
- const endIndex = numIndices;
- if (defined(maximumIndex)) {
- maximumIndexPlusOne = maximumIndex + 1;
- } else {
- while (currentIndex < endIndex) {
- if (intoIndices > maximumIndexPlusOne) {
- maximumIndexPlusOne = intoIndices;
- }
- ++currentIndex;
- intoIndices = indices[currentIndex];
- }
- if (maximumIndexPlusOne === -1) {
- return 0;
- }
- ++maximumIndexPlusOne;
- }
- // Vertices
- const vertices = [];
- let i;
- for (i = 0; i < maximumIndexPlusOne; i++) {
- vertices[i] = {
- numLiveTriangles: 0,
- timeStamp: 0,
- vertexTriangles: [],
- };
- }
- currentIndex = 0;
- let triangle = 0;
- while (currentIndex < endIndex) {
- vertices[indices[currentIndex]].vertexTriangles.push(triangle);
- ++vertices[indices[currentIndex]].numLiveTriangles;
- vertices[indices[currentIndex + 1]].vertexTriangles.push(triangle);
- ++vertices[indices[currentIndex + 1]].numLiveTriangles;
- vertices[indices[currentIndex + 2]].vertexTriangles.push(triangle);
- ++vertices[indices[currentIndex + 2]].numLiveTriangles;
- ++triangle;
- currentIndex += 3;
- }
- // Starting index
- let f = 0;
- // Time Stamp
- let s = cacheSize + 1;
- cursor = 1;
- // Process
- let oneRing = [];
- const deadEnd = []; //Stack
- let vertex;
- let intoVertices;
- let currentOutputIndex = 0;
- const outputIndices = [];
- const numTriangles = numIndices / 3;
- const triangleEmitted = [];
- for (i = 0; i < numTriangles; i++) {
- triangleEmitted[i] = false;
- }
- let index;
- let limit;
- while (f !== -1) {
- oneRing = [];
- intoVertices = vertices[f];
- limit = intoVertices.vertexTriangles.length;
- for (let k = 0; k < limit; ++k) {
- triangle = intoVertices.vertexTriangles[k];
- if (!triangleEmitted[triangle]) {
- triangleEmitted[triangle] = true;
- currentIndex = triangle + triangle + triangle;
- for (let j = 0; j < 3; ++j) {
- // Set this index as a possible next index
- index = indices[currentIndex];
- oneRing.push(index);
- deadEnd.push(index);
- // Output index
- outputIndices[currentOutputIndex] = index;
- ++currentOutputIndex;
- // Cache processing
- vertex = vertices[index];
- --vertex.numLiveTriangles;
- if (s - vertex.timeStamp > cacheSize) {
- vertex.timeStamp = s;
- ++s;
- }
- ++currentIndex;
- }
- }
- }
- f = getNextVertex(
- indices,
- cacheSize,
- oneRing,
- vertices,
- s,
- deadEnd,
- maximumIndexPlusOne
- );
- }
- return outputIndices;
- };
- export default Tipsify;
|