@pixi/core
Version:
Core PixiJS
364 lines (359 loc) • 12.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var color = require('@pixi/color');
var constants = require('@pixi/constants');
var extensions = require('@pixi/extensions');
var settings = require('@pixi/settings');
var utils = require('@pixi/utils');
var ViewableBuffer = require('../geometry/ViewableBuffer.js');
var checkMaxIfStatementsInShader = require('../shader/utils/checkMaxIfStatementsInShader.js');
var State = require('../state/State.js');
var BaseTexture = require('../textures/BaseTexture.js');
var BatchDrawCall = require('./BatchDrawCall.js');
var BatchGeometry = require('./BatchGeometry.js');
var BatchShaderGenerator = require('./BatchShaderGenerator.js');
var BatchTextureArray = require('./BatchTextureArray.js');
var canUploadSameBuffer = require('./canUploadSameBuffer.js');
var maxRecommendedTextures = require('./maxRecommendedTextures.js');
var ObjectRenderer = require('./ObjectRenderer.js');
var texture$1 = require('./texture.js');
var texture = require('./texture2.js');
const _BatchRenderer = class extends ObjectRenderer.ObjectRenderer {
constructor(renderer) {
super(renderer);
this.setShaderGenerator();
this.geometryClass = BatchGeometry.BatchGeometry;
this.vertexSize = 6;
this.state = State.State.for2d();
this.size = _BatchRenderer.defaultBatchSize * 4;
this._vertexCount = 0;
this._indexCount = 0;
this._bufferedElements = [];
this._bufferedTextures = [];
this._bufferSize = 0;
this._shader = null;
this._packedGeometries = [];
this._packedGeometryPoolSize = 2;
this._flushId = 0;
this._aBuffers = {};
this._iBuffers = {};
this.maxTextures = 1;
this.renderer.on("prerender", this.onPrerender, this);
renderer.runners.contextChange.add(this);
this._dcIndex = 0;
this._aIndex = 0;
this._iIndex = 0;
this._attributeBuffer = null;
this._indexBuffer = null;
this._tempBoundTextures = [];
}
static get defaultMaxTextures() {
this._defaultMaxTextures = this._defaultMaxTextures ?? maxRecommendedTextures.maxRecommendedTextures(32);
return this._defaultMaxTextures;
}
static set defaultMaxTextures(value) {
this._defaultMaxTextures = value;
}
static get canUploadSameBuffer() {
this._canUploadSameBuffer = this._canUploadSameBuffer ?? canUploadSameBuffer.canUploadSameBuffer();
return this._canUploadSameBuffer;
}
static set canUploadSameBuffer(value) {
this._canUploadSameBuffer = value;
}
get MAX_TEXTURES() {
utils.deprecation("7.1.0", "BatchRenderer#MAX_TEXTURES renamed to BatchRenderer#maxTextures");
return this.maxTextures;
}
static get defaultVertexSrc() {
return texture["default"];
}
static get defaultFragmentTemplate() {
return texture$1["default"];
}
setShaderGenerator({
vertex = _BatchRenderer.defaultVertexSrc,
fragment = _BatchRenderer.defaultFragmentTemplate
} = {}) {
this.shaderGenerator = new BatchShaderGenerator.BatchShaderGenerator(vertex, fragment);
}
contextChange() {
const gl = this.renderer.gl;
if (settings.settings.PREFER_ENV === constants.ENV.WEBGL_LEGACY) {
this.maxTextures = 1;
} else {
this.maxTextures = Math.min(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), _BatchRenderer.defaultMaxTextures);
this.maxTextures = checkMaxIfStatementsInShader.checkMaxIfStatementsInShader(this.maxTextures, gl);
}
this._shader = this.shaderGenerator.generateShader(this.maxTextures);
for (let i = 0; i < this._packedGeometryPoolSize; i++) {
this._packedGeometries[i] = new this.geometryClass();
}
this.initFlushBuffers();
}
initFlushBuffers() {
const {
_drawCallPool,
_textureArrayPool
} = _BatchRenderer;
const MAX_SPRITES = this.size / 4;
const MAX_TA = Math.floor(MAX_SPRITES / this.maxTextures) + 1;
while (_drawCallPool.length < MAX_SPRITES) {
_drawCallPool.push(new BatchDrawCall.BatchDrawCall());
}
while (_textureArrayPool.length < MAX_TA) {
_textureArrayPool.push(new BatchTextureArray.BatchTextureArray());
}
for (let i = 0; i < this.maxTextures; i++) {
this._tempBoundTextures[i] = null;
}
}
onPrerender() {
this._flushId = 0;
}
render(element) {
if (!element._texture.valid) {
return;
}
if (this._vertexCount + element.vertexData.length / 2 > this.size) {
this.flush();
}
this._vertexCount += element.vertexData.length / 2;
this._indexCount += element.indices.length;
this._bufferedTextures[this._bufferSize] = element._texture.baseTexture;
this._bufferedElements[this._bufferSize++] = element;
}
buildTexturesAndDrawCalls() {
const {
_bufferedTextures: textures,
maxTextures
} = this;
const textureArrays = _BatchRenderer._textureArrayPool;
const batch = this.renderer.batch;
const boundTextures = this._tempBoundTextures;
const touch = this.renderer.textureGC.count;
let TICK = ++BaseTexture.BaseTexture._globalBatch;
let countTexArrays = 0;
let texArray = textureArrays[0];
let start = 0;
batch.copyBoundTextures(boundTextures, maxTextures);
for (let i = 0; i < this._bufferSize; ++i) {
const tex = textures[i];
textures[i] = null;
if (tex._batchEnabled === TICK) {
continue;
}
if (texArray.count >= maxTextures) {
batch.boundArray(texArray, boundTextures, TICK, maxTextures);
this.buildDrawCalls(texArray, start, i);
start = i;
texArray = textureArrays[++countTexArrays];
++TICK;
}
tex._batchEnabled = TICK;
tex.touched = touch;
texArray.elements[texArray.count++] = tex;
}
if (texArray.count > 0) {
batch.boundArray(texArray, boundTextures, TICK, maxTextures);
this.buildDrawCalls(texArray, start, this._bufferSize);
++countTexArrays;
++TICK;
}
for (let i = 0; i < boundTextures.length; i++) {
boundTextures[i] = null;
}
BaseTexture.BaseTexture._globalBatch = TICK;
}
buildDrawCalls(texArray, start, finish) {
const {
_bufferedElements: elements,
_attributeBuffer,
_indexBuffer,
vertexSize
} = this;
const drawCalls = _BatchRenderer._drawCallPool;
let dcIndex = this._dcIndex;
let aIndex = this._aIndex;
let iIndex = this._iIndex;
let drawCall = drawCalls[dcIndex];
drawCall.start = this._iIndex;
drawCall.texArray = texArray;
for (let i = start; i < finish; ++i) {
const sprite = elements[i];
const tex = sprite._texture.baseTexture;
const spriteBlendMode = utils.premultiplyBlendMode[tex.alphaMode ? 1 : 0][sprite.blendMode];
elements[i] = null;
if (start < i && drawCall.blend !== spriteBlendMode) {
drawCall.size = iIndex - drawCall.start;
start = i;
drawCall = drawCalls[++dcIndex];
drawCall.texArray = texArray;
drawCall.start = iIndex;
}
this.packInterleavedGeometry(sprite, _attributeBuffer, _indexBuffer, aIndex, iIndex);
aIndex += sprite.vertexData.length / 2 * vertexSize;
iIndex += sprite.indices.length;
drawCall.blend = spriteBlendMode;
}
if (start < finish) {
drawCall.size = iIndex - drawCall.start;
++dcIndex;
}
this._dcIndex = dcIndex;
this._aIndex = aIndex;
this._iIndex = iIndex;
}
bindAndClearTexArray(texArray) {
const textureSystem = this.renderer.texture;
for (let j = 0; j < texArray.count; j++) {
textureSystem.bind(texArray.elements[j], texArray.ids[j]);
texArray.elements[j] = null;
}
texArray.count = 0;
}
updateGeometry() {
const {
_packedGeometries: packedGeometries,
_attributeBuffer: attributeBuffer,
_indexBuffer: indexBuffer
} = this;
if (!_BatchRenderer.canUploadSameBuffer) {
if (this._packedGeometryPoolSize <= this._flushId) {
this._packedGeometryPoolSize++;
packedGeometries[this._flushId] = new this.geometryClass();
}
packedGeometries[this._flushId]._buffer.update(attributeBuffer.rawBinaryData);
packedGeometries[this._flushId]._indexBuffer.update(indexBuffer);
this.renderer.geometry.bind(packedGeometries[this._flushId]);
this.renderer.geometry.updateBuffers();
this._flushId++;
} else {
packedGeometries[this._flushId]._buffer.update(attributeBuffer.rawBinaryData);
packedGeometries[this._flushId]._indexBuffer.update(indexBuffer);
this.renderer.geometry.updateBuffers();
}
}
drawBatches() {
const dcCount = this._dcIndex;
const { gl, state: stateSystem } = this.renderer;
const drawCalls = _BatchRenderer._drawCallPool;
let curTexArray = null;
for (let i = 0; i < dcCount; i++) {
const { texArray, type, size, start, blend } = drawCalls[i];
if (curTexArray !== texArray) {
curTexArray = texArray;
this.bindAndClearTexArray(texArray);
}
this.state.blendMode = blend;
stateSystem.set(this.state);
gl.drawElements(type, size, gl.UNSIGNED_SHORT, start * 2);
}
}
flush() {
if (this._vertexCount === 0) {
return;
}
this._attributeBuffer = this.getAttributeBuffer(this._vertexCount);
this._indexBuffer = this.getIndexBuffer(this._indexCount);
this._aIndex = 0;
this._iIndex = 0;
this._dcIndex = 0;
this.buildTexturesAndDrawCalls();
this.updateGeometry();
this.drawBatches();
this._bufferSize = 0;
this._vertexCount = 0;
this._indexCount = 0;
}
start() {
this.renderer.state.set(this.state);
this.renderer.texture.ensureSamplerType(this.maxTextures);
this.renderer.shader.bind(this._shader);
if (_BatchRenderer.canUploadSameBuffer) {
this.renderer.geometry.bind(this._packedGeometries[this._flushId]);
}
}
stop() {
this.flush();
}
destroy() {
for (let i = 0; i < this._packedGeometryPoolSize; i++) {
if (this._packedGeometries[i]) {
this._packedGeometries[i].destroy();
}
}
this.renderer.off("prerender", this.onPrerender, this);
this._aBuffers = null;
this._iBuffers = null;
this._packedGeometries = null;
this._attributeBuffer = null;
this._indexBuffer = null;
if (this._shader) {
this._shader.destroy();
this._shader = null;
}
super.destroy();
}
getAttributeBuffer(size) {
const roundedP2 = utils.nextPow2(Math.ceil(size / 8));
const roundedSizeIndex = utils.log2(roundedP2);
const roundedSize = roundedP2 * 8;
if (this._aBuffers.length <= roundedSizeIndex) {
this._iBuffers.length = roundedSizeIndex + 1;
}
let buffer = this._aBuffers[roundedSize];
if (!buffer) {
this._aBuffers[roundedSize] = buffer = new ViewableBuffer.ViewableBuffer(roundedSize * this.vertexSize * 4);
}
return buffer;
}
getIndexBuffer(size) {
const roundedP2 = utils.nextPow2(Math.ceil(size / 12));
const roundedSizeIndex = utils.log2(roundedP2);
const roundedSize = roundedP2 * 12;
if (this._iBuffers.length <= roundedSizeIndex) {
this._iBuffers.length = roundedSizeIndex + 1;
}
let buffer = this._iBuffers[roundedSizeIndex];
if (!buffer) {
this._iBuffers[roundedSizeIndex] = buffer = new Uint16Array(roundedSize);
}
return buffer;
}
packInterleavedGeometry(element, attributeBuffer, indexBuffer, aIndex, iIndex) {
const {
uint32View,
float32View
} = attributeBuffer;
const packedVertices = aIndex / this.vertexSize;
const uvs = element.uvs;
const indicies = element.indices;
const vertexData = element.vertexData;
const textureId = element._texture.baseTexture._batchLocation;
const alpha = Math.min(element.worldAlpha, 1);
const argb = color.Color.shared.setValue(element._tintRGB).toPremultiplied(alpha);
for (let i = 0; i < vertexData.length; i += 2) {
float32View[aIndex++] = vertexData[i];
float32View[aIndex++] = vertexData[i + 1];
float32View[aIndex++] = uvs[i];
float32View[aIndex++] = uvs[i + 1];
uint32View[aIndex++] = argb;
float32View[aIndex++] = textureId;
}
for (let i = 0; i < indicies.length; i++) {
indexBuffer[iIndex++] = packedVertices + indicies[i];
}
}
};
let BatchRenderer = _BatchRenderer;
BatchRenderer.defaultBatchSize = 4096;
BatchRenderer.extension = {
name: "batch",
type: extensions.ExtensionType.RendererPlugin
};
BatchRenderer._drawCallPool = [];
BatchRenderer._textureArrayPool = [];
extensions.extensions.add(BatchRenderer);
exports.BatchRenderer = BatchRenderer;
//# sourceMappingURL=BatchRenderer.js.map