@pixi/core
Version:
Core PixiJS
280 lines (277 loc) • 9.82 kB
JavaScript
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