constructor(parent) {
    super(parent)
    this._instanceables = Object.create(null)
    this._batchGeometryPool = new BatchGeometryPool()
    this._needsRebatch = true
    this.addEventListener('beforerender', this._setupBatchObjects.bind(this))
    this.addEventListener('afterrender', this._teardownBatchObjects.bind(this))
  }
An InstancingManager handles aggregating all Instanceable3DFacade descendants into
instancing batches. For each batch it creates a clone of the instancedThreeObject,
populates a pooled InstancedBufferGeometry with buffer attributes holding the world
matrices of all the batch's instances, and temporarily inserts that into the
scene to be rendered.

As an additional "turbo" optimization, the instancing batch objects/geometries will be
reused untouched between rendering frames if none of the managed Instanceable3DFacade
objects have changed in a way that would affect the batches or world matrix attributes.

There is a global InstancingManager automatically added to the main scene, and it does
nothing if there are no Instanceable3DFacades in the scene, so in most cases you should
not need to touch this yourself. But it is also possible to insert additional
InstancingManager facades further down in the scene if you wish to control the scope
of instancing, e.g. to increase the likelihood of the aforementioned "turbo" optimization
kicking in.

Also see InstancingShaderUpgrades, which modifies material shaders so they accept the matrix
and custom uniform values coming in as attributes. This allows built-in materials as well
as custom shaders to work with instancing without manual modification.