Skip to content

Commit

Permalink
support updating only dirty CompressedArrayTexture layers (#27972)
Browse files Browse the repository at this point in the history
* support CompressedArrayTexture

* docs

* address feedback

* s/length/size

* data array texture

* precompute texel size

* fix lint

* fix missing break statements

* s/dirty/update
  • Loading branch information
HunterLarco committed May 4, 2024
1 parent 83a058b commit b98f98a
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 2 deletions.
21 changes: 21 additions & 0 deletions docs/api/en/textures/CompressedArrayTexture.html
Expand Up @@ -61,11 +61,32 @@ <h3>[property:number wrapR]</h3>
<h3>[property:Object image]</h3>
<p>Overridden with a object containing width, height, and depth.</p>

<h3>[property:Set layerUpdates]</h3>
<p>
A set of all layers which need to be updated in the texture. See
[Page:CompressedTextureArray.addLayerUpdate addLayerUpdate].
</p>

<h3>[property:Boolean isCompressedArrayTexture]</h3>
<p>Read-only flag to check if a given object is of type [name].</p>

<h2>Methods</h2>

<h3>[method:addLayerUpdate addLayerUpdate]( layerIndex )</h3>
<p>
Describes that a specific layer of the texture needs to be updated.
Normally when [page:Texture.needsUpdate needsUpdate] is set to true, the
entire compressed texture array is sent to the GPU. Marking specific
layers will only transmit subsets of all mipmaps associated with a
specific depth in the array which is often much more performant.
</p>

<h3>[method:clearLayerUpdates clearLayerUpdates]()</h3>
<p>
Resets the layer updates registry. See
[Page:CompressedTextureArray.addLayerUpdate addLayerUpdate].
</p>

<p>
See the base [page:CompressedTexture CompressedTexture] class for common
methods.
Expand Down
22 changes: 22 additions & 0 deletions docs/api/en/textures/DataArrayTexture.html
Expand Up @@ -143,8 +143,30 @@ <h3>[property:number wrapR]</h3>
page for details.
</p>

<h3>[property:Set layerUpdates]</h3>
<p>
A set of all layers which need to be updated in the texture. See
[Page:DataArrayTexture.addLayerUpdate addLayerUpdate].
</p>

<h2>Methods</h2>

<h3>[method:addLayerUpdate addLayerUpdate]( layerIndex )</h3>
<p>
Describes that a specific layer of the texture needs to be updated.
Normally when [page:Texture.needsUpdate needsUpdate] is set to true, the
entire compressed texture array is sent to the GPU. Marking specific
layers will only transmit subsets of all mipmaps associated with a
specific depth in the array which is often much more performant.
</p>

<h3>[method:clearLayerUpdates clearLayerUpdates]()</h3>
<p>
Resets the layer updates registry. See
[Page:DataArrayTexture.addLayerUpdate addLayerUpdate].
</p>


<p>See the base [page:Texture Texture] class for common methods.</p>

<h2>Source</h2>
Expand Down
84 changes: 82 additions & 2 deletions src/renderers/webgl/WebGLTextures.js
Expand Up @@ -826,7 +826,22 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,

if ( dataReady ) {

state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 );
if ( texture.layerUpdates.size > 0 ) {

for ( const layerIndex of texture.layerUpdates ) {

const layerSize = mipmap.width * mipmap.height;
state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, layerIndex, mipmap.width, mipmap.height, 1, glFormat, mipmap.data.slice( layerSize * layerIndex, layerSize * ( layerIndex + 1 ) ), 0, 0 );

}

texture.clearLayerUpdates();

} else {

state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 );

}

}

Expand Down Expand Up @@ -932,7 +947,72 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,

if ( dataReady ) {

state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
if ( texture.layerUpdates.size > 0 ) {

// When type is GL_UNSIGNED_BYTE, each of these bytes is
// interpreted as one color component, depending on format. When
// type is one of GL_UNSIGNED_SHORT_5_6_5,
// GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1, each
// unsigned value is interpreted as containing all the components
// for a single pixel, with the color components arranged
// according to format.
//
// See https://registry.khronos.org/OpenGL-Refpages/es1.1/xhtml/glTexImage2D.xml
let texelSize;
switch ( glType ) {

case _gl.UNSIGNED_BYTE:
switch ( glFormat ) {

case _gl.ALPHA:
texelSize = 1;
break;
case _gl.LUMINANCE:
texelSize = 1;
break;
case _gl.LUMINANCE_ALPHA:
texelSize = 2;
break;
case _gl.RGB:
texelSize = 3;
break;
case _gl.RGBA:
texelSize = 4;
break;

default:
throw new Error( `Unknown texel size for format ${glFormat}.` );

}

break;

case _gl.UNSIGNED_SHORT_4_4_4_4:
case _gl.UNSIGNED_SHORT_5_5_5_1:
case _gl.UNSIGNED_SHORT_5_6_5:
texelSize = 1;
break;

default:
throw new Error( `Unknown texel size for type ${glType}.` );

}

const layerSize = image.width * image.height * texelSize;

for ( const layerIndex of texture.layerUpdates ) {

state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, layerIndex, image.width, image.height, 1, glFormat, glType, image.data.slice( layerSize * layerIndex, layerSize * ( layerIndex + 1 ) ) );

}

texture.clearLayerUpdates();

} else {

state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );

}

}

Expand Down
14 changes: 14 additions & 0 deletions src/textures/CompressedArrayTexture.js
Expand Up @@ -11,6 +11,20 @@ class CompressedArrayTexture extends CompressedTexture {
this.image.depth = depth;
this.wrapR = ClampToEdgeWrapping;

this.layerUpdates = new Set();

}

addLayerUpdates( layerIndex ) {

this.layerUpdates.add( layerIndex );

}

clearLayerUpdates() {

this.layerUpdates.clear();

}

}
Expand Down
14 changes: 14 additions & 0 deletions src/textures/DataArrayTexture.js
Expand Up @@ -20,6 +20,20 @@ class DataArrayTexture extends Texture {
this.flipY = false;
this.unpackAlignment = 1;

this.layerUpdates = new Set();

}

addLayerUpdate( layerIndex ) {

this.layerUpdates.add( layerIndex );

}

clearLayerUpdates() {

this.layerUpdates.clear();

}

}
Expand Down

0 comments on commit b98f98a

Please sign in to comment.