UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

163 lines (147 loc) 6.5 kB
import { OperationType } from "./OperationType.js"; /** * * @param {Operation[]} queue */ export function optimizeCommandQueue(queue) { /** * TODO this is pretty slow and should be optimized * @param {Array.<Operation>} queue */ function cutRedundantOperations(queue) { const queueLength = queue.length; loop_Main: for (let i = queueLength - 1; i >= 0; i--) { const opA = queue[i]; if (opA.operator === OperationType.Add) { //nothing to do continue; } const operandsA = opA.operands; if (opA.operator === OperationType.Remove) { //hunt and delete operations before this remove that affect deleted references for (let k = 0, kl = operandsA.length; k < kl; k++) { const refA = operandsA[k]; //walk the queue back and remove all previous operations on these references loop_B: for (let j = i - 1; j >= 0; j--) { const opB = queue[j]; const operandsB = opB.operands; const operatorB = opB.operator; if (operatorB === OperationType.WriteAttribute) { const refB = operandsB[0]; if (refA === refB) { //cut operation queue.splice(j, 1); i--; } } else if (operatorB === OperationType.Add) { for (let m = 0, ml = operandsB.length; m < ml; m++) { const refB = operandsB[m]; if (refA === refB) { let jump = 0; //delete operation and add operation cancel each other out if (kl > 1) { operandsA.splice(k, 1); kl--; k--; } else { queue.splice(i, 1); i--; jump |= 2; } if (ml > 1) { operandsB.splice(m, 1); m--; ml--; } else { //cut operation from the queue queue.splice(j, 1); i--; jump |= 1; } if ((jump & 2) !== 0) { continue loop_Main; } else if (jump !== 0) { continue loop_B; } } } } } } } else if (opA.operator === OperationType.WriteAttribute) { //find other operations that modify the same attribute, any previous operations are redundant at this point const refA = operandsA[0]; const attributeIndexA = operandsA[1]; //walk the queue back and remove all previous operations on these references for (let j = i - 1; j >= 0; j--) { const opB = queue[j]; if (opB.operator !== OperationType.WriteAttribute) { //ignore continue; } const operandsB = opB.operands; if (operandsB[0] === refA && operandsB[1] === attributeIndexA) { //operation will be overwritten anyway, cut it queue.splice(j, 1); i--; } } } } } /** * * @param {Array.<Operation>} queue */ function groupOperations(queue) { let l = queue.length; for (let i = 0; i < l; i++) { const opA = queue[i]; const operatorA = opA.operator; const operandsA = opA.operands; if (operatorA === OperationType.Add || operatorA === OperationType.Remove) { for (let j = i + 1; j < l; j++) { const opB = queue[j]; if (opB.operator === operatorA) { const operandsB = opB.operands; const numOperands = operandsB.length; for (let k = 0; k < numOperands; k++) { const refB = operandsB[k]; if (operandsA.indexOf(refB) === -1) { operandsA.push(refB); } } queue.splice(j, 1); j--; l--; } } } } } /** * @param {Array.<Operation>} queue */ function sortOperations(queue) { queue.sort(function (a, b) { const operatorA = a.operator; const operatorB = b.operator; //it's important that Add and Remove operations are sorted to the front of the queue, before Write operations if (operatorA < operatorB) { return -1; } else if (operatorB > operatorA) { return 1; } if (operatorA !== OperationType.WriteAttribute) { return 0; } const attributeIndexA = a.operands[1]; const attributeIndexB = b.operands[1]; //sort write operations by buffer index return attributeIndexB - attributeIndexA; }); } cutRedundantOperations(queue); groupOperations(queue); sortOperations(queue); }