UNPKG

gpu-curtains

Version:

gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.

139 lines (138 loc) 5.3 kB
import { throwError } from "../../utils/utils.mjs"; import { isRenderer } from "../renderers/utils.mjs"; import { PipelineEntry } from "./PipelineEntry.mjs"; //#region src/core/pipelines/ComputePipelineEntry.ts /** * Used to create a {@link PipelineEntry} specifically designed to handle {@link core/materials/ComputeMaterial.ComputeMaterial | ComputeMaterial}. * * ## Shaders patching * * The {@link ComputePipelineEntry} uses each of its {@link ComputePipelineEntry#bindGroups | bind groups} {@link core/bindings/Binding.Binding | Binding} to patch the given compute shader before creating the {@link GPUShaderModule}.<br> * It will prepend every {@link core/bindings/Binding.Binding | Binding} WGSL code snippets (or fragments) with the correct bind group and bindings indices. * * ## Pipeline compilation * * The {@link ComputePipelineEntry} will then create a {@link GPUComputePipeline} (asynchronously by default). */ var ComputePipelineEntry = class extends PipelineEntry { /** * ComputePipelineEntry constructor * @param parameters - {@link PipelineEntryParams | parameters} used to create this {@link ComputePipelineEntry} */ constructor(parameters) { const { label, renderer, bindGroups } = parameters; const type = "ComputePipelineEntry"; isRenderer(renderer, label ? label + " ComputePipelineEntry" : type); super(parameters); this.type = type; this.shaders = { compute: { head: "", code: "", module: null, constants: /* @__PURE__ */ new Map() } }; this.descriptor = null; } /** * Patch the shaders by appending all the {@link bindGroups | bind groups}) WGSL code fragments to the given {@link PipelineEntryParams#shaders | parameter shader code} */ patchShaders() { this.shaders.compute.head = ""; this.shaders.compute.code = ""; if (this.options.shaders.compute.constants) for (const [key, value] of Object.entries(this.options.shaders.compute.constants)) this.shaders.compute.constants.set(key, value); this.shaders.compute.constants.forEach((value, key) => { const type = typeof value === "boolean" ? "bool" : "f32"; this.shaders.compute.head += `override ${key}: ${type} = ${value};\n`; }); const groupsBindings = []; for (const bindGroup of this.bindGroups) { let bindIndex = 0; bindGroup.bindings.forEach((binding, bindingIndex) => { binding.wgslGroupFragment.forEach((groupFragment, groupFragmentIndex) => { groupsBindings.push({ groupIndex: bindGroup.index, bindIndex, wgslStructFragment: binding.wgslStructFragment, wgslGroupFragment: groupFragment, newLine: bindingIndex === bindGroup.bindings.length - 1 && groupFragmentIndex === binding.wgslGroupFragment.length - 1 }); bindIndex++; }); }); } for (const groupBinding of groupsBindings) { if (groupBinding.wgslStructFragment && this.shaders.compute.head.indexOf(groupBinding.wgslStructFragment) === -1) this.shaders.compute.head = `\n${groupBinding.wgslStructFragment}\n${this.shaders.compute.head}`; if (this.shaders.compute.head.indexOf(groupBinding.wgslGroupFragment) === -1) this.shaders.compute.head = `${this.shaders.compute.head}\n@group(${groupBinding.groupIndex}) @binding(${groupBinding.bindIndex}) ${groupBinding.wgslGroupFragment}`; if (groupBinding.newLine) this.shaders.compute.head += `\n`; } this.shaders.compute.code = this.shaders.compute.head + this.options.shaders.compute.code; } /** * Create the {@link shaders}: patch them and create the {@link GPUShaderModule} */ createShaders() { this.patchShaders(); this.shaders.compute.module = this.createShaderModule({ code: this.shaders.compute.code, type: "compute" }); } /** * Create the compute pipeline {@link descriptor} */ createPipelineDescriptor() { if (!this.shaders.compute.module) return; this.descriptor = { label: this.options.label, layout: this.layout, compute: { module: this.shaders.compute.module, entryPoint: this.options.shaders.compute.entryPoint, ...this.options.shaders.compute.constants && { constants: this.options.shaders.compute.constants } } }; } /** * Create the compute {@link pipeline} */ createComputePipeline() { if (!this.shaders.compute.module) return; try { this.pipeline = this.renderer.createComputePipeline(this.descriptor); } catch (error) { this.status.error = error; throwError(error); } } /** * Asynchronously create the compute {@link pipeline} * @returns - void promise result */ async createComputePipelineAsync() { if (!this.shaders.compute.module) return; try { this.pipeline = await this.renderer.createComputePipelineAsync(this.descriptor); this.status.compiled = true; this.status.compiling = false; this.status.error = null; } catch (error) { this.status.error = error; throwError(error); } } /** * Call {@link PipelineEntry#compilePipelineEntry | PipelineEntry compilePipelineEntry} method, then create our compute {@link pipeline} */ async compilePipelineEntry() { super.compilePipelineEntry(); if (this.options.useAsync) await this.createComputePipelineAsync(); else { this.createComputePipeline(); this.status.compiled = true; this.status.compiling = false; this.status.error = null; } } }; //#endregion export { ComputePipelineEntry };