diff options
| author | James Barnett <noreply@jamesbarnett.xyz> | 2025-07-26 13:47:20 +0100 |
|---|---|---|
| committer | James Barnett <noreply@jamesbarnett.xyz> | 2025-07-26 13:47:20 +0100 |
| commit | ebcef463bb6c447e788c90fe4235f2504de186d3 (patch) | |
| tree | c7500ef92b51544195ef9fb75744ac2ce39091a8 /src/RaytraceDispatcher.ts | |
| parent | 10bfc58085c6ab7e62077a1a9b6a6d922fffb025 (diff) | |
| download | js-raytracer-ebcef463bb6c447e788c90fe4235f2504de186d3.tar.xz js-raytracer-ebcef463bb6c447e788c90fe4235f2504de186d3.zip | |
Add configurable render chunk allocation modes
Diffstat (limited to 'src/RaytraceDispatcher.ts')
| -rw-r--r-- | src/RaytraceDispatcher.ts | 65 |
1 files changed, 49 insertions, 16 deletions
diff --git a/src/RaytraceDispatcher.ts b/src/RaytraceDispatcher.ts index 6d1bbd7..b8b5e6a 100644 --- a/src/RaytraceDispatcher.ts +++ b/src/RaytraceDispatcher.ts @@ -1,5 +1,5 @@ import {Framebuffer} from './Framebuffer'; -import {RaytraceContext} from './models/RaytraceContext'; +import {ChunkAllocationMode, RaytraceContext} from './models/RaytraceContext'; import {instanceToPlain} from 'class-transformer'; import 'reflect-metadata'; import {Logger} from './Logger'; @@ -9,9 +9,8 @@ import {Colour} from "./models/Colour"; export class RaytraceDispatcher { private readonly renderStartMs: number; private readonly contextJson: String; - private readonly chunks: FrameChunk[]; + private readonly chunkQueue: FrameChunk[]; private readonly raytraceWorkers: Worker[]; - private nextChunkIndex = 0; private completedWorkers = 0; private chunkBorderWidth = 1; @@ -23,7 +22,7 @@ export class RaytraceDispatcher { ) { this.renderStartMs = new Date().getTime(); this.contextJson = JSON.stringify(instanceToPlain(context)) - this.chunks = []; + this.chunkQueue = []; this.raytraceWorkers = []; } @@ -43,15 +42,15 @@ export class RaytraceDispatcher { // Process scene into chunks for (let y = 0; y < this.context.height; y+= chunkHeight) { for (let x = 0 ; x < this.context.width; x+= chunkWidth) { - this.chunks.push(new FrameChunk(x, y, chunkWidth, chunkHeight)); + this.chunkQueue.push(new FrameChunk(x, y, chunkWidth, chunkHeight)); } } - this.logger.log(`Scene split into ${this.chunks.length} chunks of ${chunkWidth}x${chunkHeight}`); + this.logger.log(`Scene split into ${this.chunkQueue.length} chunks of ${chunkWidth}x${chunkHeight}`); // Spawn worker threads for (let n = 0; n < this.context.options.numThreads; n++) { const worker = new Worker(new URL('./Raytracer.ts', import.meta.url)); - worker.onmessage = (event) => { this.onMessageHandler(worker, event) }; + worker.onmessage = (event) => { this.processRaytraceWorkerResult(worker, event) }; this.raytraceWorkers.push(worker); } this.logger.log(`Spawned ${this.context.options.numThreads} render threads`); @@ -63,24 +62,33 @@ export class RaytraceDispatcher { } private raytraceNextChunk(worker: Worker) { - const chunkIndex = this.nextChunkIndex++; - let chunk = this.chunks[chunkIndex]; + const chunk = this.getNextChunk(); this.drawChunkBorder(chunk, this.chunkBorderWidth); worker.postMessage({ type: 'raytraceStart', chunk: chunk, - chunkIndex: chunkIndex, context: this.contextJson, }); } - private onMessageHandler(worker: Worker, message: MessageEvent) { - if (message.data.type === 'raytraceComplete') { - this.writeChunkToFramebuffer(message.data.chunkIndex, message.data.resultBuffer); + private getNextChunk(): FrameChunk { + switch(this.context.options.chunkAllocationMode) { + case ChunkAllocationMode.SEQUENTIAL: + return this.getNextChunkSequential(); + case ChunkAllocationMode.RANDOM: + return this.getNextChunkRandom(); + case ChunkAllocationMode.CENTER_TO_EDGE: + return this.getNextChunkCenterToEdge(); + case ChunkAllocationMode.EDGE_TO_CENTER: + return this.getNextChunkEdgeToCenter(); } + } + + private processRaytraceWorkerResult(worker: Worker, message: MessageEvent) { + this.writeChunkToFramebuffer(message.data.chunk, message.data.resultBuffer); // Queue next work if available - if (this.nextChunkIndex < this.chunks.length) { + if (this.chunkQueue.length > 0) { this.raytraceNextChunk(worker); } else { worker.terminate(); @@ -93,9 +101,34 @@ export class RaytraceDispatcher { } } - private writeChunkToFramebuffer(chunkIndex: number, data: ArrayBuffer) { + private getNextChunkSequential(): FrameChunk { + return this.chunkQueue.shift()!; + } + + private getNextChunkRandom(): FrameChunk { + const index = Math.floor(Math.random() * this.chunkQueue.length); + const chunk = this.chunkQueue[index]; + this.chunkQueue.splice(index, 1); + return chunk + } + + private getNextChunkCenterToEdge(): FrameChunk { + const index = Math.floor((this.chunkQueue.length - 1) / 2); + const chunk = this.chunkQueue[index]; + this.chunkQueue.splice(index, 1); + return chunk; + } + + private getNextChunkEdgeToCenter(): FrameChunk { + if (this.chunkQueue.length % 2 == 0) { + return this.chunkQueue.shift()!; + } else { + return this.chunkQueue.pop()!; + } + } + + private writeChunkToFramebuffer(chunk: FrameChunk, data: ArrayBuffer) { const clampedRowData = new Uint8ClampedArray(data); - const chunk = this.chunks[chunkIndex]; for (let y = 0; y < chunk.height; y++) { for (let x = 0; x < chunk.width; x++) { |