{"record":{"id":"0c2fe6dcfa54ab3f","repo":"BabylonJS/Babylon.js","slug":"webgpudurationmeasure-index-out-of-range-ind","errorCode":null,"errorMessage":"WebGPUDurationMeasure: index out of range (\" + index + \")","messagePattern":"WebGPUDurationMeasure: index out of range \\(\" \\+ index \\+ \"\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Engines/WebGPU/webgpuTimestampQuery.ts","lineNumber":131,"sourceCode":"\r\n    constructor(engine: WebGPUEngine, device: GPUDevice, bufferManager: WebGPUBufferManager, count = 2, querySetLabel?: string) {\r\n        this._count = count;\r\n        this._querySet = new WebGPUQuerySet(engine, count, WebGPUConstants.QueryType.Timestamp, device, bufferManager, true, querySetLabel);\r\n    }\r\n\r\n    public start(encoder: GPUCommandEncoder): void {\r\n        (encoder as GPUCommandEncoderWithTimestamp).writeTimestamp?.(this._querySet.querySet, 0);\r\n    }\r\n\r\n    public async stop(encoder: GPUCommandEncoder): Promise<number | null> {\r\n        (encoder as GPUCommandEncoderWithTimestamp).writeTimestamp?.(this._querySet.querySet, 1);\r\n\r\n        return (encoder as GPUCommandEncoderWithTimestamp).writeTimestamp ? await this._querySet.readTwoValuesAndSubtract(0) : 0;\r\n    }\r\n\r\n    public startPass(descriptor: GPURenderPassDescriptor | GPUComputePassDescriptor, index: number): void {\r\n        if (index + 3 > this._count) {\r\n            throw new Error(\"WebGPUDurationMeasure: index out of range (\" + index + \")\");\r\n        }\r\n\r\n        descriptor.timestampWrites = {\r\n            querySet: this._querySet.querySet,\r\n            beginningOfPassWriteIndex: index + 2,\r\n            endOfPassWriteIndex: index + 3,\r\n        };\r\n    }\r\n\r\n    public async stopPass(index: number): Promise<number | null> {\r\n        return await this._querySet.readTwoValuesAndSubtract(index + 2);\r\n    }\r\n\r\n    public dispose() {\r\n        this._querySet.dispose();\r\n    }\r\n}\r\n","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Engines/WebGPU/webgpuTimestampQuery.ts#L113-L149","documentation":"WebGPUDurationMeasure (timestamp query helper) allocates a query set with a fixed count (this._count). startPass writes two timestamps at index+2 and index+3, so if index+3 exceeds the allocated count the writes would fall outside the query set. Babylon throws to prevent corrupting the GPU query set.","triggerScenarios":"Passing an index too large to WebGPUDurationMeasure.startPass (or start/end pairs) such that index+3 >= count, e.g. measuring more passes than the duration measure was created for (initTimeStampRegisters/count).","commonSituations":"Performance instrumentation loops that call startPass/endPass for every draw pass but initialized the duration measure with too few registers; copy-pasted measurement code after increasing the number of passes.","solutions":["Initialize the duration measure with enough slots: call initTimeStampRegisters / create the measure with a count >= 2*(number of passes)+2","Use distinct, incrementing indices per pass and verify the maximum index before calling","Clamp or reuse measurement slots instead of allocating a new index per pass"],"exampleFix":"// before\ndurationMeasure = new WebGPUDurationMeasure(engine, 4);\ndurationMeasure.startPass(descriptor, 3); // 3+3 > 4\n// after\ndurationMeasure = new WebGPUDurationMeasure(engine, 8); // at least index+4\ndurationMeasure.startPass(descriptor, 3);","handlingStrategy":"validation","validationCode":"const maxIndex = durationMeasure.getContainerSize?.() ?? count; // slots allocated\nif (index + 4 > count) throw new RangeError(`Pass index ${index} exceeds allocated timestamp slots (${count})`);","typeGuard":"function canStartPass(dm: { _count: number }, index: number): boolean {\n  return index + 3 < dm._count;\n}","tryCatchPattern":"try {\n  durationMeasure.startPass(descriptor, i);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('WebGPUDurationMeasure: index out of range')) {\n    console.warn(`Skipping timing for pass ${i}: not enough query slots`);\n  } else throw e;\n}","preventionTips":["Allocate count >= 2 * passes + 2 when initializing the duration measure","Keep a single counter for indices and assert its maximum before rendering","Reuse timing slots rather than assigning a new index per pass","Add a unit test covering startPass at the highest expected index"],"tags":["webgpu","timestamp-query","performance","index-out-of-range"],"backgroundTag":"query-index-out-of-range","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}