UNPKG

11 kBJavaScriptView Raw
1import { ENV, BUFFER_TYPE } from "@pixi/constants";
2import { ExtensionType, extensions } from "@pixi/extensions";
3import { settings } from "@pixi/settings";
4const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
5class GeometrySystem {
6 /** @param renderer - The renderer this System works for. */
7 constructor(renderer) {
8 this.renderer = renderer, this._activeGeometry = null, this._activeVao = null, this.hasVao = !0, this.hasInstance = !0, this.canUseUInt32ElementIndex = !1, this.managedGeometries = {};
9 }
10 /** Sets up the renderer context and necessary buffers. */
11 contextChange() {
12 this.disposeAll(!0);
13 const gl = this.gl = this.renderer.gl, context = this.renderer.context;
14 if (this.CONTEXT_UID = this.renderer.CONTEXT_UID, context.webGLVersion !== 2) {
15 let nativeVaoExtension = this.renderer.context.extensions.vertexArrayObject;
16 settings.PREFER_ENV === ENV.WEBGL_LEGACY && (nativeVaoExtension = null), nativeVaoExtension ? (gl.createVertexArray = () => nativeVaoExtension.createVertexArrayOES(), gl.bindVertexArray = (vao) => nativeVaoExtension.bindVertexArrayOES(vao), gl.deleteVertexArray = (vao) => nativeVaoExtension.deleteVertexArrayOES(vao)) : (this.hasVao = !1, gl.createVertexArray = () => null, gl.bindVertexArray = () => null, gl.deleteVertexArray = () => null);
17 }
18 if (context.webGLVersion !== 2) {
19 const instanceExt = gl.getExtension("ANGLE_instanced_arrays");
20 instanceExt ? (gl.vertexAttribDivisor = (a, b) => instanceExt.vertexAttribDivisorANGLE(a, b), gl.drawElementsInstanced = (a, b, c, d, e) => instanceExt.drawElementsInstancedANGLE(a, b, c, d, e), gl.drawArraysInstanced = (a, b, c, d) => instanceExt.drawArraysInstancedANGLE(a, b, c, d)) : this.hasInstance = !1;
21 }
22 this.canUseUInt32ElementIndex = context.webGLVersion === 2 || !!context.extensions.uint32ElementIndex;
23 }
24 /**
25 * Binds geometry so that is can be drawn. Creating a Vao if required
26 * @param geometry - Instance of geometry to bind.
27 * @param shader - Instance of shader to use vao for.
28 */
29 bind(geometry, shader) {
30 shader = shader || this.renderer.shader.shader;
31 const { gl } = this;
32 let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID], incRefCount = !1;
33 vaos || (this.managedGeometries[geometry.id] = geometry, geometry.disposeRunner.add(this), geometry.glVertexArrayObjects[this.CONTEXT_UID] = vaos = {}, incRefCount = !0);
34 const vao = vaos[shader.program.id] || this.initGeometryVao(geometry, shader, incRefCount);
35 this._activeGeometry = geometry, this._activeVao !== vao && (this._activeVao = vao, this.hasVao ? gl.bindVertexArray(vao) : this.activateVao(geometry, shader.program)), this.updateBuffers();
36 }
37 /** Reset and unbind any active VAO and geometry. */
38 reset() {
39 this.unbind();
40 }
41 /** Update buffers of the currently bound geometry. */
42 updateBuffers() {
43 const geometry = this._activeGeometry, bufferSystem = this.renderer.buffer;
44 for (let i = 0; i < geometry.buffers.length; i++) {
45 const buffer = geometry.buffers[i];
46 bufferSystem.update(buffer);
47 }
48 }
49 /**
50 * Check compatibility between a geometry and a program
51 * @param geometry - Geometry instance.
52 * @param program - Program instance.
53 */
54 checkCompatibility(geometry, program) {
55 const geometryAttributes = geometry.attributes, shaderAttributes = program.attributeData;
56 for (const j in shaderAttributes)
57 if (!geometryAttributes[j])
58 throw new Error(`shader and geometry incompatible, geometry missing the "${j}" attribute`);
59 }
60 /**
61 * Takes a geometry and program and generates a unique signature for them.
62 * @param geometry - To get signature from.
63 * @param program - To test geometry against.
64 * @returns - Unique signature of the geometry and program
65 */
66 getSignature(geometry, program) {
67 const attribs = geometry.attributes, shaderAttributes = program.attributeData, strings = ["g", geometry.id];
68 for (const i in attribs)
69 shaderAttributes[i] && strings.push(i, shaderAttributes[i].location);
70 return strings.join("-");
71 }
72 /**
73 * Creates or gets Vao with the same structure as the geometry and stores it on the geometry.
74 * If vao is created, it is bound automatically. We use a shader to infer what and how to set up the
75 * attribute locations.
76 * @param geometry - Instance of geometry to to generate Vao for.
77 * @param shader - Instance of the shader.
78 * @param incRefCount - Increment refCount of all geometry buffers.
79 */
80 initGeometryVao(geometry, shader, incRefCount = !0) {
81 const gl = this.gl, CONTEXT_UID = this.CONTEXT_UID, bufferSystem = this.renderer.buffer, program = shader.program;
82 program.glPrograms[CONTEXT_UID] || this.renderer.shader.generateProgram(shader), this.checkCompatibility(geometry, program);
83 const signature = this.getSignature(geometry, program), vaoObjectHash = geometry.glVertexArrayObjects[this.CONTEXT_UID];
84 let vao = vaoObjectHash[signature];
85 if (vao)
86 return vaoObjectHash[program.id] = vao, vao;
87 const buffers = geometry.buffers, attributes = geometry.attributes, tempStride = {}, tempStart = {};
88 for (const j in buffers)
89 tempStride[j] = 0, tempStart[j] = 0;
90 for (const j in attributes)
91 !attributes[j].size && program.attributeData[j] ? attributes[j].size = program.attributeData[j].size : attributes[j].size || console.warn(`PIXI Geometry attribute '${j}' size cannot be determined (likely the bound shader does not have the attribute)`), tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type];
92 for (const j in attributes) {
93 const attribute = attributes[j], attribSize = attribute.size;
94 attribute.stride === void 0 && (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type] ? attribute.stride = 0 : attribute.stride = tempStride[attribute.buffer]), attribute.start === void 0 && (attribute.start = tempStart[attribute.buffer], tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]);
95 }
96 vao = gl.createVertexArray(), gl.bindVertexArray(vao);
97 for (let i = 0; i < buffers.length; i++) {
98 const buffer = buffers[i];
99 bufferSystem.bind(buffer), incRefCount && buffer._glBuffers[CONTEXT_UID].refCount++;
100 }
101 return this.activateVao(geometry, program), vaoObjectHash[program.id] = vao, vaoObjectHash[signature] = vao, gl.bindVertexArray(null), bufferSystem.unbind(BUFFER_TYPE.ARRAY_BUFFER), vao;
102 }
103 /**
104 * Disposes geometry.
105 * @param geometry - Geometry with buffers. Only VAO will be disposed
106 * @param [contextLost=false] - If context was lost, we suppress deleteVertexArray
107 */
108 disposeGeometry(geometry, contextLost) {
109 if (!this.managedGeometries[geometry.id])
110 return;
111 delete this.managedGeometries[geometry.id];
112 const vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID], gl = this.gl, buffers = geometry.buffers, bufferSystem = this.renderer?.buffer;
113 if (geometry.disposeRunner.remove(this), !!vaos) {
114 if (bufferSystem)
115 for (let i = 0; i < buffers.length; i++) {
116 const buf = buffers[i]._glBuffers[this.CONTEXT_UID];
117 buf && (buf.refCount--, buf.refCount === 0 && !contextLost && bufferSystem.dispose(buffers[i], contextLost));
118 }
119 if (!contextLost) {
120 for (const vaoId in vaos)
121 if (vaoId[0] === "g") {
122 const vao = vaos[vaoId];
123 this._activeVao === vao && this.unbind(), gl.deleteVertexArray(vao);
124 }
125 }
126 delete geometry.glVertexArrayObjects[this.CONTEXT_UID];
127 }
128 }
129 /**
130 * Dispose all WebGL resources of all managed geometries.
131 * @param [contextLost=false] - If context was lost, we suppress `gl.delete` calls
132 */
133 disposeAll(contextLost) {
134 const all = Object.keys(this.managedGeometries);
135 for (let i = 0; i < all.length; i++)
136 this.disposeGeometry(this.managedGeometries[all[i]], contextLost);
137 }
138 /**
139 * Activate vertex array object.
140 * @param geometry - Geometry instance.
141 * @param program - Shader program instance.
142 */
143 activateVao(geometry, program) {
144 const gl = this.gl, CONTEXT_UID = this.CONTEXT_UID, bufferSystem = this.renderer.buffer, buffers = geometry.buffers, attributes = geometry.attributes;
145 geometry.indexBuffer && bufferSystem.bind(geometry.indexBuffer);
146 let lastBuffer = null;
147 for (const j in attributes) {
148 const attribute = attributes[j], buffer = buffers[attribute.buffer], glBuffer = buffer._glBuffers[CONTEXT_UID];
149 if (program.attributeData[j]) {
150 lastBuffer !== glBuffer && (bufferSystem.bind(buffer), lastBuffer = glBuffer);
151 const location = program.attributeData[j].location;
152 if (gl.enableVertexAttribArray(location), gl.vertexAttribPointer(
153 location,
154 attribute.size,
155 attribute.type || gl.FLOAT,
156 attribute.normalized,
157 attribute.stride,
158 attribute.start
159 ), attribute.instance)
160 if (this.hasInstance)
161 gl.vertexAttribDivisor(location, attribute.divisor);
162 else
163 throw new Error("geometry error, GPU Instancing is not supported on this device");
164 }
165 }
166 }
167 /**
168 * Draws the currently bound geometry.
169 * @param type - The type primitive to render.
170 * @param size - The number of elements to be rendered. If not specified, all vertices after the
171 * starting vertex will be drawn.
172 * @param start - The starting vertex in the geometry to start drawing from. If not specified,
173 * drawing will start from the first vertex.
174 * @param instanceCount - The number of instances of the set of elements to execute. If not specified,
175 * all instances will be drawn.
176 */
177 draw(type, size, start, instanceCount) {
178 const { gl } = this, geometry = this._activeGeometry;
179 if (geometry.indexBuffer) {
180 const byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT, glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
181 byteSize === 2 || byteSize === 4 && this.canUseUInt32ElementIndex ? geometry.instanced ? gl.drawElementsInstanced(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount || 1) : gl.drawElements(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize) : console.warn("unsupported index buffer type: uint32");
182 } else
183 geometry.instanced ? gl.drawArraysInstanced(type, start, size || geometry.getSize(), instanceCount || 1) : gl.drawArrays(type, start, size || geometry.getSize());
184 return this;
185 }
186 /** Unbind/reset everything. */
187 unbind() {
188 this.gl.bindVertexArray(null), this._activeVao = null, this._activeGeometry = null;
189 }
190 destroy() {
191 this.renderer = null;
192 }
193}
194GeometrySystem.extension = {
195 type: ExtensionType.RendererSystem,
196 name: "geometry"
197};
198extensions.add(GeometrySystem);
199export {
200 GeometrySystem
201};
202//# sourceMappingURL=GeometrySystem.mjs.map