From 1dca365e85599bd3aed5ca3013a79039cd7257b6 Mon Sep 17 00:00:00 2001 From: Sebastian Macke Date: Sat, 23 Sep 2023 23:42:29 +0200 Subject: [PATCH] Remove Render from classes and separate into own class --- src/scripts/AbstractGPURunner.ts | 6 ++ src/scripts/GPURenderRunner.ts | 45 +++++++++++ src/scripts/{GPURunner.ts => RunGPURunner.ts} | 2 +- src/scripts/diffuse/diffuse.ts | 14 ++-- src/scripts/fluid/render/render.frag.wgsl | 14 ---- src/scripts/light/light.ts | 31 ------- src/scripts/raytrace/raytrace.ts | 51 +++++------- src/scripts/render/render.ts | 28 +++---- src/scripts/sdf/sdf.ts | 16 ++-- src/scripts/ui.ts | 80 ++++++++++--------- 10 files changed, 147 insertions(+), 140 deletions(-) create mode 100644 src/scripts/GPURenderRunner.ts rename src/scripts/{GPURunner.ts => RunGPURunner.ts} (99%) diff --git a/src/scripts/AbstractGPURunner.ts b/src/scripts/AbstractGPURunner.ts index af6033f..d97f166 100644 --- a/src/scripts/AbstractGPURunner.ts +++ b/src/scripts/AbstractGPURunner.ts @@ -1,3 +1,4 @@ +import {Texture} from "./webgpu/texture"; export enum RunnerType { HTML = 1, @@ -8,6 +9,7 @@ export enum RunnerType { export interface GPURunner { getType(): RunnerType; getHTML(): string; + getRenderInfo(): {textures: Texture[], fragmentShaderFilenames: string[]}; getCommandBuffer(): GPUCommandBuffer; Run(): Promise; Init(): Promise; @@ -27,4 +29,8 @@ export abstract class GPUAbstractRunner implements GPURunner { getHTML(): string { throw new Error("Method not implemented."); } + + getRenderInfo(): { textures: Texture[]; fragmentShaderFilenames: string[] } { + throw new Error("Method not implemented."); + } } diff --git a/src/scripts/GPURenderRunner.ts b/src/scripts/GPURenderRunner.ts new file mode 100644 index 0000000..cd3f1eb --- /dev/null +++ b/src/scripts/GPURenderRunner.ts @@ -0,0 +1,45 @@ +import {GPURunner, RunnerType} from "./AbstractGPURunner"; +import {Render} from "./render/render"; +import {Texture} from "./webgpu/texture"; +import {GPU} from "./webgpu/gpu"; + +export class GPURenderRunner implements GPURunner { + runner: GPURunner + render: Render + + constructor(runner: GPURunner) { + this.runner = runner + } + + getHTML(): string { + return this.runner.getHTML() + } + + getType(): RunnerType { + return this.runner.getType() + } + + async Destroy() { + await this.render.Destroy() + await this.runner.Destroy() + } + + async Init() { + await this.runner.Init() + let renderInfo = this.runner.getRenderInfo() + this.render = new Render(renderInfo.textures, ...renderInfo.fragmentShaderFilenames) + await this.render.Init() + } + + async Run() { + GPU.device.queue.submit([this.runner.getCommandBuffer(), this.render.getCommandBuffer()]) + } + + getCommandBuffer(): GPUCommandBuffer { + throw new Error("Method not implemented."); + } + + getRenderInfo(): { textures: Texture[]; fragmentShaderFilenames: string[] } { + throw new Error("Method not implemented."); + } +} \ No newline at end of file diff --git a/src/scripts/GPURunner.ts b/src/scripts/RunGPURunner.ts similarity index 99% rename from src/scripts/GPURunner.ts rename to src/scripts/RunGPURunner.ts index 502244e..64c407d 100644 --- a/src/scripts/GPURunner.ts +++ b/src/scripts/RunGPURunner.ts @@ -5,7 +5,7 @@ import {Mutex} from "async-mutex"; let stop_immediately = true; -function ListenToError() { +export function ListenToError() { GPU.device.addEventListener("uncapturederror", (event) => { let e = event as GPUUncapturedErrorEvent console.error("A WebGPU error was not captured", e.error); diff --git a/src/scripts/diffuse/diffuse.ts b/src/scripts/diffuse/diffuse.ts index 438ca0d..0c0b6ff 100644 --- a/src/scripts/diffuse/diffuse.ts +++ b/src/scripts/diffuse/diffuse.ts @@ -10,7 +10,6 @@ export class Diffuse extends GPUAbstractRunner { texturesrc: Texture; texturedest: Texture; - render: Render; bind_group_layout: GPUBindGroupLayout bind_group_layout_mesh: GPUBindGroupLayout @@ -52,7 +51,6 @@ export class Diffuse extends GPUAbstractRunner { } override async Destroy() { - await this.render.Destroy() this.texturesrc.destroy() this.texturedest.destroy() this.vertexBuffer.destroy() @@ -157,8 +155,6 @@ export class Diffuse extends GPUAbstractRunner { this.texturesrc = GPU.CreateStorageTexture(this.width, this.height, "rgba32float") this.texturedest = GPU.CreateStorageTexture(this.width, this.height, "rgba32float") - this.render = new Render([this.texturedest], "scripts/diffuse/aces-tone-mapping.wgsl") - await this.render.Init() this.stagingBuffer = GPU.CreateUniformBuffer(4 * 4) // must be a multiple of 16 bytes this.stagingData = new Float32Array(4) @@ -278,7 +274,15 @@ export class Diffuse extends GPUAbstractRunner { } override async Run() { - GPU.device.queue.submit([this.getCommandBuffer(), this.render.getCommandBuffer()]) + GPU.device.queue.submit([this.getCommandBuffer()]) await GPU.device.queue.onSubmittedWorkDone() } + + override getRenderInfo(): { textures: Texture[]; fragmentShaderFilenames: string[] } { + return { + textures: [this.texturedest], + fragmentShaderFilenames: ["scripts/diffuse/aces-tone-mapping.wgsl"] + } + } + } diff --git a/src/scripts/fluid/render/render.frag.wgsl b/src/scripts/fluid/render/render.frag.wgsl index e0a705d..61e20b7 100755 --- a/src/scripts/fluid/render/render.frag.wgsl +++ b/src/scripts/fluid/render/render.frag.wgsl @@ -5,24 +5,10 @@ struct VertexOutput { @builtin(position) Position : vec4, @location(0) fragUV : vec2 }; -/* -[[stage(fragment)]] -fn main(data: VertexOutput) -> [[location(0)]] vec4 { - return textureSample(myTexture, mySampler, data.fragUV); -} -*/ @fragment fn main(data: VertexOutput) -> @location(0) vec4 { var d: vec2 = vec2(textureDimensions(myTexture, 0)); let c = textureLoad(myTexture, vec2(data.fragUV*d), 0).rgb; return vec4(abs(c), 1.); - //return vec4(c.r, -c.r, 0., 1.); -} - -/* -[[stage(fragment)]] -fn main([[location(0)]] fragUV: vec2) -> [[location(0)]] vec4 { - return vec4(fragUV.x, 0.0, 0.0, 1.0); } -*/ \ No newline at end of file diff --git a/src/scripts/light/light.ts b/src/scripts/light/light.ts index 593dd2b..f4553b9 100644 --- a/src/scripts/light/light.ts +++ b/src/scripts/light/light.ts @@ -12,17 +12,12 @@ import {Texture} from "../webgpu/texture"; import {Buffer} from "../webgpu/buffer"; import {GPUAbstractRunner, RunnerType} from "../AbstractGPURunner"; import {Render} from "../render/render"; -import {Raytrace} from "../raytrace/raytrace"; -import {SDF} from "../sdf/sdf"; export class LightPropagation extends GPUAbstractRunner { width: number height: number render: Render - sdf: SDF - raytrace: Raytrace - textureSignedDistance: Texture textureDest: Texture textureSrc: Texture @@ -48,35 +43,9 @@ export class LightPropagation extends GPUAbstractRunner { async Destroy() { this.textureDest.destroy() this.textureSrc.destroy() - /* - await this.sdf.Destroy() - await this.raytrace.Destroy() - */ } async Init() { -/* - this.raytrace = new Raytrace("fbm.wgsl", false) - try { - await this.raytrace.Init() - await this.raytrace.Run() - } catch (e) { - ShowError("Creation of FBM failed", e as Error) - throw e - } - this.sdf = new SDF(this.raytrace.texturedest) - try { - await this.sdf.Init() - for(let i = 0; i < 256; i++) { - await this.sdf.Run() - } - } catch (e) { - ShowError("Creation of SDF failed", e as Error) - throw e - } - //this.textureSignedDistance = await GPU.createTextureFromTexture(this.sdf.texturea, "rgba16float") - this.textureSignedDistance = this.sdf.texturea -*/ console.log("Create Texture") this.textureDest = GPU.CreateStorageTextureArray(this.width, this.height, 3, "rgba16float") this.textureSrc = GPU.CreateStorageTextureArray(this.width, this.height, 3, "rgba16float") diff --git a/src/scripts/raytrace/raytrace.ts b/src/scripts/raytrace/raytrace.ts index 0e9e691..c457943 100755 --- a/src/scripts/raytrace/raytrace.ts +++ b/src/scripts/raytrace/raytrace.ts @@ -5,12 +5,12 @@ import {GPUAbstractRunner, RunnerType} from "../AbstractGPURunner"; import {Render} from "../render/render"; export class Raytrace extends GPUAbstractRunner { - width: number; - height: number; + width: number + height: number - texturesrc: Texture; - texturedest: Texture; - render: Render; + texturesrc: Texture + texturedest: Texture + render: Render bind_group_layout: GPUBindGroupLayout bind_group: GPUBindGroup @@ -20,16 +20,13 @@ export class Raytrace extends GPUAbstractRunner { stagingBuffer: Buffer stagingData: Float32Array - filename: string; + filename: string + fragmentShaderFilename: string - showOnScreen: boolean; - fragmentShaderFilename: string; + startTime: number - startTime: number; - - constructor(filename: string, showOnScreen: boolean, fragmentShaderFilename: string = null) { + constructor(filename: string, fragmentShaderFilename: string = null) { super(); - this.showOnScreen = showOnScreen this.filename = filename this.width = GPU.viewport.width this.height = GPU.viewport.height @@ -41,9 +38,6 @@ export class Raytrace extends GPUAbstractRunner { } override async Destroy() { - if (this.showOnScreen) { - await this.render.Destroy() - } this.texturesrc.destroy() this.texturedest.destroy() } @@ -53,15 +47,6 @@ export class Raytrace extends GPUAbstractRunner { this.texturesrc = GPU.CreateStorageTexture(this.width, this.height, "rgba32float") this.texturedest = GPU.CreateStorageTexture(this.width, this.height, "rgba32float") - if (this.showOnScreen) { - if (this.fragmentShaderFilename === null) { - this.render = new Render([this.texturedest]) - } else { - this.render = new Render([this.texturedest], "scripts/raytrace/" + this.fragmentShaderFilename) - } - await this.render.Init() - } - this.stagingBuffer = GPU.CreateUniformBuffer(4*3 + 4) // must be a multiple of 16 bytes this.stagingData = new Float32Array(4) @@ -112,7 +97,19 @@ export class Raytrace extends GPUAbstractRunner { }) this.startTime = new Date().getTime(); + } + override getRenderInfo(): { textures: Texture[]; fragmentShaderFilenames: string[] } { + if (this.fragmentShaderFilename == null) { + return { + textures: [this.texturedest], + fragmentShaderFilenames: [] + } + } + return { + textures: [this.texturedest], + fragmentShaderFilenames: ["scripts/raytrace/" + this.fragmentShaderFilename] + } } previousMouseCoordinatex: number = 0 @@ -145,11 +142,7 @@ export class Raytrace extends GPUAbstractRunner { } override async Run() { - if (this.showOnScreen) { - GPU.device.queue.submit([this.getCommandBuffer(), this.render.getCommandBuffer()]) - } else { - GPU.device.queue.submit([this.getCommandBuffer()]) - } + GPU.device.queue.submit([this.getCommandBuffer()]) await GPU.device.queue.onSubmittedWorkDone() } } diff --git a/src/scripts/render/render.ts b/src/scripts/render/render.ts index be03c68..7f76ede 100755 --- a/src/scripts/render/render.ts +++ b/src/scripts/render/render.ts @@ -3,13 +3,12 @@ import {Texture} from "../webgpu/texture"; import {GPUAbstractRunner, RunnerType} from "../AbstractGPURunner"; export class Render extends GPUAbstractRunner { - bind_group_layout: GPUBindGroupLayout; - bind_group: GPUBindGroup; - pipeline_layout: GPUPipelineLayout; - pipeline: GPURenderPipeline; - textures: Texture[]; - - fragmentShaderFilenames: string[]; + bind_group_layout: GPUBindGroupLayout + bind_group: GPUBindGroup + pipeline_layout: GPUPipelineLayout + pipeline: GPURenderPipeline + textures: Texture[] + fragmentShaderFilenames: string[] constructor(textures: Texture[], ...fragmentShaderFilenames: string[]) { super(); @@ -21,7 +20,6 @@ export class Render extends GPUAbstractRunner { return RunnerType.GRAPHIC } - override async Destroy() { } @@ -101,16 +99,16 @@ export class Render extends GPUAbstractRunner { topology: "triangle-strip", stripIndexFormat: "uint32" } - }); + }) } override getCommandBuffer(): GPUCommandBuffer { - const commandEncoder = GPU.device.createCommandEncoder({}); - const passEncoder = commandEncoder.beginRenderPass(GPU.getRenderPassDescriptor()); - passEncoder.setPipeline(this.pipeline); - passEncoder.setBindGroup(0, this.bind_group); - passEncoder.draw(4, 1, 0, 0); - passEncoder.end(); + const commandEncoder = GPU.device.createCommandEncoder({}) + const passEncoder = commandEncoder.beginRenderPass(GPU.getRenderPassDescriptor()) + passEncoder.setPipeline(this.pipeline) + passEncoder.setBindGroup(0, this.bind_group) + passEncoder.draw(4, 1, 0, 0) + passEncoder.end() return commandEncoder.finish() } diff --git a/src/scripts/sdf/sdf.ts b/src/scripts/sdf/sdf.ts index 241247e..e6e2402 100755 --- a/src/scripts/sdf/sdf.ts +++ b/src/scripts/sdf/sdf.ts @@ -1,7 +1,6 @@ import {GPU} from "../webgpu/gpu"; import {Texture} from "../webgpu/texture"; import {GPUAbstractRunner, RunnerType} from "../AbstractGPURunner"; -import {Render} from "../render/render"; export class SDF extends GPUAbstractRunner { width: number; @@ -17,8 +16,6 @@ export class SDF extends GPUAbstractRunner { pipeline_layout: GPUPipelineLayout; compute_pipeline: GPUComputePipeline; - render: Render; - constructor(texture_border: Texture) { super(); this.texture_border = texture_border; @@ -32,7 +29,6 @@ export class SDF extends GPUAbstractRunner { async Destroy() { - await this.render.Destroy() this.texturea.destroy() this.textureb.destroy() } @@ -44,9 +40,6 @@ export class SDF extends GPUAbstractRunner { this.textureb = GPU.CreateStorageTexture(this.width, this.height, "rg32float") this.render_output = GPU.CreateStorageTexture(this.width, this.height, "rgba32float") - this.render = new Render([this.render_output]) - await this.render.Init() - this.bind_group_layout = GPU.device.createBindGroupLayout({ entries: [{ binding: 0, @@ -114,8 +107,13 @@ export class SDF extends GPUAbstractRunner { } async Run() { - GPU.device.queue.submit([this.getCommandBuffer(), this.render.getCommandBuffer()]); + GPU.device.queue.submit([this.getCommandBuffer()]); await GPU.device.queue.onSubmittedWorkDone(); } - + override getRenderInfo(): { textures: Texture[]; fragmentShaderFilenames: string[] } { + return { + textures: [this.render_output], + fragmentShaderFilenames: [] + } + } } \ No newline at end of file diff --git a/src/scripts/ui.ts b/src/scripts/ui.ts index 8d0e449..940bcd4 100755 --- a/src/scripts/ui.ts +++ b/src/scripts/ui.ts @@ -7,29 +7,31 @@ import {Fluid} from "./fluid/fluid"; import {Texture} from "./webgpu/texture"; import {LightPropagation} from "./light/light"; import {Features} from "./features/features"; -import {HandleRunner} from "./GPURunner"; +import {HandleRunner} from "./RunGPURunner"; import {Diffuse} from "./diffuse/diffuse"; +import {GPURunner} from "./AbstractGPURunner"; +import {GPURenderRunner} from "./GPURenderRunner"; -let lastframeTime = 0 as number; -let nFrame = 0 as number; +let lastframeTime = 0 as number +let nFrame = 0 as number export function MeasureFrame() { if (lastframeTime == 0) { - lastframeTime = performance.now(); - nFrame = 0; + lastframeTime = performance.now() + nFrame = 0 } nFrame++ if (nFrame >= 20) { - let currentFrameTime = performance.now(); - let fps = 20 / (currentFrameTime - lastframeTime) * 1000; - lastframeTime = currentFrameTime; - nFrame = 0; - document.getElementById("textFps").innerHTML = fps.toFixed(2) + " fps"; + let currentFrameTime = performance.now() + let fps = 20 / (currentFrameTime - lastframeTime) * 1000 + lastframeTime = currentFrameTime + nFrame = 0 + document.getElementById("textFps").innerHTML = fps.toFixed(2) + " fps" } } export function ShowError(message: string, e: Error) { - let errorObject = document.createElement("pre"); + let errorObject = document.createElement("pre") errorObject.style.color = "#dc3545" @@ -37,17 +39,17 @@ export function ShowError(message: string, e: Error) { errorObject.innerHTML += "\n\n" errorObject.innerHTML += e.message - let infoElement = document.getElementById("info"); - infoElement.innerHTML = ""; - infoElement.appendChild(errorObject); - document.getElementById("screen").style.visibility = "hidden"; + let infoElement = document.getElementById("info") + infoElement.innerHTML = "" + infoElement.appendChild(errorObject) + document.getElementById("screen").style.visibility = "hidden" } async function Init(powerPreference: GPUPowerPreference) { - let infoElement = document.getElementById("info"); + let infoElement = document.getElementById("info") infoElement.innerHTML = "Initializing WebGPU..." try { - await GPU.Init(powerPreference); + await GPU.Init(powerPreference) GPU.SetCanvas("screen") } catch (e) { ShowError("WebGPU initialization failed", e as Error) @@ -57,49 +59,55 @@ async function Init(powerPreference: GPUPowerPreference) { async function ShowFeatures() { - await HandleRunner(new Features()); + await HandleRunner(new Features()) } async function ShowCollatz() { - await HandleRunner(new Collatz()); + await HandleRunner(new Collatz()) } async function ShowTexture() { - let texture: Texture; - texture = await GPU.createTextureFromImage("scripts/render/Lenna.png"); - await HandleRunner(new Render([texture])); - texture.destroy(); + let texture: Texture + texture = await GPU.createTextureFromImage("scripts/render/Lenna.png") + await HandleRunner(new Render([texture])) + texture.destroy() } async function ShowRaytrace(filename: string, fragmentShaderFilename: string = null) { - await HandleRunner(new Raytrace(filename, true, fragmentShaderFilename)); + let raytrace = new Raytrace(filename, fragmentShaderFilename) + await HandleRunner(new GPURenderRunner(raytrace)) } - async function ShowDiffuse() { - await HandleRunner(new Diffuse()); + let diffuse = new Diffuse() + await HandleRunner(new GPURenderRunner(diffuse)) } - async function ShowFluid() { - await HandleRunner(new Fluid()); + await HandleRunner(new Fluid()) } async function ShowLightPropagation() { - await HandleRunner(new LightPropagation()); + await HandleRunner(new LightPropagation()) } -async function ShowSDF() { - let raytrace = new Raytrace("fbm.wgsl", false); +async function RunOnce(runner: GPURunner) { try { - await raytrace.Init(); - await raytrace.Run(); + await runner.Init() + await runner.Run() } catch (e) { ShowError("GPU object creation failed", e as Error) throw e } - await HandleRunner(new SDF(raytrace.texturedest)); - await raytrace.Destroy(); +} + +async function ShowSDF() { + let raytrace = new Raytrace("fbm.wgsl") + await RunOnce(raytrace) + + let sdf = new SDF(raytrace.texturedest) + await HandleRunner(new GPURenderRunner(sdf)) + await raytrace.Destroy() } document.getElementById("button_features").addEventListener("click", ShowFeatures) @@ -147,7 +155,7 @@ window.addEventListener("DOMContentLoaded", async () => { await Init(preference); console.log("Init finished"); - ShowFeatures(); + await ShowFeatures(); });