Estimate glTF VRAM from JSON Manifests
Estimating the Video RAM (VRAM) footprint of a 3D asset directly from a glTF JSON manifest involves calculating the memory required for vertex attributes, index buffers, morph targets, and textures once uploaded to the GPU. Because the glTF manifest explicitly defines data structures, component types, and buffer allocations, deterministic parsing algorithms can evaluate these metrics prior to asset loading. This guide details the core algorithms used to parse the glTF JSON structure and compute an accurate projection of runtime graphics memory consumption.
1. Vertex Buffer Memory (Attribute Parsing Algorithm)
To calculate geometry memory, the parser traverses either the
bufferViews array directly or reconstructs memory footprint
through the accessors and meshes
properties.
If the glTF file explicitly declares GPU buffer binding targets, the
algorithm processes the bufferViews array:
\[\text{VRAM}_{\text{BufferViews}} = \sum_{i=0}^{n} \text{bufferView}[i]\text{.byteLength} \quad \text{where target} \in \{34962, 34963\}\]
Target 34962 represents ARRAY_BUFFER
(vertex attributes) and 34963 represents
ELEMENT_ARRAY_BUFFER (indices).
If binding targets are omitted (common in raw glTF files), the
algorithm evaluates every primitive's attributes through
accessors:
- Iterate over every mesh in
meshes[]and primitive inmesh.primitives[]. - For each attribute key (e.g.,
POSITION,NORMAL,TEXCOORD_0):- Look up the accessor via the corresponding integer ID.
- Determine the component byte size \(S_c\) from
componentType:5120(BYTE): 1 byte5121(UNSIGNED_BYTE): 1 byte5122(SHORT): 2 bytes5123(UNSIGNED_SHORT): 2 bytes5125(UNSIGNED_INT): 4 bytes5126(FLOAT): 4 bytes
- Determine the component count \(N_c\) from
type:SCALAR: 1,VEC2: 2,VEC3: 3,VEC4: 4,MAT4: 16.
- Calculate byte length: \[\text{Attribute Bytes} = \text{accessor.count} \times S_c \times N_c\]
- Pad the size of each vertex attribute to a multiple of 4 bytes to match standard GPU alignment constraints.
2. Index Buffer Memory Algorithm
Primitives containing an indices property reference an
accessor for element arrays.
- Fetch the index accessor using the ID defined in
primitive.indices. - Compute index allocation: \[\text{VRAM}_{\text{Indices}} =
\text{accessor.count} \times S_c\] (Where \(S_c\) is typically 1 for
UNSIGNED_BYTE, 2 forUNSIGNED_SHORT, or 4 forUNSIGNED_INT). - If memory alignment requires 4-byte boundaries, align \(\text{VRAM}_{\text{Indices}}\) up to the nearest multiple of 4: \[\text{Aligned Bytes} = \lceil \frac{\text{VRAM}_{\text{Indices}}}{4} \rceil \times 4\]
3. Morph Target and Skinning Extensions
Morph targets and skinning add separate vertex buffer objects (VBOs) in graphics memory:
- Morph Targets: Iterate through
primitive.targets[]. For each displacement attribute (POSITION,NORMAL,TANGENT), run the attribute algorithm and sum the byte size. Sparse accessors (accessor.sparse) must be accounted for by calculating: \[\text{Sparse Bytes} = \text{sparse.count} \times S_c \times N_c + \text{Index Bytes}\] - Skinning Data: Joints (
JOINTS_0) and weights (WEIGHTS_0) exist as standard vertex attributes and are included during the general attribute parsing stage.
4. Texture Memory Estimation Algorithm
Textures consume the largest portion of VRAM. Standard glTF JSON
files contain arrays for textures, samplers,
and images. While the JSON points to image URIs or buffer
views, it rarely stores pixel dimensions directly unless explicit vendor
extensions are present.
Handling Base Dimension Metadata
When dimensions (\(W, H\)) are
provided via extensions (such as KHR_texture_basisu) or
retrieved via image header sniffing:
- Determine base uncompressed format (commonly 4 bytes per pixel for 8-bit RGBA).
- Compute base surface size: \[\text{Surface Bytes} = W \times H \times \text{BytesPerPixel}\]
- Account for mipmaps (standard complete chain adds approximately 33.3% memory): \[\text{VRAM}_{\text{Texture}} = \text{Surface Bytes} \times 1.333\]
Block-Compressed Textures (KTX2 / Basis Universal)
When the manifest references compressed GPU texture extensions
(KHR_texture_basisu), calculate memory using compressed
block sizes instead of uncompressed pixel counts:
- BC1 / DXT1 / ETC2 RGB: 8 bytes per \(4 \times 4\) pixel block.
- BC3 / BC7 / ASTC 4x4 / ETC2 RGBA: 16 bytes per \(4 \times 4\) pixel block.
Algorithm for block compression: \[\text{Blocks}_X = \max(1, \lceil \frac{W}{4} \rceil), \quad \text{Blocks}_Y = \max(1, \lceil \frac{H}{4} \rceil)\] \[\text{VRAM}_{\text{Mip0}} = \text{Blocks}_X \times \text{Blocks}_Y \times \text{BlockSize}\] \[\text{VRAM}_{\text{Total}} = \text{VRAM}_{\text{Mip0}} \times 1.333\]
5. Unified Estimation Formula
To arrive at the total estimated VRAM, deduplicate shared buffer
references (multiple accessors pointing to the same
bufferView) and combine the evaluated sub-systems:
\[\text{Total VRAM} = \sum \text{VRAM}_{\text{Unique BufferViews}} + \sum \text{VRAM}_{\text{Textures}} + \text{Overhead}\]
A typical driver-overhead margin of 5% to 10% is added to account for descriptor sets, vertex array objects (VAOs), frame-allocation padding, and staging buffers created by APIs such as Vulkan, DirectX 12, or WebGPU.