Sfoglia il codice sorgente

support updating only dirty `CompressedArrayTexture` layers (#27972)

* support CompressedArrayTexture

* docs

* address feedback

* s/length/size

* data array texture

* precompute texel size

* fix lint

* fix missing break statements

* s/dirty/update
Hunter Larco 1 anno fa
parent
commit
b98f98a7b8

+ 21 - 0
docs/api/en/textures/CompressedArrayTexture.html

@@ -61,11 +61,32 @@
 		<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.

+ 22 - 0
docs/api/en/textures/DataArrayTexture.html

@@ -143,8 +143,30 @@
 			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>

+ 82 - 2
src/renderers/webgl/WebGLTextures.js

@@ -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 );
+
+										}
 
 									}
 
@@ -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 );
+
+						}
 
 					}
 

+ 14 - 0
src/textures/CompressedArrayTexture.js

@@ -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();
+
 	}
 
 }

+ 14 - 0
src/textures/DataArrayTexture.js

@@ -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();
+
 	}
 
 }