Skip to content

Commit

Permalink
feat: Add worker for LASParser
Browse files Browse the repository at this point in the history
  • Loading branch information
Desplandis committed Feb 12, 2024
1 parent 1daf66c commit 8ea5bc0
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 3 deletions.
115 changes: 112 additions & 3 deletions src/Parser/LASParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,115 @@ import LASLoader from 'Parser/LASLoader';

const lasLoader = new LASLoader();

const WORKER_LIMIT = 3;
/** @type{Worker[]} */
const workerPool = [];
let taskIdCounter = 0;

function createWorker() {
const worker = new Worker(new URL('../Worker/LASWorker.js', import.meta.url));

worker._callbacks = {};
worker._taskCost = {};
worker._taskLoad = 0;

worker.onmessage = (event) => {
const { id, attributes } = event.data;

// console.log(`Task ${id} status: ???`);

if (attributes !== undefined) {
worker._callbacks[id].resolve(attributes);
} else {
worker._callbacks[id].reject();
}

returnWorker(worker, id);
};

return worker;
}

function getWorker(/** @type{number} */ taskId, /** @type{number} */ taskCost) {
// console.warn(`[${taskId}]: get with ${taskCost}`);
let worker;
if (workerPool.length < WORKER_LIMIT) {
worker = createWorker();
workerPool.push(worker);
} else {
const pool = workerPool.sort((a, b) => b._taskLoad - a._taskLoad);
worker = pool[pool.length - 1];
}

worker._taskCost[taskId] = taskCost;
worker._taskLoad += taskCost;

return worker;
}

function returnWorker(worker, taskId) {
// console.log(`[${taskId}]: return with ${worker._taskCost[taskId]}`);
worker._taskLoad -= worker._taskCost[taskId];
delete worker._callbacks[taskId];
delete worker._taskCost[taskId];
}

/**
* Parses a LAS or LAZ (LASZip) file. Note that this function is
* **CPU-bound** and shall be parallelised in a dedicated worker.
* @param {ArrayBuffer} data - Binary data to parse.
* @param {Object} [options] - Parsing options.
* @param {8 | 16} [options.colorDepth] - Color depth encoding (in bits).
* Either 8 or 16 bits. Defaults to 8 bits for LAS 1.2 and 16 bits for later
* versions (as mandatory by the specification)
*/
async function parseFile(data, options) {
const useWorker = true && !!window.Worker; // TODO: Add as options or when getWorker fails ?
// TODO: Worker can throw SecurityError, NetworkError or SyntaxError

if (useWorker) {
const taskId = taskIdCounter++;
const worker = getWorker(taskId, data.byteLength);
return new Promise((resolve, reject) => {
worker._callbacks[taskId] = { resolve, reject };
worker.postMessage({
id: taskId,
data,
options: {
colorDepth: options?.colorDepth,
},
});
});
}

return lasLoader.parseFile(data, options);
}

async function parseChunk(data, options) {
const useWorker = true && !!window.Worker; // TODO: Add as options or when getWorker fails ?
// TODO: Worker can throw SecurityError, NetworkError or SyntaxError

if (useWorker) {
const taskId = taskIdCounter++;
const worker = getWorker(taskId, data.byteLength);
return new Promise((resolve, reject) => {
worker._callbacks[taskId] = { resolve, reject };
worker.postMessage({
id: taskId,
data,
options: {
colorDepth: options?.colorDepth,
pointCount: options.pointCount,
header: options.header,
},
});
});
}

return lasLoader.parseChunk(data, options);
}


function buildBufferGeometry(attributes) {
const geometry = new THREE.BufferGeometry();

Expand Down Expand Up @@ -43,7 +152,7 @@ function buildBufferGeometry(attributes) {
* @module LASParser
*/
export default {
/*
/**
* Set the laz-perf decoder path.
* @param {string} path - path to `laz-perf.wasm` folder.
*/
Expand Down Expand Up @@ -78,7 +187,7 @@ export default {
* `THREE.BufferGeometry`.
*/
parseChunk(data, options = {}) {
return lasLoader.parseChunk(data, options.in).then((parsedData) => {
return parseChunk(data, options.in).then((parsedData) => {
const geometry = buildBufferGeometry(parsedData.attributes);
geometry.userData = parsedData.header;
geometry.computeBoundingBox();
Expand All @@ -104,7 +213,7 @@ export default {
if (options.out?.skip) {
console.warn("Warning: options 'skip' not supported anymore");
}
return lasLoader.parseFile(data, {
return parseFile(data, {
colorDepth: options.in?.colorDepth,
}).then((parsedData) => {
const geometry = buildBufferGeometry(parsedData.attributes);
Expand Down
17 changes: 17 additions & 0 deletions src/Worker/LASWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import LASLoader from 'Parser/LASLoader';

const loader = new LASLoader();

self.onmessage = (event) => {
const { id, data, options } = event.data;

if (!options.pointCount) {
loader.parseFile(data, options)
.then(attributes => self.postMessage({ id, attributes }))
.catch(() => self.postMessage({ id, attributes: undefined }));
} else {
loader.parseChunk(data, options)
.then(attributes => self.postMessage({ id, attributes }))
.catch(() => self.postMessage({ id, attributes: undefined }));
}
};

0 comments on commit 8ea5bc0

Please sign in to comment.