@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
163 lines (147 loc) • 6.5 kB
JavaScript
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);
}