@pixi/core
Version:
Core PixiJS
167 lines (166 loc) • 7.51 kB
JavaScript
import { ExtensionType, extensions } from "@pixi/extensions";
import "./utils/index.mjs";
import { generateProgram } from "./utils/generateProgram.mjs";
import { generateUniformBufferSync } from "./utils/generateUniformBufferSync.mjs";
import { unsafeEvalSupported } from "./utils/unsafeEvalSupported.mjs";
import { generateUniformsSync } from "./utils/generateUniformsSync.mjs";
let UID = 0;
const defaultSyncData = { textureCount: 0, uboCount: 0 };
class ShaderSystem {
/** @param renderer - The renderer this System works for. */
constructor(renderer) {
this.destroyed = !1, this.renderer = renderer, this.systemCheck(), this.gl = null, this.shader = null, this.program = null, this.cache = {}, this._uboCache = {}, this.id = UID++;
}
/**
* Overrideable function by `@pixi/unsafe-eval` to silence
* throwing an error if platform doesn't support unsafe-evals.
* @private
*/
systemCheck() {
if (!unsafeEvalSupported())
throw new Error("Current environment does not allow unsafe-eval, please use @pixi/unsafe-eval module to enable support.");
}
contextChange(gl) {
this.gl = gl, this.reset();
}
/**
* Changes the current shader to the one given in parameter.
* @param shader - the new shader
* @param dontSync - false if the shader should automatically sync its uniforms.
* @returns the glProgram that belongs to the shader.
*/
bind(shader, dontSync) {
shader.disposeRunner.add(this), shader.uniforms.globals = this.renderer.globalUniforms;
const program = shader.program, glProgram = program.glPrograms[this.renderer.CONTEXT_UID] || this.generateProgram(shader);
return this.shader = shader, this.program !== program && (this.program = program, this.gl.useProgram(glProgram.program)), dontSync || (defaultSyncData.textureCount = 0, defaultSyncData.uboCount = 0, this.syncUniformGroup(shader.uniformGroup, defaultSyncData)), glProgram;
}
/**
* Uploads the uniforms values to the currently bound shader.
* @param uniforms - the uniforms values that be applied to the current shader
*/
setUniforms(uniforms) {
const shader = this.shader.program, glProgram = shader.glPrograms[this.renderer.CONTEXT_UID];
shader.syncUniforms(glProgram.uniformData, uniforms, this.renderer);
}
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/**
* Syncs uniforms on the group
* @param group - the uniform group to sync
* @param syncData - this is data that is passed to the sync function and any nested sync functions
*/
syncUniformGroup(group, syncData) {
const glProgram = this.getGlProgram();
(!group.static || group.dirtyId !== glProgram.uniformDirtyGroups[group.id]) && (glProgram.uniformDirtyGroups[group.id] = group.dirtyId, this.syncUniforms(group, glProgram, syncData));
}
/**
* Overrideable by the @pixi/unsafe-eval package to use static syncUniforms instead.
* @param group
* @param glProgram
* @param syncData
*/
syncUniforms(group, glProgram, syncData) {
(group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group))(glProgram.uniformData, group.uniforms, this.renderer, syncData);
}
createSyncGroups(group) {
const id = this.getSignature(group, this.shader.program.uniformData, "u");
return this.cache[id] || (this.cache[id] = generateUniformsSync(group, this.shader.program.uniformData)), group.syncUniforms[this.shader.program.id] = this.cache[id], group.syncUniforms[this.shader.program.id];
}
/**
* Syncs uniform buffers
* @param group - the uniform buffer group to sync
* @param name - the name of the uniform buffer
*/
syncUniformBufferGroup(group, name) {
const glProgram = this.getGlProgram();
if (!group.static || group.dirtyId !== 0 || !glProgram.uniformGroups[group.id]) {
group.dirtyId = 0;
const syncFunc = glProgram.uniformGroups[group.id] || this.createSyncBufferGroup(group, glProgram, name);
group.buffer.update(), syncFunc(
glProgram.uniformData,
group.uniforms,
this.renderer,
defaultSyncData,
group.buffer
);
}
this.renderer.buffer.bindBufferBase(group.buffer, glProgram.uniformBufferBindings[name]);
}
/**
* Will create a function that uploads a uniform buffer using the STD140 standard.
* The upload function will then be cached for future calls
* If a group is manually managed, then a simple upload function is generated
* @param group - the uniform buffer group to sync
* @param glProgram - the gl program to attach the uniform bindings to
* @param name - the name of the uniform buffer (must exist on the shader)
*/
createSyncBufferGroup(group, glProgram, name) {
const { gl } = this.renderer;
this.renderer.buffer.bind(group.buffer);
const uniformBlockIndex = this.gl.getUniformBlockIndex(glProgram.program, name);
glProgram.uniformBufferBindings[name] = this.shader.uniformBindCount, gl.uniformBlockBinding(glProgram.program, uniformBlockIndex, this.shader.uniformBindCount), this.shader.uniformBindCount++;
const id = this.getSignature(group, this.shader.program.uniformData, "ubo");
let uboData = this._uboCache[id];
if (uboData || (uboData = this._uboCache[id] = generateUniformBufferSync(group, this.shader.program.uniformData)), group.autoManage) {
const data = new Float32Array(uboData.size / 4);
group.buffer.update(data);
}
return glProgram.uniformGroups[group.id] = uboData.syncFunc, glProgram.uniformGroups[group.id];
}
/**
* Takes a uniform group and data and generates a unique signature for them.
* @param group - The uniform group to get signature of
* @param group.uniforms
* @param uniformData - Uniform information generated by the shader
* @param preFix
* @returns Unique signature of the uniform group
*/
getSignature(group, uniformData, preFix) {
const uniforms = group.uniforms, strings = [`${preFix}-`];
for (const i in uniforms)
strings.push(i), uniformData[i] && strings.push(uniformData[i].type);
return strings.join("-");
}
/**
* Returns the underlying GLShade rof the currently bound shader.
*
* This can be handy for when you to have a little more control over the setting of your uniforms.
* @returns The glProgram for the currently bound Shader for this context
*/
getGlProgram() {
return this.shader ? this.shader.program.glPrograms[this.renderer.CONTEXT_UID] : null;
}
/**
* Generates a glProgram version of the Shader provided.
* @param shader - The shader that the glProgram will be based on.
* @returns A shiny new glProgram!
*/
generateProgram(shader) {
const gl = this.gl, program = shader.program, glProgram = generateProgram(gl, program);
return program.glPrograms[this.renderer.CONTEXT_UID] = glProgram, glProgram;
}
/** Resets ShaderSystem state, does not affect WebGL state. */
reset() {
this.program = null, this.shader = null;
}
/**
* Disposes shader.
* If disposing one equals with current shader, set current as null.
* @param shader - Shader object
*/
disposeShader(shader) {
this.shader === shader && (this.shader = null);
}
/** Destroys this System and removes all its textures. */
destroy() {
this.renderer = null, this.destroyed = !0;
}
}
ShaderSystem.extension = {
type: ExtensionType.RendererSystem,
name: "shader"
};
extensions.add(ShaderSystem);
export {
ShaderSystem
};
//# sourceMappingURL=ShaderSystem.mjs.map