175 lines
5.5 KiB
JavaScript
175 lines
5.5 KiB
JavaScript
|
|
/**
|
||
|
|
* A batch renderer with a customizable data transfer function to packed geometries.
|
||
|
|
* @extends PIXI.AbstractBatchRenderer
|
||
|
|
*/
|
||
|
|
class BatchRenderer extends PIXI.BatchRenderer {
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The batch shader generator class.
|
||
|
|
* @type {typeof BatchShaderGenerator}
|
||
|
|
*/
|
||
|
|
static shaderGeneratorClass = BatchShaderGenerator;
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The default uniform values for the batch shader.
|
||
|
|
* @type {object | (maxTextures: number) => object}
|
||
|
|
*/
|
||
|
|
static defaultUniforms = {};
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The PackInterleavedGeometry function provided by the sampler.
|
||
|
|
* @type {Function|undefined}
|
||
|
|
* @protected
|
||
|
|
*/
|
||
|
|
_packInterleavedGeometry;
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The update function provided by the sampler and that is called just before a flush.
|
||
|
|
* @type {(batchRenderer: BatchRenderer) => void | undefined}
|
||
|
|
* @protected
|
||
|
|
*/
|
||
|
|
_preRenderBatch;
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the uniforms bound to this abstract batch renderer.
|
||
|
|
* @returns {object|undefined}
|
||
|
|
*/
|
||
|
|
get uniforms() {
|
||
|
|
return this._shader?.uniforms;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The number of reserved texture units that the shader generator should not use (maximum 4).
|
||
|
|
* @param {number} val
|
||
|
|
* @protected
|
||
|
|
*/
|
||
|
|
set reservedTextureUnits(val) {
|
||
|
|
// Some checks before...
|
||
|
|
if ( typeof val !== "number" ) {
|
||
|
|
throw new Error("BatchRenderer#reservedTextureUnits must be a number!");
|
||
|
|
}
|
||
|
|
if ( (val < 0) || (val > 4) ) {
|
||
|
|
throw new Error("BatchRenderer#reservedTextureUnits must be positive and can't exceed 4.");
|
||
|
|
}
|
||
|
|
this.#reservedTextureUnits = val;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Number of reserved texture units reserved by the batch shader that cannot be used by the batch renderer.
|
||
|
|
* @returns {number}
|
||
|
|
*/
|
||
|
|
get reservedTextureUnits() {
|
||
|
|
return this.#reservedTextureUnits;
|
||
|
|
}
|
||
|
|
|
||
|
|
#reservedTextureUnits = 0;
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/** @override */
|
||
|
|
setShaderGenerator({
|
||
|
|
vertex=this.constructor.defaultVertexSrc,
|
||
|
|
fragment=this.constructor.defaultFragmentTemplate,
|
||
|
|
uniforms=this.constructor.defaultUniforms
|
||
|
|
}={}) {
|
||
|
|
this.shaderGenerator = new this.constructor.shaderGeneratorClass(vertex, fragment, uniforms);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This override allows to allocate a given number of texture units reserved for a custom batched shader.
|
||
|
|
* These reserved texture units won't be used to batch textures for PIXI.Sprite or SpriteMesh.
|
||
|
|
* @override
|
||
|
|
*/
|
||
|
|
contextChange() {
|
||
|
|
const gl = this.renderer.gl;
|
||
|
|
|
||
|
|
// First handle legacy environment
|
||
|
|
if ( PIXI.settings.PREFER_ENV === PIXI.ENV.WEBGL_LEGACY ) this.maxTextures = 1;
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// Step 1: first check max texture units the GPU can handle
|
||
|
|
const gpuMaxTex = Math.min(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), 65536);
|
||
|
|
|
||
|
|
// Step 2: Remove the number of reserved texture units that could be used by a custom batch shader
|
||
|
|
const batchMaxTex = gpuMaxTex - this.#reservedTextureUnits;
|
||
|
|
|
||
|
|
// Step 3: Checking if remainder of texture units is at least 1. Should never happens on GPU < than 20 years old!
|
||
|
|
if ( batchMaxTex < 1 ) {
|
||
|
|
const msg = "Impossible to allocate the required number of texture units in contextChange#BatchRenderer. "
|
||
|
|
+ "Your GPU should handle at least 8 texture units. Currently, it is supporting: "
|
||
|
|
+ `${gpuMaxTex} texture units.`;
|
||
|
|
throw new Error(msg);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 4: Check with the maximum number of textures of the setting (webGL specifications)
|
||
|
|
this.maxTextures = Math.min(batchMaxTex, PIXI.settings.SPRITE_MAX_TEXTURES);
|
||
|
|
|
||
|
|
// Step 5: Check the maximum number of if statements the shader can have too..
|
||
|
|
this.maxTextures = PIXI.checkMaxIfStatementsInShader(this.maxTextures, gl);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Generate the batched shader
|
||
|
|
this._shader = this.shaderGenerator?.generateShader(this.maxTextures) ?? null;
|
||
|
|
|
||
|
|
// Initialize packed geometries
|
||
|
|
for ( let i = 0; i < this._packedGeometryPoolSize; i++ ) {
|
||
|
|
this._packedGeometries[i] = new (this.geometryClass)();
|
||
|
|
}
|
||
|
|
this.initFlushBuffers();
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/** @inheritdoc */
|
||
|
|
onPrerender() {
|
||
|
|
if ( !this.shaderGenerator ) this.setShaderGenerator();
|
||
|
|
this._shader ??= this.shaderGenerator.generateShader(this.maxTextures);
|
||
|
|
super.onPrerender();
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/** @override */
|
||
|
|
start() {
|
||
|
|
this._preRenderBatch?.(this);
|
||
|
|
super.start();
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/** @override */
|
||
|
|
packInterleavedGeometry(element, attributeBuffer, indexBuffer, aIndex, iIndex) {
|
||
|
|
// If we have a specific function to pack data into geometry, we call it
|
||
|
|
if ( this._packInterleavedGeometry ) {
|
||
|
|
this._packInterleavedGeometry(element, attributeBuffer, indexBuffer, aIndex, iIndex);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Otherwise, we call the parent method, with the classic packing
|
||
|
|
super.packInterleavedGeometry(element, attributeBuffer, indexBuffer, aIndex, iIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Verify if a PIXI plugin exists. Check by name.
|
||
|
|
* @param {string} name The name of the pixi plugin to check.
|
||
|
|
* @returns {boolean} True if the plugin exists, false otherwise.
|
||
|
|
*/
|
||
|
|
static hasPlugin(name) {
|
||
|
|
return Object.keys(PIXI.Renderer.__plugins).some(k => k === name);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|