KhronosGroup/glTF-Validator

totalVertexCount seems misleading, based on accessors rather than on buffers

tim-rex opened this issue · 3 comments

Perhaps this is by design, but it seems that the reported totalVertexCount field is perhaps misleading...

For instance, the Brainstem.gltf model reports as follows:

        "animationCount": 1,
        "materialCount": 59,
        "hasMorphTargets": false,
        "hasSkins": true,
        "hasTextures": false,
        "hasDefaultScene": true,
        "drawCallCount": 59,
        "totalVertexCount": 34159,
        "totalTriangleCount": 61666,
        "maxUVs": 0,
        "maxInfluences": 4,
        "maxAttributes": 4

Whereas the same model that has been processed using third-party tooling (which happens to make more liberal use of accessors and bufferViews) reports:

        "animationCount": 1,
        "materialCount": 59,
        "hasMorphTargets": false,
        "hasSkins": true,
        "hasTextures": false,
        "hasDefaultScene": true,
        "drawCallCount": 59,
        "totalVertexCount": 1047958,
        "totalTriangleCount": 30832,
        "maxUVs": 0,
        "maxInfluences": 4,
        "maxAttributes": 4

Note the totalVertexCount is disproportionately large, when in actual fact the single buffer that is referenced is some 30% smaller in the second example.

I guess it is double counting where accessors are referencing the same bufferView.

Could I ask what is the third-party tool, and do you know if there's an obvious reason for its creating extra (duplicate?) accessors into the same buffer?

I think there are two ways one could approach a total vertex count:

  1. How many vertices did the original artist intend?
  2. How many vertices is the viewer going to draw?

I think it's unlikely that an engine — particularly one loading at runtime — is going to process the buffers and consolidate accessors on overlapping byte ranges. That's an optimization much better accomplished in an offline tool like gltfpack or gltf-transform. So I worry that reporting a vertex count of 34,159 here would give an unrealistic picture of the real-world performance of such a model, and that (2) is the better approach. With (1), there's an additional complication in that authoring tools like Blender allow a vertex to store multiple normals, UVs, and normals, all of which must be split for realtime engines, so the number of apparent vertices in the authoring software may have been even lower than 34K.

Apologies in advance for the long reply...

I'm using Simplygon's API to process a mesh reduction.
The original model is brainstem.gltf

An exported model (after reduction) is attached: brainstem_mesh_reduction.zip

For context.... My early rendering code was naively constructing vertex buffers based on the accessor view. For this model, where those accessors would overlap significantly, that became a bit of a problem (wasteful buffer usage, very poor performance). With this approach, the reported vertex count probably is indicative of real-world performance (it certainly hurt to render).

Once I realised what was happening, I pivoted to using the raw vertex buffers from the glTF scene directly, rather than rebuilding buffers based on the accessors. As such, I don't need to be concerned about consolidating accessors or overlapping byte ranges. Using those raw buffers directly, and configuring vertex attributes accordingly - provides the expected performance for a model that is half the size of the original

(slight aside on the use of raw buffers directly: This is my own personal renderer, I don't have a preferred vertex or buffer format - though the tools you've mentioned will one day form part of my asset pipeline, I'm trying to be as agnostic as possible for the time being).

So, I guess the question is... what is going on with this model - and does it conform to the glTF specification? (There are other warnings emitted by the validator, though minor in the grand scheme of things).

This reduced model is defined with:
A single buffer, having a byteLength of 2,121,944
A total of 67 bufferView's referencing that buffer, with a total byteLength of 2,121,944

The bufferViews would seem to be non-overlapping.

Most bufferViews are referenced by only a single accessor, with the exception of bufferView 64/65/66 which are referenced by a number of accessors.

# quick and dirty check
grep '"bufferView":' BrainStem.gltf  | sort | uniq -c
...
     19             "bufferView": 64,
     38             "bufferView": 65,
     19             "bufferView": 66,
...

It does not appear that any accessors are overlapping... while they might reference the same bufferView they appear to have different byteOffset and counts.

In terms of how many vertices are drawn.. The total index count passed to glDrawElements is:
Original scene mesh: 10,914,882
Reduced scene mesh: 5,456,910

Inspecting the Simplygon output, the first few mesh primitives are:

{
    "attributes": {
        "NORMAL": 77,
        "POSITION": 76,
        "JOINTS_0": 78,
        "WEIGHTS_0": 79
    },
    "indices": 80,
    "material": 0
},
{
    "attributes": {
        "POSITION": 76,
        "NORMAL": 77,
        "JOINTS_0": 78,
        "WEIGHTS_0": 79
    },
    "indices": 81,
    "material": 1
},
{
    "attributes": {
        "NORMAL": 77,
        "POSITION": 76,
        "JOINTS_0": 78,
        "WEIGHTS_0": 79
    },
    "indices": 82,
    "material": 2
},

Note that each mesh has the same vertex attribute accessors, but different indices and different materials. That's very reasonable, and I don't see anything non-conformant here (aside from the ANIMATION_CHANNEL_TARGET_NODE_MATRIX validator warning, unrelated).

I'm not sure how glTF-Validator reports the vertex count in this situation, I guess I could see either approach (report # vertices in the buffer, or # vertex draws referenced by indices) being useful in different contexts.