updateTexture method
Implementation
void updateTexture(Texture texture, ImageElement image, int glFormat, int glType ) {
int componentStride = 4; // only RGBA supported
final updateRanges = texture.updateRanges;
if ( updateRanges.length == 0 ) {
state.texSubImage2D( WebGL.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data );
}
else {
// Before applying update ranges, we merge any adjacent / overlapping
// ranges to reduce load on `gl.texSubImage2D`. 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.
int mergeIndex = 0;
for (int i = 1; i < updateRanges.length; i ++ ) {
final previousRange = updateRanges[ mergeIndex ];
final range = updateRanges[ i ];
// Only merge if in the same row and overlapping/adjacent
final previousEnd = previousRange.start + previousRange.count;
final currentRow = getRow( range.start, image.width, componentStride );
final previousRow = getRow( previousRange.start, image.width, componentStride );
// We add one here to merge adjacent ranges. This is safe because ranges
// operate over positive integers.
if (
range.start <= previousEnd + 1 &&
currentRow == previousRow &&
getRow( range.start + range.count - 1, image.width, componentStride ) == currentRow // ensure range doesn't spill
) {
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;
final currentUnpackRowLen = _gl.getParameter( WebGL.UNPACK_ROW_LENGTH );
final currentUnpackSkipPixels = _gl.getParameter( WebGL.UNPACK_SKIP_PIXELS );
final currentUnpackSkipRows = _gl.getParameter( WebGL.UNPACK_SKIP_ROWS );
_gl.pixelStorei( WebGL.UNPACK_ROW_LENGTH, image.width.toInt() );
for (int i = 0, l = updateRanges.length; i < l; i ++ ) {
final range = updateRanges[ i ];
final pixelStart = ( range.start / componentStride ).floor();
final pixelCount = ( range.count / componentStride ).ceil();
final x = (pixelStart % image.width).toInt();
final y = ( pixelStart / image.width ).floor();
// Assumes update ranges refer to contiguous memory
final width = pixelCount;
final height = 1;
_gl.pixelStorei( WebGL.UNPACK_SKIP_PIXELS, x );
_gl.pixelStorei( WebGL.UNPACK_SKIP_ROWS, y );
state.texSubImage2D( WebGL.TEXTURE_2D, 0, x, y, width, height, glFormat, glType, image.data );
}
texture.clearUpdateRanges();
_gl.pixelStorei( WebGL.UNPACK_ROW_LENGTH, currentUnpackRowLen );
_gl.pixelStorei( WebGL.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
_gl.pixelStorei( WebGL.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
}
}