UNPKG

@pixi/core

Version:
280 lines (277 loc) 9.82 kB
import { ENV, BUFFER_TYPE } from '@pixi/constants'; import { ExtensionType, extensions } from '@pixi/extensions'; import { settings } from '@pixi/settings'; const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; class GeometrySystem { constructor(renderer) { this.renderer = renderer; this._activeGeometry = null; this._activeVao = null; this.hasVao = true; this.hasInstance = true; this.canUseUInt32ElementIndex = false; this.managedGeometries = {}; } contextChange() { this.disposeAll(true); const gl = this.gl = this.renderer.gl; const context = this.renderer.context; this.CONTEXT_UID = this.renderer.CONTEXT_UID; if (context.webGLVersion !== 2) { let nativeVaoExtension = this.renderer.context.extensions.vertexArrayObject; if (settings.PREFER_ENV === ENV.WEBGL_LEGACY) { nativeVaoExtension = null; } if (nativeVaoExtension) { gl.createVertexArray = () => nativeVaoExtension.createVertexArrayOES(); gl.bindVertexArray = (vao) => nativeVaoExtension.bindVertexArrayOES(vao); gl.deleteVertexArray = (vao) => nativeVaoExtension.deleteVertexArrayOES(vao); } else { this.hasVao = false; gl.createVertexArray = () => null; gl.bindVertexArray = () => null; gl.deleteVertexArray = () => null; } } if (context.webGLVersion !== 2) { const instanceExt = gl.getExtension("ANGLE_instanced_arrays"); if (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); } else { this.hasInstance = false; } } this.canUseUInt32ElementIndex = context.webGLVersion === 2 || !!context.extensions.uint32ElementIndex; } bind(geometry, shader) { shader = shader || this.renderer.shader.shader; const { gl } = this; let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; let incRefCount = false; if (!vaos) { this.managedGeometries[geometry.id] = geometry; geometry.disposeRunner.add(this); geometry.glVertexArrayObjects[this.CONTEXT_UID] = vaos = {}; incRefCount = true; } const vao = vaos[shader.program.id] || this.initGeometryVao(geometry, shader, incRefCount); this._activeGeometry = geometry; if (this._activeVao !== vao) { this._activeVao = vao; if (this.hasVao) { gl.bindVertexArray(vao); } else { this.activateVao(geometry, shader.program); } } this.updateBuffers(); } reset() { this.unbind(); } updateBuffers() { const geometry = this._activeGeometry; const bufferSystem = this.renderer.buffer; for (let i = 0; i < geometry.buffers.length; i++) { const buffer = geometry.buffers[i]; bufferSystem.update(buffer); } } checkCompatibility(geometry, program) { const geometryAttributes = geometry.attributes; const shaderAttributes = program.attributeData; for (const j in shaderAttributes) { if (!geometryAttributes[j]) { throw new Error(`shader and geometry incompatible, geometry missing the "${j}" attribute`); } } } getSignature(geometry, program) { const attribs = geometry.attributes; const shaderAttributes = program.attributeData; const strings = ["g", geometry.id]; for (const i in attribs) { if (shaderAttributes[i]) { strings.push(i, shaderAttributes[i].location); } } return strings.join("-"); } initGeometryVao(geometry, shader, incRefCount = true) { const gl = this.gl; const CONTEXT_UID = this.CONTEXT_UID; const bufferSystem = this.renderer.buffer; const program = shader.program; if (!program.glPrograms[CONTEXT_UID]) { this.renderer.shader.generateProgram(shader); } this.checkCompatibility(geometry, program); const signature = this.getSignature(geometry, program); const vaoObjectHash = geometry.glVertexArrayObjects[this.CONTEXT_UID]; let vao = vaoObjectHash[signature]; if (vao) { vaoObjectHash[program.id] = vao; return vao; } const buffers = geometry.buffers; const attributes = geometry.attributes; const tempStride = {}; const tempStart = {}; for (const j in buffers) { tempStride[j] = 0; tempStart[j] = 0; } for (const j in attributes) { if (!attributes[j].size && program.attributeData[j]) { attributes[j].size = program.attributeData[j].size; } else if (!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]; } for (const j in attributes) { const attribute = attributes[j]; const attribSize = attribute.size; if (attribute.stride === void 0) { if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) { attribute.stride = 0; } else { attribute.stride = tempStride[attribute.buffer]; } } if (attribute.start === void 0) { attribute.start = tempStart[attribute.buffer]; tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]; } } vao = gl.createVertexArray(); gl.bindVertexArray(vao); for (let i = 0; i < buffers.length; i++) { const buffer = buffers[i]; bufferSystem.bind(buffer); if (incRefCount) { buffer._glBuffers[CONTEXT_UID].refCount++; } } this.activateVao(geometry, program); vaoObjectHash[program.id] = vao; vaoObjectHash[signature] = vao; gl.bindVertexArray(null); bufferSystem.unbind(BUFFER_TYPE.ARRAY_BUFFER); return vao; } disposeGeometry(geometry, contextLost) { if (!this.managedGeometries[geometry.id]) { return; } delete this.managedGeometries[geometry.id]; const vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; const gl = this.gl; const buffers = geometry.buffers; const bufferSystem = this.renderer?.buffer; geometry.disposeRunner.remove(this); if (!vaos) { return; } if (bufferSystem) { for (let i = 0; i < buffers.length; i++) { const buf = buffers[i]._glBuffers[this.CONTEXT_UID]; if (buf) { buf.refCount--; if (buf.refCount === 0 && !contextLost) { bufferSystem.dispose(buffers[i], contextLost); } } } } if (!contextLost) { for (const vaoId in vaos) { if (vaoId[0] === "g") { const vao = vaos[vaoId]; if (this._activeVao === vao) { this.unbind(); } gl.deleteVertexArray(vao); } } } delete geometry.glVertexArrayObjects[this.CONTEXT_UID]; } disposeAll(contextLost) { const all = Object.keys(this.managedGeometries); for (let i = 0; i < all.length; i++) { this.disposeGeometry(this.managedGeometries[all[i]], contextLost); } } activateVao(geometry, program) { const gl = this.gl; const CONTEXT_UID = this.CONTEXT_UID; const bufferSystem = this.renderer.buffer; const buffers = geometry.buffers; const attributes = geometry.attributes; if (geometry.indexBuffer) { bufferSystem.bind(geometry.indexBuffer); } let lastBuffer = null; for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; const glBuffer = buffer._glBuffers[CONTEXT_UID]; if (program.attributeData[j]) { if (lastBuffer !== glBuffer) { bufferSystem.bind(buffer); lastBuffer = glBuffer; } const location = program.attributeData[j].location; gl.enableVertexAttribArray(location); gl.vertexAttribPointer(location, attribute.size, attribute.type || gl.FLOAT, attribute.normalized, attribute.stride, attribute.start); if (attribute.instance) { if (this.hasInstance) { gl.vertexAttribDivisor(location, attribute.divisor); } else { throw new Error("geometry error, GPU Instancing is not supported on this device"); } } } } } draw(type, size, start, instanceCount) { const { gl } = this; const geometry = this._activeGeometry; if (geometry.indexBuffer) { const byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT; const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT; if (byteSize === 2 || byteSize === 4 && this.canUseUInt32ElementIndex) { if (geometry.instanced) { gl.drawElementsInstanced(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount || 1); } else { gl.drawElements(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize); } } else { console.warn("unsupported index buffer type: uint32"); } } else if (geometry.instanced) { gl.drawArraysInstanced(type, start, size || geometry.getSize(), instanceCount || 1); } else { gl.drawArrays(type, start, size || geometry.getSize()); } return this; } unbind() { this.gl.bindVertexArray(null); this._activeVao = null; this._activeGeometry = null; } destroy() { this.renderer = null; } } GeometrySystem.extension = { type: ExtensionType.RendererSystem, name: "geometry" }; extensions.add(GeometrySystem); export { GeometrySystem }; //# sourceMappingURL=GeometrySystem.mjs.map