Skip to content

Commit

Permalink
Switch to asynchronous handling of computation and rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
s-macke committed Jun 26, 2024
1 parent 0f1a98f commit 04b138d
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 28 deletions.
8 changes: 7 additions & 1 deletion src/scripts/AbstractGPURunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ export enum RunnerType {
HTML = 1,
GRAPHIC = 2,
ANIM= 3,
BENCHMARK= 4,
ASYNCANIM= 4,
BENCHMARK= 5,
}

export interface GPURunner {
getType(): RunnerType;
getRenderInfo(): {textures: Texture[], fragmentShaderFilenames: string[]};
getCommandBuffer(): GPUCommandBuffer;
Run(): Promise<void>;
Render(): Promise<void>;
Init(): Promise<void>;
Destroy(): Promise<void>;
}
Expand All @@ -22,6 +24,10 @@ export abstract class GPUAbstractRunner implements GPURunner {
public abstract Init(): Promise<void>
public abstract Run(): Promise<void>

Render(): Promise<void> {
throw new Error("Method not implemented.");
}

getCommandBuffer(): GPUCommandBuffer {
throw new Error("Method not implemented.");
}
Expand Down
6 changes: 5 additions & 1 deletion src/scripts/GPURenderRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export class GPURenderRunner implements GPURunner {
this.runner = runner
}

async Render() {
GPU.device.queue.submit([this.render.getCommandBuffer()])
}

getType(): RunnerType {
return this.runner.getType()
}
Expand All @@ -28,7 +32,7 @@ export class GPURenderRunner implements GPURunner {
}

async Run() {
GPU.device.queue.submit([this.runner.getCommandBuffer(), this.render.getCommandBuffer()])
await this.runner.Run()
}

getCommandBuffer(): GPUCommandBuffer {
Expand Down
48 changes: 47 additions & 1 deletion src/scripts/RunGPURunner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {GPU} from "./webgpu/gpu";
import {GPURunner, RunnerType} from "./AbstractGPURunner";
import {MeasureFrame, ShowError} from "./ui";
import {MeasureFrame, MeasureIteration, ShowError} from "./ui";

let stop_immediately = true;

Expand Down Expand Up @@ -89,6 +89,8 @@ async function HandleAnimation(runner: GPURunner) {
let frame = async () => {
try {
await runner.Run()
await runner.Render()
await GPU.device.queue.onSubmittedWorkDone()
} catch (e) {
ShowError("GPU error", e as Error)
await runner.Destroy()
Expand All @@ -109,6 +111,48 @@ async function HandleAnimation(runner: GPURunner) {
})
}

async function HandleAsyncAnimation(runner: GPURunner) {
await SwitchToGraphic()

// never return from this function unless the animation is stopped
await new Promise(async resolve => {
let nIter = 0
let queuePartFinished = async() => {
if (stop_immediately) {
return
}
await runner.Run()
nIter++
GPU.device.queue.onSubmittedWorkDone().then(() => queuePartFinished())
}
// fill the queue
queuePartFinished().then(r => {})
queuePartFinished().then(r => {})

let frame = async () => {
try {
await runner.Render()
} catch (e) {
ShowError("GPU error", e as Error)
await runner.Destroy()
resolve(0)
throw e
}
MeasureIteration(nIter)
if (stop_immediately) {
await GPU.device.queue.onSubmittedWorkDone()
await runner.Destroy()
document.getElementById("textFps").innerHTML = ""
resolve(0)
return;
}
requestAnimationFrame(frame)
}
requestAnimationFrame(frame)
})
}


async function HandleBenchmark(runner: GPURunner) {
await SwitchToHTML()

Expand Down Expand Up @@ -160,6 +204,8 @@ export async function HandleRunner(runner: GPURunner) {
return HandleGraphic(runner)
case RunnerType.ANIM:
return HandleAnimation(runner)
case RunnerType.ASYNCANIM:
return HandleAsyncAnimation(runner)
case RunnerType.BENCHMARK:
return HandleBenchmark(runner)
}
Expand Down
4 changes: 1 addition & 3 deletions src/scripts/diffuse/diffuse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {GPU} from "../webgpu/gpu";
import {Texture} from "../webgpu/texture";
import {Buffer} from "../webgpu/buffer";
import {GPUAbstractRunner, RunnerType} from "../AbstractGPURunner";
import {Render} from "../render/render";

export class Diffuse extends GPUAbstractRunner {
width: number;
Expand Down Expand Up @@ -47,7 +46,7 @@ export class Diffuse extends GPUAbstractRunner {
}

override getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}

override async Destroy() {
Expand Down Expand Up @@ -275,7 +274,6 @@ export class Diffuse extends GPUAbstractRunner {

override async Run() {
GPU.device.queue.submit([this.getCommandBuffer()])
await GPU.device.queue.onSubmittedWorkDone()
}

override getRenderInfo(): { textures: Texture[]; fragmentShaderFilenames: string[] } {
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/diffuse/diffuse.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct StagingBuffer {
@group(0) @binding(2) var<uniform> staging: StagingBuffer;

const PI = 3.14159265359;
const SAMPLES = 40;
const SAMPLES = 10;
const MAXDEPTH = 4;

struct Ray {
Expand Down
12 changes: 8 additions & 4 deletions src/scripts/fluid/fluid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {GPUAbstractRunner, RunnerType} from "../AbstractGPURunner";

export class Fluid extends GPUAbstractRunner {
public getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}
public async Destroy() {
}
Expand Down Expand Up @@ -118,15 +118,19 @@ export class Fluid extends GPUAbstractRunner {
this.div.GetCommandBuffer(),
this.poisson.GetCommandBuffer(),
this.project.GetCommandBuffer(),
this.render.GetCommandBuffer()
])
await GPU.device.queue.onSubmittedWorkDone();
//await GPU.Render(this.transport.texturea);
//await GPU.Render(this.transport.texturea);
//await GPU.Render(this.poisson.pressurea);
}

async InitVelocity() {
async Render() {
GPU.device.queue.submit([
this.render.GetCommandBuffer()
])
}

async InitVelocity() {
let vel = new Uint16Array(this.width * this.height * 4)
/*
for (let j = 0; j < this.height; j++)
Expand Down
10 changes: 6 additions & 4 deletions src/scripts/light/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class LightPropagation extends GPUAbstractRunner {
}

getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}

async Destroy() {
Expand Down Expand Up @@ -153,7 +153,7 @@ export class LightPropagation extends GPUAbstractRunner {
GPU.device.queue.writeBuffer(this.stagingBuffer.buffer, 0, this.stagingData)

let encoder: GPUCommandEncoder = GPU.CreateCommandEncoder();
for(let i = 0; i < 40; i++) {
for(let i = 0; i < 10; i++) {
let pass: GPUComputePassEncoder = encoder.beginComputePass();
pass.setBindGroup(0, this.bind_group);
pass.setBindGroup(1, this.scene_bind_group);
Expand All @@ -170,8 +170,10 @@ export class LightPropagation extends GPUAbstractRunner {
}

async Run() {
GPU.device.queue.submit([this.scene.GetCommandBuffer(), this.GetCommandBuffer(), this.render.getCommandBuffer()]);
await GPU.device.queue.onSubmittedWorkDone();
GPU.device.queue.submit([this.scene.GetCommandBuffer(), this.GetCommandBuffer()]);
}

async Render() {
GPU.device.queue.submit([this.render.getCommandBuffer()]);
}
}
1 change: 0 additions & 1 deletion src/scripts/light/scene/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,5 @@ export class LightScene {

async Run() {
GPU.device.queue.submit([this.GetCommandBuffer()]);
await GPU.device.queue.onSubmittedWorkDone()
}
}
10 changes: 6 additions & 4 deletions src/scripts/light2/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class LightPropagation2 extends GPUAbstractRunner {
}

getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}

async Destroy() {
Expand Down Expand Up @@ -171,7 +171,7 @@ export class LightPropagation2 extends GPUAbstractRunner {
GPU.device.queue.writeBuffer(this.stagingBuffer.buffer, 0, this.stagingData)

let encoder: GPUCommandEncoder = GPU.CreateCommandEncoder();
for(let i = 0; i < 80; i++) {
for(let i = 0; i < 10; i++) {
let pass: GPUComputePassEncoder = encoder.beginComputePass();
pass.setBindGroup(0, this.bind_group_atob);
pass.setBindGroup(1, this.scene_bind_group);
Expand All @@ -196,8 +196,10 @@ export class LightPropagation2 extends GPUAbstractRunner {
}

async Run() {
GPU.device.queue.submit([this.scene.GetCommandBuffer(), this.GetCommandBuffer(), this.render.getCommandBuffer()]);
await GPU.device.queue.onSubmittedWorkDone();
GPU.device.queue.submit([this.scene.GetCommandBuffer(), this.GetCommandBuffer()]);
}

async Render() {
GPU.device.queue.submit([this.render.getCommandBuffer()]);
}
}
10 changes: 7 additions & 3 deletions src/scripts/light_monte_carlo_path_tracing/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class LightMonteCarloPathTracing extends GPUAbstractRunner {
}

getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}

async Destroy() {
Expand Down Expand Up @@ -192,8 +192,12 @@ export class LightMonteCarloPathTracing extends GPUAbstractRunner {
}

async Run() {
GPU.device.queue.submit([this.scene.GetCommandBuffer(), this.GetCommandBuffer(), this.render.getCommandBuffer()]);
await GPU.device.queue.onSubmittedWorkDone();
GPU.device.queue.submit([this.scene.GetCommandBuffer(), this.GetCommandBuffer()]);
}

async Render() {
GPU.device.queue.submit([this.render.getCommandBuffer()]);
}


}
2 changes: 1 addition & 1 deletion src/scripts/light_monte_carlo_path_tracing/propagate.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct StagingBuffer {

const PI = 3.14159265359;

const SAMPLES = 10;
const SAMPLES = 2;
const MAXDEPTH = 700;

var<private> seed: u32 = 0u;
Expand Down
3 changes: 1 addition & 2 deletions src/scripts/raytrace/raytrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Raytrace extends GPUAbstractRunner {
}

override getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}

override async Destroy() {
Expand Down Expand Up @@ -143,6 +143,5 @@ export class Raytrace extends GPUAbstractRunner {

override async Run() {
GPU.device.queue.submit([this.getCommandBuffer()])
await GPU.device.queue.onSubmittedWorkDone()
}
}
2 changes: 1 addition & 1 deletion src/scripts/raytrace/smallpt.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct StagingBuffer {

// Play with the two following values to change quality.
// You want as many samples as your GPU can bear. :)
const SAMPLES = 60;
const SAMPLES = 10;
const MAXDEPTH = 4;

const PI = 3.14159265359;
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/sdf/sdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class SDF extends GPUAbstractRunner {
}

getType(): RunnerType {
return RunnerType.ANIM
return RunnerType.ASYNCANIM
}


Expand Down
19 changes: 19 additions & 0 deletions src/scripts/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {PrepareSidebar, ShowFeatures} from "./sidebar";

let lastframeTime = 0 as number
let nFrame = 0 as number
let lastIteration = 0 as number

export function MeasureFrame() {
if (lastframeTime == 0) {
Expand All @@ -19,6 +20,24 @@ export function MeasureFrame() {
}
}

export function MeasureIteration(currentIteration: number) {
if (lastframeTime == 0) {
lastframeTime = performance.now()
nFrame = 0
lastIteration = 0
}
nFrame++
if (nFrame >= 20) {
let currentFrameTime = performance.now()
let fps = (currentIteration - lastIteration) / (currentFrameTime - lastframeTime) * 1000
lastframeTime = currentFrameTime
lastIteration = currentIteration
nFrame = 0
document.getElementById("textFps").innerHTML = fps.toFixed(2) + " fps"
}
}


export function ShowError(message: string, e: Error) {
document.getElementById("screen").style.visibility = "hidden"

Expand Down

0 comments on commit 04b138d

Please sign in to comment.