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
JavaScript
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}) ${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 };