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.
147 lines (144 loc) • 4.93 kB
JavaScript
import { PipelineEntry } from './PipelineEntry.mjs';
import { isRenderer } from '../renderers/utils.mjs';
import { throwError } from '../../utils/utils.mjs';
class ComputePipelineEntry 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 + " " + type : type);
super(parameters);
this.type = type;
this.shaders = {
compute: {
head: "",
code: "",
module: null,
constants: /* @__PURE__ */ new Map()
}
};
this.descriptor = null;
}
/* SHADERS */
/**
* 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};
`;
});
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 = `
${groupBinding.wgslStructFragment}
${this.shaders.compute.head}`;
}
if (this.shaders.compute.head.indexOf(groupBinding.wgslGroupFragment) === -1) {
this.shaders.compute.head = `${this.shaders.compute.head}
${groupBinding.wgslGroupFragment}`;
}
if (groupBinding.newLine) this.shaders.compute.head += `
`;
}
this.shaders.compute.code = this.shaders.compute.head + this.options.shaders.compute.code;
}
/* SETUP */
/**
* 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;
}
}
}
export { ComputePipelineEntry };