Texture2Drd nested in ShaderMaterial keeps completely white once an empty RID was assigned to it
Tested versions
Godot v4.2.2
System information
Godot v4.2.2.stable.mono - Windows 10.0.22631 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4070 (NVIDIA; 31.0.15.3161) - AMD Ryzen 9 7900X 12-Core Processor (24 Threads)
Issue description
For a Texture2Drd used as a shader uniform parameter (already settled in editor and saved in scene), the Texture2Drd keeps completed white once assigned an empty RID even if a valid RID is re-assigned to the Texture2Drd.
And once the Texture2Drd turns to white, it never recovers to correct state even the scene is reloaded (not demonstrated in MRP). So it seems to be a Resource serialization bug?
Replace the uniform parameter with a new Texture2Drd created by code can resolve it.
Steps to reproduce
- Create a ShaderMaterial with a sampler2d uniform parameter.
- Bind a Texture2Drd to the uniform parameter in editor inspector.
- At runtime, assign an empty RID to this Texture2Drd.
- Then whatever you re-assign to the Texture2Drd, this Texture2Drd keeps completely white even the full scene is reloaded.
Minimal reproduction project (MRP)
In the MRP, the upper two ColorRects should be in red, and lower two should be blue. However, the red ones assigned an empty RID before the red texture is assigned, and they keep completed white in the rest time.
I can reproduce it in 4.4[f42e612da286b14b3a50eff7935615b3bc132c37]. This is MRP I cleanded without C# test_texture2drd_1.zip
I think this is as expected, when assigning an empty RID, the Internal texture rid inside Texture2DRD is released and recreated rather than updated by RS::texture_replace, so you should reassign the shader parameter like this:
should_red_texture.texture_rd_rid = RID()
should_red_texture.texture_rd_rid = create_texture(Color.RED)
# ---> reseting the shader parameter fixes the issue
$ColorRect.material.set_shader_parameter("_texture", should_red_texture)
Edit: but this issue doesn't happen in Texture2DRD as Sprite2D's texture, I'am not sure if the issue is intended or should be fixed. may related to
- #85989
As per my comment on this related issue here.
I think is is a bug and not working as intended, since on a Texture2DRD you can update the RID to a new valid RID and it all keeps working (even if the new RID has a totally different size), but if you set it to an empty it invalidates the texture such that any reference to it also becomes invalid.
I think the result of the issue is ShaderMaterial does not connect its shader parameter's changed signal, so change the internal rid of texture won't update the material.
For other textures, the rid will not changes, so it's okay even if the changed signal is not connected, but textureXXRD is different.
We can remain this behavior, or specially connect their changed signal.
I'm not sure that is the whole story - since I can change the RID on the Texture2DRD and the material is fine - it only breaks when changing to a RID() (value of 0).
I'm not sure that is the whole story - since I can change the RID on the Texture2DRD and the material is fine - it only breaks when changing to a RID() (value of 0).
Changing texture_rd_rid to valid rid doesn't change the Texture2DRD's rid, but changing it to empty rid does.
I see what you are saying @beicause - I think the solution should include to not touch the texture_rid when setting texture_rd_rid at all, that way the no shader needs to look for changes. Rather if setting an invalid texture_rd_rid then it should set texture_rd_rid to an RD RID for a placeholder texture, or even just set it to RID() allow it to show as in invalid/missing texture.
This way the shader reference to the resource remains valid.