Files
nothoughts/node_modules/pixi.js/lib/rendering/renderers/shared/shader/Shader.mjs
2025-08-04 18:57:35 +02:00

183 lines
5.7 KiB
JavaScript

import EventEmitter from 'eventemitter3';
import { GlProgram } from '../../gl/shader/GlProgram.mjs';
import { BindGroup } from '../../gpu/shader/BindGroup.mjs';
import { GpuProgram } from '../../gpu/shader/GpuProgram.mjs';
import { RendererType } from '../../types.mjs';
import { UniformGroup } from './UniformGroup.mjs';
"use strict";
class Shader extends EventEmitter {
constructor(options) {
super();
/**
* A record of the uniform groups and resources used by the shader.
* This is used by WebGL renderer to sync uniform data.
* @internal
* @ignore
*/
this._uniformBindMap = /* @__PURE__ */ Object.create(null);
this._ownedBindGroups = [];
let {
gpuProgram,
glProgram,
groups,
resources,
compatibleRenderers,
groupMap
} = options;
this.gpuProgram = gpuProgram;
this.glProgram = glProgram;
if (compatibleRenderers === void 0) {
compatibleRenderers = 0;
if (gpuProgram)
compatibleRenderers |= RendererType.WEBGPU;
if (glProgram)
compatibleRenderers |= RendererType.WEBGL;
}
this.compatibleRenderers = compatibleRenderers;
const nameHash = {};
if (!resources && !groups) {
resources = {};
}
if (resources && groups) {
throw new Error("[Shader] Cannot have both resources and groups");
} else if (!gpuProgram && groups && !groupMap) {
throw new Error("[Shader] No group map or WebGPU shader provided - consider using resources instead.");
} else if (!gpuProgram && groups && groupMap) {
for (const i in groupMap) {
for (const j in groupMap[i]) {
const uniformName = groupMap[i][j];
nameHash[uniformName] = {
group: i,
binding: j,
name: uniformName
};
}
}
} else if (gpuProgram && groups && !groupMap) {
const groupData = gpuProgram.structsAndGroups.groups;
groupMap = {};
groupData.forEach((data) => {
groupMap[data.group] = groupMap[data.group] || {};
groupMap[data.group][data.binding] = data.name;
nameHash[data.name] = data;
});
} else if (resources) {
groups = {};
groupMap = {};
if (gpuProgram) {
const groupData = gpuProgram.structsAndGroups.groups;
groupData.forEach((data) => {
groupMap[data.group] = groupMap[data.group] || {};
groupMap[data.group][data.binding] = data.name;
nameHash[data.name] = data;
});
}
let bindTick = 0;
for (const i in resources) {
if (nameHash[i])
continue;
if (!groups[99]) {
groups[99] = new BindGroup();
this._ownedBindGroups.push(groups[99]);
}
nameHash[i] = { group: 99, binding: bindTick, name: i };
groupMap[99] = groupMap[99] || {};
groupMap[99][bindTick] = i;
bindTick++;
}
for (const i in resources) {
const name = i;
let value = resources[i];
if (!value.source && !value._resourceType) {
value = new UniformGroup(value);
}
const data = nameHash[name];
if (data) {
if (!groups[data.group]) {
groups[data.group] = new BindGroup();
this._ownedBindGroups.push(groups[data.group]);
}
groups[data.group].setResource(value, data.binding);
}
}
}
this.groups = groups;
this._uniformBindMap = groupMap;
this.resources = this._buildResourceAccessor(groups, nameHash);
}
/**
* Sometimes a resource group will be provided later (for example global uniforms)
* In such cases, this method can be used to let the shader know about the group.
* @param name - the name of the resource group
* @param groupIndex - the index of the group (should match the webGPU shader group location)
* @param bindIndex - the index of the bind point (should match the webGPU shader bind point)
*/
addResource(name, groupIndex, bindIndex) {
var _a, _b;
(_a = this._uniformBindMap)[groupIndex] || (_a[groupIndex] = {});
(_b = this._uniformBindMap[groupIndex])[bindIndex] || (_b[bindIndex] = name);
if (!this.groups[groupIndex]) {
this.groups[groupIndex] = new BindGroup();
this._ownedBindGroups.push(this.groups[groupIndex]);
}
}
_buildResourceAccessor(groups, nameHash) {
const uniformsOut = {};
for (const i in nameHash) {
const data = nameHash[i];
Object.defineProperty(uniformsOut, data.name, {
get() {
return groups[data.group].getResource(data.binding);
},
set(value) {
groups[data.group].setResource(value, data.binding);
}
});
}
return uniformsOut;
}
/**
* Use to destroy the shader when its not longer needed.
* It will destroy the resources and remove listeners.
* @param destroyPrograms - if the programs should be destroyed as well.
* Make sure its not being used by other shaders!
*/
destroy(destroyPrograms = false) {
this.emit("destroy", this);
if (destroyPrograms) {
this.gpuProgram?.destroy();
this.glProgram?.destroy();
}
this.gpuProgram = null;
this.glProgram = null;
this.removeAllListeners();
this._uniformBindMap = null;
this._ownedBindGroups.forEach((bindGroup) => {
bindGroup.destroy();
});
this._ownedBindGroups = null;
this.resources = null;
this.groups = null;
}
static from(options) {
const { gpu, gl, ...rest } = options;
let gpuProgram;
let glProgram;
if (gpu) {
gpuProgram = GpuProgram.from(gpu);
}
if (gl) {
glProgram = GlProgram.from(gl);
}
return new Shader({
gpuProgram,
glProgram,
...rest
});
}
}
export { Shader };
//# sourceMappingURL=Shader.mjs.map