From 08626b443924e5896ec9ba352e093044e80d1023 Mon Sep 17 00:00:00 2001 From: Hunter Larco Date: Mon, 2 Sep 2024 04:26:33 -0400 Subject: [PATCH] [performance] merge WebGL BufferAttribute update ranges before issuing updates to the GPU (#29189) * sort update ranges before applying * self review --- src/renderers/webgl/WebGLAttributes.js | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/renderers/webgl/WebGLAttributes.js b/src/renderers/webgl/WebGLAttributes.js index aee0be82a1955f..19893fb844e845 100644 --- a/src/renderers/webgl/WebGLAttributes.js +++ b/src/renderers/webgl/WebGLAttributes.js @@ -90,6 +90,50 @@ function WebGLAttributes( gl ) { if ( updateRanges.length !== 0 ) { + // Before applying update ranges, we merge any adjacent / overlapping + // ranges to reduce load on `gl.bufferSubData`. Empirically, this has led + // to performance improvements for applications which make heavy use of + // update ranges. Likely due to GPU command overhead. + // + // Note that to reduce garbage collection between frames, we merge the + // update ranges in-place. This is safe because this method will clear the + // update ranges once updated. + + updateRanges.sort( ( a, b ) => a.start - b.start ); + + // To merge the update ranges in-place, we work from left to right in the + // existing updateRanges array, merging ranges. This may result in a final + // array which is smaller than the original. This index tracks the last + // index representing a merged range, any data after this index can be + // trimmed once the merge algorithm is completed. + let mergeIndex = 0; + + for ( let i = 1; i < updateRanges.length; i ++ ) { + + const previousRange = updateRanges[ mergeIndex ]; + const range = updateRanges[ i ]; + + // We add one here to merge adjacent ranges. This is safe because ranges + // operate over positive integers. + if ( range.start <= previousRange.start + previousRange.count + 1 ) { + + previousRange.count = Math.max( + previousRange.count, + range.start + range.count - previousRange.start + ); + + } else { + + ++ mergeIndex; + updateRanges[ mergeIndex ] = range; + + } + + } + + // Trim the array to only contain the merged ranges. + updateRanges.length = mergeIndex + 1; + for ( let i = 0, l = updateRanges.length; i < l; i ++ ) { const range = updateRanges[ i ];