`numOfMeshes === 1` check in Geometry._applyToMesh

In Geometry._applyToMesh, there are numOfMeshes === 1 checks, which uploads data to gpu if the geometry has exactly 1 mesh.
_applyToMesh is used in 2 places, applyToMesh, where meshes are added one by one, it’s ok to use this check to upload for first mesh, another is _queueLoad, where _applyToMesh is called for each mesh bound to this geometry. If the gpu buffers are not correctly created and uploaded in _delayLoadingFunction, they would never be uploaded.
A quick git blame shows that this check origins about 11 years ago.
So, here is the question, why not just check for existance, or if possible, validity of the gpu buffer, instead of numOfMeshes === 1 checks?

Some pseudocode to make it more clear
    private _applyToMesh(mesh: Mesh): void {
        const numOfMeshes = this._meshes.length;

        // vertexBuffers
        for (const kind in this._vertexBuffers) {
            const buf = this._vertexBuffers[kind];
            if (!hasUploadedAndValidGpuBuffer(buf)) {
                this._vertexBuffers[kind].create();
            }

            if (kind === VertexBuffer.PositionKind) {
                if (!this._extend) {
                    this._updateExtend();
                }
                mesh.buildBoundingInfo(this._extend.minimum, this._extend.maximum);

                mesh._createGlobalSubMesh(mesh.isUnIndexed);

                //bounding info was just created again, world matrix should be applied again.
                mesh._updateBoundingInfo();
            }
        }

        // indexBuffer
        if ((!this._indexBuffer || !isValid(this._indexBuffer)) && this._indices && this._indices.length > 0) {
            this._indexBuffer = this._engine.createIndexBuffer(this._indices, this._updatable, "Geometry_" + this.id + "_IndexBuffer");
        }

        // morphTargets
        mesh._syncGeometryWithMorphTargetManager();

        // instances
        mesh.synchronizeInstances();
    }

And I have not found a method to check if a buffer if valid on gpu yet, so maybe it’s ok to just check for existance

Could you share a repro for this ? This would help finding a workaround as the code hasnt moved for a long time without facing this it seems.

Sure, here is a playground using delay loaded geometry with 2 meshes, which is not loaded:

Remove one of the 2 meshes, and it’s loaded:

Can you share where this pattern is used in our code or why are you using it at the moment ?

I am trying to see if we could fix usage site instead.

I was trying to accelerate loading of a huge scene, and profiling shows _ImportGeometry and bufferData (uploads vertex to gpu) are taking considerable amount of cpu time, so I made a queue to throttle the load and try to delay gpu upload as much as possible.

The playgrounds only shows a minimal reproducation of this use case, including creating the geometry in advance, set indices (which won’t invoke a gpu upload), bind to meshes, and create vertex buffers later. But if the geometry is bound to 1 mesh, everything is ok, but with 2, gpu upload would never happen.
The actual code I used overwrites geometry._queueLoad to make a delayed and throttled creation of vertex buffers, but i do not think this changes the behavior of gpu upload.

you could add a flag loaded and only upload the first time (the check would be on the flag instead of num mesh) do you want to do a PR ?

I don’t think an extra flag needed in this case, after some futher looking with the code, the VertexBuffer.create(), which calls Buffer.create(), checks for existance of this._buffer, so it should be safe to just check for it.
A patch like this should just work.

diff --git a/packages/dev/core/src/Meshes/geometry.ts b/packages/dev/core/src/Meshes/geometry.ts
index 9bbc327d7d..c06802414f 100644
--- a/packages/dev/core/src/Meshes/geometry.ts
+++ b/packages/dev/core/src/Meshes/geometry.ts
@@ -784,12 +784,11 @@ export class Geometry implements IGetSetVerticesData {
     }
 
     private _applyToMesh(mesh: Mesh): void {
-        const numOfMeshes = this._meshes.length;
-
         // vertexBuffers
         for (const kind in this._vertexBuffers) {
-            if (numOfMeshes === 1) {
-                this._vertexBuffers[kind].create();
+            const vertexBuffer = this._vertexBuffers[kind];
+            if (!vertexBuffer._buffer.getBuffer()) {
+                vertexBuffer.create();
             }
 
             if (kind === VertexBuffer.PositionKind) {
@@ -806,7 +805,7 @@ export class Geometry implements IGetSetVerticesData {
         }
 
         // indexBuffer
-        if (numOfMeshes === 1 && this._indices && this._indices.length > 0) {
+        if (!this._indexBuffer && this._indices && this._indices.length > 0) {
             this._indexBuffer = this._engine.createIndexBuffer(this._indices, this._updatable, "Geometry_" + this.id + "_IndexBuffer");
         }
 

Also, in case of context lost, _rebuild() will ignore existing DataBuffers, so it’s not affected by this.

sounds perfect, do you want to try and make a PR ?

Seems the title is too long to fit into github commit title