1 | import { ExtensionType, extensions } from '@pixi/extensions';
|
2 | import './utils/index.mjs';
|
3 | import { generateProgram } from './utils/generateProgram.mjs';
|
4 | import { generateUniformBufferSync } from './utils/generateUniformBufferSync.mjs';
|
5 | import { unsafeEvalSupported } from './utils/unsafeEvalSupported.mjs';
|
6 | import { generateUniformsSync } from './utils/generateUniformsSync.mjs';
|
7 |
|
8 | let UID = 0;
|
9 | const defaultSyncData = { textureCount: 0, uboCount: 0 };
|
10 | class ShaderSystem {
|
11 | constructor(renderer) {
|
12 | this.destroyed = false;
|
13 | this.renderer = renderer;
|
14 | this.systemCheck();
|
15 | this.gl = null;
|
16 | this.shader = null;
|
17 | this.program = null;
|
18 | this.cache = {};
|
19 | this._uboCache = {};
|
20 | this.id = UID++;
|
21 | }
|
22 | systemCheck() {
|
23 | if (!unsafeEvalSupported()) {
|
24 | throw new Error("Current environment does not allow unsafe-eval, please use @pixi/unsafe-eval module to enable support.");
|
25 | }
|
26 | }
|
27 | contextChange(gl) {
|
28 | this.gl = gl;
|
29 | this.reset();
|
30 | }
|
31 | bind(shader, dontSync) {
|
32 | shader.disposeRunner.add(this);
|
33 | shader.uniforms.globals = this.renderer.globalUniforms;
|
34 | const program = shader.program;
|
35 | const glProgram = program.glPrograms[this.renderer.CONTEXT_UID] || this.generateProgram(shader);
|
36 | this.shader = shader;
|
37 | if (this.program !== program) {
|
38 | this.program = program;
|
39 | this.gl.useProgram(glProgram.program);
|
40 | }
|
41 | if (!dontSync) {
|
42 | defaultSyncData.textureCount = 0;
|
43 | defaultSyncData.uboCount = 0;
|
44 | this.syncUniformGroup(shader.uniformGroup, defaultSyncData);
|
45 | }
|
46 | return glProgram;
|
47 | }
|
48 | setUniforms(uniforms) {
|
49 | const shader = this.shader.program;
|
50 | const glProgram = shader.glPrograms[this.renderer.CONTEXT_UID];
|
51 | shader.syncUniforms(glProgram.uniformData, uniforms, this.renderer);
|
52 | }
|
53 | syncUniformGroup(group, syncData) {
|
54 | const glProgram = this.getGlProgram();
|
55 | if (!group.static || group.dirtyId !== glProgram.uniformDirtyGroups[group.id]) {
|
56 | glProgram.uniformDirtyGroups[group.id] = group.dirtyId;
|
57 | this.syncUniforms(group, glProgram, syncData);
|
58 | }
|
59 | }
|
60 | syncUniforms(group, glProgram, syncData) {
|
61 | const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
|
62 | syncFunc(glProgram.uniformData, group.uniforms, this.renderer, syncData);
|
63 | }
|
64 | createSyncGroups(group) {
|
65 | const id = this.getSignature(group, this.shader.program.uniformData, "u");
|
66 | if (!this.cache[id]) {
|
67 | this.cache[id] = generateUniformsSync(group, this.shader.program.uniformData);
|
68 | }
|
69 | group.syncUniforms[this.shader.program.id] = this.cache[id];
|
70 | return group.syncUniforms[this.shader.program.id];
|
71 | }
|
72 | syncUniformBufferGroup(group, name) {
|
73 | const glProgram = this.getGlProgram();
|
74 | if (!group.static || group.dirtyId !== 0 || !glProgram.uniformGroups[group.id]) {
|
75 | group.dirtyId = 0;
|
76 | const syncFunc = glProgram.uniformGroups[group.id] || this.createSyncBufferGroup(group, glProgram, name);
|
77 | group.buffer.update();
|
78 | syncFunc(glProgram.uniformData, group.uniforms, this.renderer, defaultSyncData, group.buffer);
|
79 | }
|
80 | this.renderer.buffer.bindBufferBase(group.buffer, glProgram.uniformBufferBindings[name]);
|
81 | }
|
82 | createSyncBufferGroup(group, glProgram, name) {
|
83 | const { gl } = this.renderer;
|
84 | this.renderer.buffer.bind(group.buffer);
|
85 | const uniformBlockIndex = this.gl.getUniformBlockIndex(glProgram.program, name);
|
86 | glProgram.uniformBufferBindings[name] = this.shader.uniformBindCount;
|
87 | gl.uniformBlockBinding(glProgram.program, uniformBlockIndex, this.shader.uniformBindCount);
|
88 | this.shader.uniformBindCount++;
|
89 | const id = this.getSignature(group, this.shader.program.uniformData, "ubo");
|
90 | let uboData = this._uboCache[id];
|
91 | if (!uboData) {
|
92 | uboData = this._uboCache[id] = generateUniformBufferSync(group, this.shader.program.uniformData);
|
93 | }
|
94 | if (group.autoManage) {
|
95 | const data = new Float32Array(uboData.size / 4);
|
96 | group.buffer.update(data);
|
97 | }
|
98 | glProgram.uniformGroups[group.id] = uboData.syncFunc;
|
99 | return glProgram.uniformGroups[group.id];
|
100 | }
|
101 | getSignature(group, uniformData, preFix) {
|
102 | const uniforms = group.uniforms;
|
103 | const strings = [`${preFix}-`];
|
104 | for (const i in uniforms) {
|
105 | strings.push(i);
|
106 | if (uniformData[i]) {
|
107 | strings.push(uniformData[i].type);
|
108 | }
|
109 | }
|
110 | return strings.join("-");
|
111 | }
|
112 | getGlProgram() {
|
113 | if (this.shader) {
|
114 | return this.shader.program.glPrograms[this.renderer.CONTEXT_UID];
|
115 | }
|
116 | return null;
|
117 | }
|
118 | generateProgram(shader) {
|
119 | const gl = this.gl;
|
120 | const program = shader.program;
|
121 | const glProgram = generateProgram(gl, program);
|
122 | program.glPrograms[this.renderer.CONTEXT_UID] = glProgram;
|
123 | return glProgram;
|
124 | }
|
125 | reset() {
|
126 | this.program = null;
|
127 | this.shader = null;
|
128 | }
|
129 | disposeShader(shader) {
|
130 | if (this.shader === shader) {
|
131 | this.shader = null;
|
132 | }
|
133 | }
|
134 | destroy() {
|
135 | this.renderer = null;
|
136 | this.destroyed = true;
|
137 | }
|
138 | }
|
139 | ShaderSystem.extension = {
|
140 | type: ExtensionType.RendererSystem,
|
141 | name: "shader"
|
142 | };
|
143 | extensions.add(ShaderSystem);
|
144 |
|
145 | export { ShaderSystem };
|
146 |
|