aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJames Barnett <noreply@jamesbarnett.xyz>2025-07-26 14:44:33 +0100
committerJames Barnett <noreply@jamesbarnett.xyz>2025-07-26 14:44:33 +0100
commit7b06f4e9958562f3df09eecc368e92af013e5b39 (patch)
tree14050e7bd1d77cee6b806b07d177bd8734a73e6d /src
parentebcef463bb6c447e788c90fe4235f2504de186d3 (diff)
downloadjs-raytracer-7b06f4e9958562f3df09eecc368e92af013e5b39.tar.xz
js-raytracer-7b06f4e9958562f3df09eecc368e92af013e5b39.zip
Add configurable render chunk size
Diffstat (limited to 'src')
-rw-r--r--src/RaytraceDispatcher.ts52
-rw-r--r--src/index.ts25
-rw-r--r--src/models/RaytraceContext.ts1
3 files changed, 57 insertions, 21 deletions
diff --git a/src/RaytraceDispatcher.ts b/src/RaytraceDispatcher.ts
index b8b5e6a..eeae5ce 100644
--- a/src/RaytraceDispatcher.ts
+++ b/src/RaytraceDispatcher.ts
@@ -12,7 +12,6 @@ export class RaytraceDispatcher {
private readonly chunkQueue: FrameChunk[];
private readonly raytraceWorkers: Worker[];
private completedWorkers = 0;
- private chunkBorderWidth = 1;
constructor(
readonly framebuffer: Framebuffer,
@@ -27,25 +26,14 @@ export class RaytraceDispatcher {
}
requestRender() {
- let chunkHeight, chunkWidth;
-
- if (this.context.height <= 720) {
- chunkHeight = 64;
- chunkWidth = 64;
- this.chunkBorderWidth = 2;
- } else {
- chunkHeight = 128;
- chunkWidth = 128;
- this.chunkBorderWidth = 5;
- }
-
// 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.chunkQueue.push(new FrameChunk(x, y, chunkWidth, chunkHeight));
+ const chunkSize = this.getChunkSize();
+ for (let y = 0; y < this.context.height; y+= chunkSize) {
+ for (let x = 0 ; x < this.context.width; x+= chunkSize) {
+ this.chunkQueue.push(new FrameChunk(x, y, chunkSize, chunkSize));
}
}
- this.logger.log(`Scene split into ${this.chunkQueue.length} chunks of ${chunkWidth}x${chunkHeight}`);
+ this.logger.log(`Scene split into ${this.chunkQueue.length} chunks of ${chunkSize}x${chunkSize}`);
// Spawn worker threads
for (let n = 0; n < this.context.options.numThreads; n++) {
@@ -61,9 +49,15 @@ export class RaytraceDispatcher {
}
}
+ public stopRender() {
+ for (let worker of this.raytraceWorkers) {
+ worker.terminate();
+ }
+ }
+
private raytraceNextChunk(worker: Worker) {
const chunk = this.getNextChunk();
- this.drawChunkBorder(chunk, this.chunkBorderWidth);
+ this.drawChunkBorder(chunk);
worker.postMessage({
type: 'raytraceStart',
chunk: chunk,
@@ -145,10 +139,17 @@ export class RaytraceDispatcher {
this.framebuffer.flush();
}
- private drawChunkBorder(chunk: FrameChunk, borderWidth: number) {
+ private drawChunkBorder(chunk: FrameChunk) {
const width = chunk.width;
const height = chunk.height;
+ let borderWidth = 0;
+ if (this.context.height <= 720) {
+ borderWidth = 2;
+ } else {
+ borderWidth = 5;
+ }
+
const borderColour = new Colour(220, 128, 128);
const unrenderedAreaColour = new Colour(160, 160, 160);
@@ -166,4 +167,17 @@ export class RaytraceDispatcher {
this.framebuffer.flush();
}
+
+ private getChunkSize(): number {
+ if (this.context.options.chunkSize === 0) {
+ // Auto sizing based on scene height
+ if (this.context.height <= 720) {
+ return 64;
+ } else {
+ return 128;
+ }
+ } else {
+ return this.context.options.chunkSize;
+ }
+ }
}
diff --git a/src/index.ts b/src/index.ts
index f58cc5b..3dab80f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,12 +8,21 @@ import {ChunkAllocationMode, RaytraceContext, RaytracerOptions} from './models/R
import {Vector} from './models/Vector';
import {Logger} from './Logger';
+let dispatcher: RaytraceDispatcher;
+
function render() {
getRenderButton().classList.add('loading');
- const dispatcher = initDispatcher(parseOptions());
+ getStopRenderButton().classList.remove('d-hide');
+ getViewFullButton().classList.add('d-hide');
+ dispatcher = initDispatcher(parseOptions());
dispatcher.requestRender();
}
+function stopRender() {
+ dispatcher.stopRender();
+ onRenderComplete();
+}
+
function initDispatcher(options: RaytracerOptions): RaytraceDispatcher {
const {width, height} = parseResolution();
@@ -110,6 +119,7 @@ function parseOptions(): RaytracerOptions {
maxRecurseDepth: 5,
maxDrawDistance: 1000,
directMemoryTransfer: getInputElement('direct-transfer').checked,
+ chunkSize: parseInt(getInputElement('chunk-size').value, 10),
chunkAllocationMode: getChunkAllocationMode()
};
}
@@ -148,8 +158,9 @@ function getChunkAllocationMode(): ChunkAllocationMode {
function registerEventListeners() {
getRenderButton().addEventListener('click', render);
+ getStopRenderButton().addEventListener('click', stopRender);
- document.getElementById('view-full')!.addEventListener('click', () => {
+ getViewFullButton().addEventListener('click', () => {
const canvas = document.getElementById(
'render-output'
) as HTMLCanvasElement;
@@ -177,12 +188,22 @@ function getDesiredThreadCount(): number {
function onRenderComplete() {
getRenderButton().classList.remove('loading');
+ getStopRenderButton().classList.add('d-hide');
+ getViewFullButton().classList.remove('d-hide');
}
function getRenderButton(): HTMLElement {
return document.getElementById('render')!;
}
+function getStopRenderButton(): HTMLElement {
+ return document.getElementById('stop-render')!;
+}
+
+function getViewFullButton(): HTMLElement {
+ return document.getElementById('view-full')!;
+}
+
function getInputElement(elementId: string) {
return document.getElementById(elementId) as HTMLInputElement;
}
diff --git a/src/models/RaytraceContext.ts b/src/models/RaytraceContext.ts
index d028c1a..1f22d32 100644
--- a/src/models/RaytraceContext.ts
+++ b/src/models/RaytraceContext.ts
@@ -39,6 +39,7 @@ export interface RaytracerOptions {
maxRecurseDepth: number;
maxDrawDistance: number;
directMemoryTransfer: boolean;
+ chunkSize: number;
chunkAllocationMode: ChunkAllocationMode;
}