Repository: mrdoob/three.js (JavaScript)

Issue:
WebGL Error when using UniformsUtils.merge to merge a WebGLCubeRenderTarget texture
### Description

When using a WebGLCubeRenderTarget texture as an input to a shader, and then passing that through UniformsUtils.merge, you get the following error:

THREE.WebGLState: TypeError: Failed to execute 'texSubImage2D' on 'WebGL2RenderingContext': Overload resolution failed.
    at e.executeFunction (<anonymous>:3:467593)
    at e.executeOriginFunction (<anonymous>:3:467224)
    at WebGL2RenderingContext.texSubImage2D (<anonymous>:3:499508)
    at Object.texSubImage2D (three.module.js?module:formatted:22863:30)
    at uploadCubeTexture (three.module.js?module:formatted:24452:35)
    at WebGLTextures.setTextureCube (three.module.js?module:formatted:23693:13)
    at SingleUniform.setValueT6 [as setValue] (three.module.js?module:formatted:18496:14)
    at WebGLUniforms.upload (three.module.js?module:formatted:19113:19)
    at setProgram (three.module.js?module:formatted:29632:27)
    at WebGLRenderer.renderBufferDirect (three.module.js?module:formatted:28539:25)


From what I can see, UniformUtils.merge clones and does a copy() on the CubeTexture, which flags needsUpdate, which I'm guessing leads to a version increment and eventually the uploadCubeTexture path. Don't think this is an issue with plain WebGLRenderTarget textures though.


### Reproduction steps

1. Create a WebGLCubeRenderTarget
2. Assign the texture to a uniform
3. Use UniformUtils.merge to merge this with anything, even an empty dict {}



### Code

```js
// Goes something like this, check the jsfiddle for full source

const cubeRT = new THREE.WebGLCubeRenderTarget(128);

const brokenUniforms = THREE.UniformsUtils.merge([
    {},
    { cubemap: { value: cubeRT .texture }}]);

```


### Live example

* https://jsfiddle.net/SimonDevYT/ga69wLuy/

### Screenshots

_No response_

### Version

latest

### Device

Desktop

### Browser

Chrome

### OS

Windows


Discussion: The problem is that cloning a render target texture is an invalid operation. You have to clone the render target itself (the framebuffer) and not it's texture (the attachment). Besides, setting `needsUpdate` to `true` is also not valid for render target textures. Unlike normal (image based) textures, their data does not need to be uploaded to the GPU. 

The solution for this particular use case is to assign the texture value _after_ the uniform clone/merge operation. Meaning: https://jsfiddle.net/uc6ntLrw/
Yeah, that was the fix I had settled on to work around the issue heh. I thought it would be a good idea to file an issue about it, since this feels a bit footgunny. I worry other developers could easily sink time tracking this down.

1. What's the expected behaviour of UniformsUtils.merge with normal image-based textures? Create a full on copy?
2. Since this is more of a helper function, would it be reasonable for it to check if they're rendertargettextures, dump out an error to console?
> What's the expected behaviour of UniformsUtils.merge with normal image-based textures? Create a full on copy?

It should create a clone which means a new instance of `Texture` but with the same data source.

> Since this is more of a helper function, would it be reasonable for it to check if they're rendertargettextures, dump out an error to console?

Now that we have the `Texture.isRenderTargetTexture` flag, we could indeed implement a check in `cloneUniforms()`. Maybe it's a good idea of doing this.
> > What's the expected behaviour of UniformsUtils.merge with normal image-based textures? Create a full on copy?
> 
> It should create a clone which means a new instance of `Texture` but with the same data source.
> 

My current understanding is that the clone/copy flags a needsUpdate which eventually results in a reupload, but it feels like you want to differentiate between the source data actually changing and other properties about the texture potentially changing. Am I understanding correctly?

> > Since this is more of a helper function, would it be reasonable for it to check if they're rendertargettextures, dump out an error to console?
> 
> Now that we have the `Texture.isRenderTargetTexture` flag, we could indeed implement a check in `cloneUniforms()`. Maybe it's a good idea of doing this.

That sounds good, I can go and add that check a bit later.

Sorry for the questions, trying to be more involved in giving back a bit to Three.js after benefitting from using it for a while :)
> My current understanding is that the clone/copy flags a needsUpdate which eventually results in a reupload, but it feels like you want to differentiate between the source data actually changing and other properties about the texture potentially changing. Am I understanding correctly?

There is an issue for this topic: #14375

> That sounds good, I can go and add that check a bit later.

Great! 👍 



Fix (patch):
diff --git a/src/renderers/shaders/UniformsUtils.js b/src/renderers/shaders/UniformsUtils.js
index b20b4abd60aa32..ce1315af893b59 100644
--- a/src/renderers/shaders/UniformsUtils.js
+++ b/src/renderers/shaders/UniformsUtils.js
@@ -21,7 +21,16 @@ export function cloneUniforms( src ) {
 				property.isVector2 || property.isVector3 || property.isVector4 ||
 				property.isTexture || property.isQuaternion ) ) {
 
-				dst[ u ][ p ] = property.clone();
+				if ( property.isRenderTargetTexture ) {
+
+					console.warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' );
+					dst[ u ][ p ] = null;
+
+				} else {
+
+					dst[ u ][ p ] = property.clone();
+
+				}
 
 			} else if ( Array.isArray( property ) ) {