{"record":{"id":"5c42f66268946235","repo":"BabylonJS/Babylon.js","slug":"writestreamingsplats-splatsdata-has-ubuffer-le","errorCode":null,"errorMessage":"_writeStreamingSplats: splatsData has ${uBuffer.length} bytes, need ${count * 32} for ${count} splats (stride 32)","messagePattern":"_writeStreamingSplats: splatsData has (.+?) bytes, need (.+?) for (.+?) splats \\(stride 32\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.ts","lineNumber":3171,"sourceCode":"     * @param count number of splats to write\n     * @param splatsData raw `.splat` bytes for `count` splats (stride 32)\n     * @param min optional running min accumulator for the written centers\n     * @param max optional running max accumulator for the written centers\n     */\n    protected _writeStreamingSplats(globalOffset: number, count: number, splatsData: ArrayBuffer | ArrayBufferView, min?: Vector3, max?: Vector3): void {\n        if (!this._splatPositions) {\n            return;\n        }\n        // Validate the range against the atlas and the input length (independent of GPU state): an out-of-range\n        // offset/count would write over another part's texels or allocate a huge transient (the CPU arrays are\n        // atlas-indexed up to `end`); a short input would read past its end.\n        const capacity = this._splatPositions.length / 4;\n        const uBuffer = GaussianSplattingMeshBase._GetSplatDataBytes(splatsData);\n        if (!Number.isInteger(globalOffset) || !Number.isInteger(count) || globalOffset < 0 || count < 0 || globalOffset + count > capacity) {\n            throw new Error(`_writeStreamingSplats: range [${globalOffset}, ${globalOffset + count}) is outside the atlas bounds [0, ${capacity})`);\n        }\n        if (uBuffer.length < count * 32) {\n            throw new Error(`_writeStreamingSplats: splatsData has ${uBuffer.length} bytes, need ${count * 32} for ${count} splats (stride 32)`);\n        }\n        if (count === 0 || !this._covariancesATexture) {\n            return;\n        }\n        const textureSize = this._getTextureSize(this._vertexCount);\n        const width = textureSize.x;\n        const covBSItemSize = this._useRGBACovariants ? 4 : 2;\n        const end = globalOffset + count;\n\n        const fBuffer = GaussianSplattingMeshBase._GetSplatDataFloats(splatsData);\n\n        // Transients are sized to `count`, not `end` (globalOffset + count): sizing by atlas position would allocate\n        // tens of MB for a small write near a large atlas's tail. dstIndex still addresses the atlas _splatPositions.\n        const covA = new Uint16Array(count * 4);\n        const covB = new Uint16Array(count * covBSItemSize);\n        const colorArray = new Uint8Array(count * 4);\n        const localMin = min ?? new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);\n        const localMax = max ?? new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);","sourceCodeStart":3153,"sourceCodeEnd":3189,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.ts#L3153-L3189","documentation":"_writeStreamingSplats requires the input buffer to contain at least count * 32 bytes (the 32-byte stride per splat: position, scale, rotation, color/covariance data). It throws when splatsData is shorter than that, preventing reads past the end of the supplied buffer.","triggerScenarios":"Passing a packed subset buffer whose length is less than count*32 while claiming to write count splats; mismatched stride assumptions (e.g. 20-byte .splat rows vs 32-byte expected).","commonSituations":"Converting between splat file formats with different strides; slicing a buffer with wrong byte math (count * stride mismatch).","solutions":["Ensure splatsData.byteLength >= count * 32 before the call, slicing from a larger source buffer as needed","Reduce count to Math.floor(uBuffer.length / 32) if the buffer is the limiting factor","Verify the source data stride is 32 bytes and re-pack it if not"],"exampleFix":"// before\nbase._writeStreamingSplats(0, count, smallBuffer);\n// after\nconst usable = Math.min(count, Math.floor(smallBuffer.byteLength / 32));\nbase._writeStreamingSplats(0, usable, smallBuffer);","handlingStrategy":"validation","validationCode":"if (splatsData.byteLength < count * 32) {\n  count = Math.floor(splatsData.byteLength / 32); // or abort\n}","typeGuard":"const hasEnoughSplats = (buf: ArrayBuffer | ArrayBufferView, count: number) =>\n  (ArrayBuffer.isView(buf) ? buf.byteLength : buf.byteLength) >= count * 32;","tryCatchPattern":"try {\n  base._writeStreamingSplats(offset, count, data);\n} catch (e) {\n  if (String(e).includes('stride 32')) { count = Math.floor(u8.byteLength / 32); retry(); }\n  else throw e;\n}","preventionTips":["Fix the per-splat stride at 32 bytes across your pipeline","Slice source buffers with count * 32 math, not assumed strides","Unit-test buffer sizing for boundary counts"],"tags":["gaussian-splatting","binary-data","validation"],"backgroundTag":"buffer-size-mismatch","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}