@@ -739,6 +739,115 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
739
739
740
740
}
741
741
742
+ function getRow ( index , rowLength , componentStride ) {
743
+
744
+ return Math . floor ( Math . floor ( index / componentStride ) / rowLength ) ;
745
+
746
+ }
747
+
748
+ function updateTexture ( texture , image , glFormat , glType ) {
749
+
750
+ const componentStride = 4 ; // only RGBA supported
751
+
752
+ const updateRanges = texture . updateRanges ;
753
+
754
+ if ( updateRanges . length === 0 ) {
755
+
756
+ state . texSubImage2D ( _gl . TEXTURE_2D , 0 , 0 , 0 , image . width , image . height , glFormat , glType , image . data ) ;
757
+
758
+ } else {
759
+
760
+ // Before applying update ranges, we merge any adjacent / overlapping
761
+ // ranges to reduce load on `gl.texSubImage2D`. Empirically, this has led
762
+ // to performance improvements for applications which make heavy use of
763
+ // update ranges. Likely due to GPU command overhead.
764
+ //
765
+ // Note that to reduce garbage collection between frames, we merge the
766
+ // update ranges in-place. This is safe because this method will clear the
767
+ // update ranges once updated.
768
+
769
+ updateRanges . sort ( ( a , b ) => a . start - b . start ) ;
770
+
771
+ // To merge the update ranges in-place, we work from left to right in the
772
+ // existing updateRanges array, merging ranges. This may result in a final
773
+ // array which is smaller than the original. This index tracks the last
774
+ // index representing a merged range, any data after this index can be
775
+ // trimmed once the merge algorithm is completed.
776
+ let mergeIndex = 0 ;
777
+
778
+ for ( let i = 1 ; i < updateRanges . length ; i ++ ) {
779
+
780
+ const previousRange = updateRanges [ mergeIndex ] ;
781
+ const range = updateRanges [ i ] ;
782
+
783
+ // Only merge if in the same row and overlapping/adjacent
784
+ const previousEnd = previousRange . start + previousRange . count ;
785
+ const currentRow = getRow ( range . start , image . width , componentStride ) ;
786
+ const previousRow = getRow ( previousRange . start , image . width , componentStride ) ;
787
+
788
+ // We add one here to merge adjacent ranges. This is safe because ranges
789
+ // operate over positive integers.
790
+ if (
791
+ range . start <= previousEnd + 1 &&
792
+ currentRow === previousRow &&
793
+ getRow ( range . start + range . count - 1 , image . width , componentStride ) === currentRow // ensure range doesn't spill
794
+ ) {
795
+
796
+ previousRange . count = Math . max (
797
+ previousRange . count ,
798
+ range . start + range . count - previousRange . start
799
+ ) ;
800
+
801
+ } else {
802
+
803
+ ++ mergeIndex ;
804
+ updateRanges [ mergeIndex ] = range ;
805
+
806
+ }
807
+
808
+
809
+ }
810
+
811
+ // Trim the array to only contain the merged ranges.
812
+ updateRanges . length = mergeIndex + 1 ;
813
+
814
+ const currentUnpackRowLen = _gl . getParameter ( _gl . UNPACK_ROW_LENGTH ) ;
815
+ const currentUnpackSkipPixels = _gl . getParameter ( _gl . UNPACK_SKIP_PIXELS ) ;
816
+ const currentUnpackSkipRows = _gl . getParameter ( _gl . UNPACK_SKIP_ROWS ) ;
817
+
818
+ _gl . pixelStorei ( _gl . UNPACK_ROW_LENGTH , image . width ) ;
819
+
820
+ for ( let i = 0 , l = updateRanges . length ; i < l ; i ++ ) {
821
+
822
+ const range = updateRanges [ i ] ;
823
+
824
+ const pixelStart = Math . floor ( range . start / componentStride ) ;
825
+ const pixelCount = Math . ceil ( range . count / componentStride ) ;
826
+
827
+ const x = pixelStart % image . width ;
828
+ const y = Math . floor ( pixelStart / image . width ) ;
829
+
830
+ // Assumes update ranges refer to contiguous memory
831
+ const width = pixelCount ;
832
+ const height = 1 ;
833
+
834
+ _gl . pixelStorei ( _gl . UNPACK_SKIP_PIXELS , x ) ;
835
+ _gl . pixelStorei ( _gl . UNPACK_SKIP_ROWS , y ) ;
836
+
837
+ state . texSubImage2D ( _gl . TEXTURE_2D , 0 , x , y , width , height , glFormat , glType , image . data ) ;
838
+
839
+ }
840
+
841
+ texture . clearUpdateRanges ( ) ;
842
+
843
+ _gl . pixelStorei ( _gl . UNPACK_ROW_LENGTH , currentUnpackRowLen ) ;
844
+ _gl . pixelStorei ( _gl . UNPACK_SKIP_PIXELS , currentUnpackSkipPixels ) ;
845
+ _gl . pixelStorei ( _gl . UNPACK_SKIP_ROWS , currentUnpackSkipRows ) ;
846
+
847
+ }
848
+
849
+ }
850
+
742
851
function uploadTexture ( textureProperties , texture , slot ) {
743
852
744
853
let textureType = _gl . TEXTURE_2D ;
@@ -852,7 +961,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
852
961
853
962
if ( dataReady ) {
854
963
855
- state . texSubImage2D ( _gl . TEXTURE_2D , 0 , 0 , 0 , image . width , image . height , glFormat , glType , image . data ) ;
964
+ updateTexture ( texture , image , glFormat , glType ) ;
856
965
857
966
}
858
967
0 commit comments