Files
Foundry-VTT-Docker/resources/app/client/pixi/webgl/extensions/batch-renderer.js

175 lines
5.5 KiB
JavaScript
Raw Normal View History

2025-01-04 00:34:03 +01:00
/**
* 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);
}
}