UNPKG

9.82 kBJavaScriptView Raw
1import { ENV, BUFFER_TYPE } from '@pixi/constants';
2import { ExtensionType, extensions } from '@pixi/extensions';
3import { settings } from '@pixi/settings';
4
5const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
6class GeometrySystem {
7 constructor(renderer) {
8 this.renderer = renderer;
9 this._activeGeometry = null;
10 this._activeVao = null;
11 this.hasVao = true;
12 this.hasInstance = true;
13 this.canUseUInt32ElementIndex = false;
14 this.managedGeometries = {};
15 }
16 contextChange() {
17 this.disposeAll(true);
18 const gl = this.gl = this.renderer.gl;
19 const context = this.renderer.context;
20 this.CONTEXT_UID = this.renderer.CONTEXT_UID;
21 if (context.webGLVersion !== 2) {
22 let nativeVaoExtension = this.renderer.context.extensions.vertexArrayObject;
23 if (settings.PREFER_ENV === ENV.WEBGL_LEGACY) {
24 nativeVaoExtension = null;
25 }
26 if (nativeVaoExtension) {
27 gl.createVertexArray = () => nativeVaoExtension.createVertexArrayOES();
28 gl.bindVertexArray = (vao) => nativeVaoExtension.bindVertexArrayOES(vao);
29 gl.deleteVertexArray = (vao) => nativeVaoExtension.deleteVertexArrayOES(vao);
30 } else {
31 this.hasVao = false;
32 gl.createVertexArray = () => null;
33 gl.bindVertexArray = () => null;
34 gl.deleteVertexArray = () => null;
35 }
36 }
37 if (context.webGLVersion !== 2) {
38 const instanceExt = gl.getExtension("ANGLE_instanced_arrays");
39 if (instanceExt) {
40 gl.vertexAttribDivisor = (a, b) => instanceExt.vertexAttribDivisorANGLE(a, b);
41 gl.drawElementsInstanced = (a, b, c, d, e) => instanceExt.drawElementsInstancedANGLE(a, b, c, d, e);
42 gl.drawArraysInstanced = (a, b, c, d) => instanceExt.drawArraysInstancedANGLE(a, b, c, d);
43 } else {
44 this.hasInstance = false;
45 }
46 }
47 this.canUseUInt32ElementIndex = context.webGLVersion === 2 || !!context.extensions.uint32ElementIndex;
48 }
49 bind(geometry, shader) {
50 shader = shader || this.renderer.shader.shader;
51 const { gl } = this;
52 let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID];
53 let incRefCount = false;
54 if (!vaos) {
55 this.managedGeometries[geometry.id] = geometry;
56 geometry.disposeRunner.add(this);
57 geometry.glVertexArrayObjects[this.CONTEXT_UID] = vaos = {};
58 incRefCount = true;
59 }
60 const vao = vaos[shader.program.id] || this.initGeometryVao(geometry, shader, incRefCount);
61 this._activeGeometry = geometry;
62 if (this._activeVao !== vao) {
63 this._activeVao = vao;
64 if (this.hasVao) {
65 gl.bindVertexArray(vao);
66 } else {
67 this.activateVao(geometry, shader.program);
68 }
69 }
70 this.updateBuffers();
71 }
72 reset() {
73 this.unbind();
74 }
75 updateBuffers() {
76 const geometry = this._activeGeometry;
77 const bufferSystem = this.renderer.buffer;
78 for (let i = 0; i < geometry.buffers.length; i++) {
79 const buffer = geometry.buffers[i];
80 bufferSystem.update(buffer);
81 }
82 }
83 checkCompatibility(geometry, program) {
84 const geometryAttributes = geometry.attributes;
85 const shaderAttributes = program.attributeData;
86 for (const j in shaderAttributes) {
87 if (!geometryAttributes[j]) {
88 throw new Error(`shader and geometry incompatible, geometry missing the "${j}" attribute`);
89 }
90 }
91 }
92 getSignature(geometry, program) {
93 const attribs = geometry.attributes;
94 const shaderAttributes = program.attributeData;
95 const strings = ["g", geometry.id];
96 for (const i in attribs) {
97 if (shaderAttributes[i]) {
98 strings.push(i, shaderAttributes[i].location);
99 }
100 }
101 return strings.join("-");
102 }
103 initGeometryVao(geometry, shader, incRefCount = true) {
104 const gl = this.gl;
105 const CONTEXT_UID = this.CONTEXT_UID;
106 const bufferSystem = this.renderer.buffer;
107 const program = shader.program;
108 if (!program.glPrograms[CONTEXT_UID]) {
109 this.renderer.shader.generateProgram(shader);
110 }
111 this.checkCompatibility(geometry, program);
112 const signature = this.getSignature(geometry, program);
113 const vaoObjectHash = geometry.glVertexArrayObjects[this.CONTEXT_UID];
114 let vao = vaoObjectHash[signature];
115 if (vao) {
116 vaoObjectHash[program.id] = vao;
117 return vao;
118 }
119 const buffers = geometry.buffers;
120 const attributes = geometry.attributes;
121 const tempStride = {};
122 const tempStart = {};
123 for (const j in buffers) {
124 tempStride[j] = 0;
125 tempStart[j] = 0;
126 }
127 for (const j in attributes) {
128 if (!attributes[j].size && program.attributeData[j]) {
129 attributes[j].size = program.attributeData[j].size;
130 } else if (!attributes[j].size) {
131 console.warn(`PIXI Geometry attribute '${j}' size cannot be determined (likely the bound shader does not have the attribute)`);
132 }
133 tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type];
134 }
135 for (const j in attributes) {
136 const attribute = attributes[j];
137 const attribSize = attribute.size;
138 if (attribute.stride === void 0) {
139 if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) {
140 attribute.stride = 0;
141 } else {
142 attribute.stride = tempStride[attribute.buffer];
143 }
144 }
145 if (attribute.start === void 0) {
146 attribute.start = tempStart[attribute.buffer];
147 tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type];
148 }
149 }
150 vao = gl.createVertexArray();
151 gl.bindVertexArray(vao);
152 for (let i = 0; i < buffers.length; i++) {
153 const buffer = buffers[i];
154 bufferSystem.bind(buffer);
155 if (incRefCount) {
156 buffer._glBuffers[CONTEXT_UID].refCount++;
157 }
158 }
159 this.activateVao(geometry, program);
160 vaoObjectHash[program.id] = vao;
161 vaoObjectHash[signature] = vao;
162 gl.bindVertexArray(null);
163 bufferSystem.unbind(BUFFER_TYPE.ARRAY_BUFFER);
164 return vao;
165 }
166 disposeGeometry(geometry, contextLost) {
167 if (!this.managedGeometries[geometry.id]) {
168 return;
169 }
170 delete this.managedGeometries[geometry.id];
171 const vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID];
172 const gl = this.gl;
173 const buffers = geometry.buffers;
174 const bufferSystem = this.renderer?.buffer;
175 geometry.disposeRunner.remove(this);
176 if (!vaos) {
177 return;
178 }
179 if (bufferSystem) {
180 for (let i = 0; i < buffers.length; i++) {
181 const buf = buffers[i]._glBuffers[this.CONTEXT_UID];
182 if (buf) {
183 buf.refCount--;
184 if (buf.refCount === 0 && !contextLost) {
185 bufferSystem.dispose(buffers[i], contextLost);
186 }
187 }
188 }
189 }
190 if (!contextLost) {
191 for (const vaoId in vaos) {
192 if (vaoId[0] === "g") {
193 const vao = vaos[vaoId];
194 if (this._activeVao === vao) {
195 this.unbind();
196 }
197 gl.deleteVertexArray(vao);
198 }
199 }
200 }
201 delete geometry.glVertexArrayObjects[this.CONTEXT_UID];
202 }
203 disposeAll(contextLost) {
204 const all = Object.keys(this.managedGeometries);
205 for (let i = 0; i < all.length; i++) {
206 this.disposeGeometry(this.managedGeometries[all[i]], contextLost);
207 }
208 }
209 activateVao(geometry, program) {
210 const gl = this.gl;
211 const CONTEXT_UID = this.CONTEXT_UID;
212 const bufferSystem = this.renderer.buffer;
213 const buffers = geometry.buffers;
214 const attributes = geometry.attributes;
215 if (geometry.indexBuffer) {
216 bufferSystem.bind(geometry.indexBuffer);
217 }
218 let lastBuffer = null;
219 for (const j in attributes) {
220 const attribute = attributes[j];
221 const buffer = buffers[attribute.buffer];
222 const glBuffer = buffer._glBuffers[CONTEXT_UID];
223 if (program.attributeData[j]) {
224 if (lastBuffer !== glBuffer) {
225 bufferSystem.bind(buffer);
226 lastBuffer = glBuffer;
227 }
228 const location = program.attributeData[j].location;
229 gl.enableVertexAttribArray(location);
230 gl.vertexAttribPointer(location, attribute.size, attribute.type || gl.FLOAT, attribute.normalized, attribute.stride, attribute.start);
231 if (attribute.instance) {
232 if (this.hasInstance) {
233 gl.vertexAttribDivisor(location, attribute.divisor);
234 } else {
235 throw new Error("geometry error, GPU Instancing is not supported on this device");
236 }
237 }
238 }
239 }
240 }
241 draw(type, size, start, instanceCount) {
242 const { gl } = this;
243 const geometry = this._activeGeometry;
244 if (geometry.indexBuffer) {
245 const byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT;
246 const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
247 if (byteSize === 2 || byteSize === 4 && this.canUseUInt32ElementIndex) {
248 if (geometry.instanced) {
249 gl.drawElementsInstanced(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount || 1);
250 } else {
251 gl.drawElements(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize);
252 }
253 } else {
254 console.warn("unsupported index buffer type: uint32");
255 }
256 } else if (geometry.instanced) {
257 gl.drawArraysInstanced(type, start, size || geometry.getSize(), instanceCount || 1);
258 } else {
259 gl.drawArrays(type, start, size || geometry.getSize());
260 }
261 return this;
262 }
263 unbind() {
264 this.gl.bindVertexArray(null);
265 this._activeVao = null;
266 this._activeGeometry = null;
267 }
268 destroy() {
269 this.renderer = null;
270 }
271}
272GeometrySystem.extension = {
273 type: ExtensionType.RendererSystem,
274 name: "geometry"
275};
276extensions.add(GeometrySystem);
277
278export { GeometrySystem };
279//# sourceMappingURL=GeometrySystem.mjs.map