xgpu
Version:
XGPU is an extendable library for WebGPU that provides a higher-level, easy-to-use interface for building rendering engines or processing numeric data. It handles automatic data binding, buffer alignment, variable declarations in shaders, and more. XGPU i
243 lines (242 loc) • 10.1 kB
JavaScript
// Copyright (c) 2023 Thomas Le Coz. All rights reserved.
// This code is governed by an MIT license that can be found in the LICENSE file.
import { XGPU } from "../XGPU";
import { Bindgroups } from "../shader/Bindgroups";
import { VertexAttribute } from "../shader/resources/VertexAttribute";
import { VertexBuffer } from "../shader/resources/VertexBuffer";
import { ShaderStruct } from "../shader/shaderParts/ShaderStruct";
import { PrimitiveFloatUniform, PrimitiveIntUniform, PrimitiveUintUniform } from "../PrimitiveType";
import { EventDispatcher } from "../EventDispatcher";
export class Pipeline extends EventDispatcher {
description = {};
nbVertex;
bindGroups;
vertexBuffers;
vertexShader;
fragmentShader;
vertexBufferLayouts;
gpuBindgroups = [];
gpuBindGroupLayouts = [];
gpuPipelineLayout;
type = null;
constructor() {
super();
this.bindGroups = new Bindgroups(this, "pipeline");
}
get isComputePipeline() { return this.type === "compute" || this.type === "compute_mixed"; }
get isRenderPipeline() { return this.type === "render"; }
get isMixedPipeline() { return this.type === "compute_mixed"; }
_resources;
get resources() { return this._resources; }
debug;
clearAfterDeviceLostAndRebuild() {
this.bindGroups.clearAfterDeviceLost();
this.vertexBufferLayouts = undefined;
this.gpuPipelineLayout = undefined;
this.gpuBindGroupLayouts = [];
this.gpuBindgroups = [];
//this.build();
}
initFromObject(obj) {
this._resources = obj;
}
pipelineCount = 1;
static getResourceDefinition(resources) {
const result = {};
let o;
for (let z in resources) {
o = resources[z];
result[o.name] = o;
}
return result;
}
addBindgroup(group) {
this.bindGroups.add(group);
}
createVertexBufferLayout() {
this.vertexBufferLayouts = [];
this.vertexBuffers = [];
const groups = this.bindGroups.groups;
let elements;
let resource;
let builtin = 0;
let k = 0;
for (let i = 0; i < groups.length; i++) {
elements = groups[i].elements;
for (let j = 0; j < elements.length; j++) {
resource = elements[j].resource;
if (resource instanceof VertexBuffer) {
this.vertexBuffers[k] = resource;
this.vertexBufferLayouts[k++] = resource.createVertexBufferLayout(builtin);
builtin += resource.vertexArrays.length;
}
}
}
return this.vertexBufferLayouts;
}
createShaderInput(shader, buffers) {
const vertexInput = new ShaderStruct("Input", shader.inputs);
;
if (buffers) {
let arrays;
let builtin = 0;
for (let i = 0; i < buffers.length; i++) {
arrays = buffers[i].vertexArrays;
for (let j = 0; j < arrays.length; j++) {
vertexInput.addProperty({ name: arrays[j].name, type: arrays[j].varType, builtin: "@location(" + builtin + ")" });
builtin++;
}
}
}
return vertexInput;
}
createLayouts() {
this.gpuBindGroupLayouts = [];
this.gpuBindgroups = [];
this.gpuPipelineLayout = null;
const groups = this.bindGroups.groups;
let elements;
let resource;
let layout, group;
let layouts = [];
let k, n = 0;
for (let i = 0; i < groups.length; i++) {
elements = groups[i].elements;
layout = layouts[i] = { entries: [] };
group = { entries: [], layout: null };
k = 0;
for (let j = 0; j < elements.length; j++) {
resource = elements[j].resource;
if (!(resource instanceof VertexBuffer) || this.isComputePipeline) {
layout.entries[k] = resource.createBindGroupLayoutEntry(k);
group.entries[k] = resource.createBindGroupEntry(k);
k++;
}
}
if (k > 0) {
group.layout = this.gpuBindGroupLayouts[n] = XGPU.createBindgroupLayout(layout);
this.gpuBindgroups[n] = XGPU.createBindgroup(group);
n++;
}
}
this.gpuPipelineLayout = XGPU.createPipelineLayout({ bindGroupLayouts: this.gpuBindGroupLayouts });
}
initPipelineResources(pipeline) {
const resources = this.bindGroups.resources.all;
//console.log("all = ", resources)
if (!resources)
return;
for (let i = 0; i < resources.length; i++)
resources[i].setPipelineType(pipeline.type);
}
build() {
this.createVertexBufferLayout();
this.createLayouts();
}
update(o) {
if (o) {
}
//must be overided
}
getResourceName(resource) {
if (resource instanceof VertexAttribute) {
if (this.type !== "render") {
const buffer = resource.vertexBuffer;
const bufferName = this.bindGroups.getNameByResource(buffer);
return bufferName + "." + resource.name;
}
else {
return resource.name;
}
}
else {
if (resource.uniformBuffer) {
const bufferName = this.bindGroups.getNameByResource(resource.uniformBuffer);
return bufferName + "." + resource.name;
}
else {
return this.bindGroups.getNameByResource(resource);
}
}
}
createPipelineInstanceArray(resources, nbInstance) {
this.pipelineCount = nbInstance;
const result = [];
let instance;
let resource;
let clonedUniformBuffers;
const resourceNames = [];
const resourceBindgroup = [];
const resourceUniformBufferName = [];
for (let i = 0; i < resources.length; i++) {
resource = resources[i];
const name = this.bindGroups.getNameByResource(resource);
const bindgroup = this.bindGroups.getGroupByPropertyName(name);
bindgroup.mustRefreshBindgroup = true;
resourceNames[i] = name;
resourceBindgroup[i] = bindgroup;
if (resource instanceof PrimitiveFloatUniform || resource instanceof PrimitiveIntUniform || resource instanceof PrimitiveUintUniform) {
resourceUniformBufferName[i] = bindgroup.getResourceName(resource.uniformBuffer);
}
}
for (let k = 0; k < nbInstance; k++) {
result[k] = instance = {};
clonedUniformBuffers = {};
for (let i = 0; i < resources.length; i++) {
resource = resources[i];
resource.update();
const name = resourceNames[i];
const bindgroup = resourceBindgroup[i];
//console.log("resource = ", resource)
if (resource instanceof PrimitiveFloatUniform || resource instanceof PrimitiveIntUniform || resource instanceof PrimitiveUintUniform) {
const uniformBufferName = resourceUniformBufferName[i]; //bindgroup.getResourceName(resource.uniformBuffer);
//console.log("uniformBufferName = ", uniformBufferName, name)
if (!clonedUniformBuffers[uniformBufferName]) {
clonedUniformBuffers[uniformBufferName] = resource.uniformBuffer.clone();
clonedUniformBuffers[uniformBufferName].name = uniformBufferName;
//console.log("cloned uniformBuffer = ", clonedUniformBuffers[uniformBufferName])
}
//console.log("===>>> uniformBufferName = ", bindgroup.getResourceName(resource.uniformBuffer))
instance[uniformBufferName] = clonedUniformBuffers[uniformBufferName];
instance[uniformBufferName].name = clonedUniformBuffers[uniformBufferName].name;
instance[uniformBufferName].bindgroup = bindgroup;
instance[name] = clonedUniformBuffers[uniformBufferName].getUniformByName(name);
}
else {
instance[name] = resource.clone();
instance[name].bindgroup = bindgroup;
instance[name].name = name;
}
}
const shaderResources = [];
for (let z in instance) {
resource = instance[z];
if (!(resource instanceof PrimitiveFloatUniform || resource instanceof PrimitiveIntUniform || resource instanceof PrimitiveUintUniform)) {
resource.setPipelineType(this.type);
resource.createGpuResource();
shaderResources.push(resource);
}
}
instance.deviceId = XGPU.deviceId;
instance.apply = () => {
let rebuild = false;
if (XGPU.deviceId != instance.deviceId) {
rebuild = true;
instance.deviceId = XGPU.deviceId;
}
let o;
for (let i = 0; i < shaderResources.length; i++) {
o = shaderResources[i];
if (rebuild) {
o.destroyGpuResource();
o.createGpuResource();
}
o.update();
o.bindgroup.set(o.name, o);
}
this.update();
};
}
return result;
}
}