Hi, i am using an nVidia GTX 750 Ti with full DX11/12 support and unity 5.6 to 2017.3 beta6 to try using Graphics.CopyTexture to copy information from a render texture to a normal Texture2D which should be possible as the platform and the GPU seems to be capable of this.
Copying from a Texture2D to a render texture is working great just the opposite is not.
How one suppose to copy a RenderTexture information to a regular Texture2D using Gprahics.CopyTexture… ?
All the textures are created without mip maps.
Same with Texture2DArrays. Copying the Texture2DArray to the render texture array is working fine for me but i am not able to do the opposite.
There are no any errors - it is just the normal textures are purely white instead of having the appropriate color copied from the render texture!
Notes:
the render texture has color information because when i save it it is as it should.
both render texture and the Texture2D are the same ARGB32 type and the same resolution.
I just noticed that the actual CopyTexture is working. The issue though is that the texture to which the information is copied can not be just saved to an asset. I guess the texture exists in the GPU memory, but it is visible in the inspector with all the colors. Once i attempt to save it, the new saved texture is white.
Same question still - how should one have to properly save a Texture2D when Graphics.CopyTexture was used to copy info to it. I guess it should be done on the CPU side before save it to an asset !?
If you need to save an image out of a render texture, you must use ReadPixels() to copy the contents of a RenderTexture to a Texture2D. This is because the contents of a render texture only exists on the GPU, and CopyTexture() is purely a GPU side operation* if a render texture is used as either the source or destination.
If you use CopyTexture() on two non rendertexture texture assets that are both marked as readable, it will also perform a separate CPU side copy in addition to the GPU side copy.
I see. It’s weird because the copy does work, and the inspector does show the Texture2D with what was on the render texture. So it creates a strange scenario where the Texture2D that exists on the GPU is different than the one that exists in the RAM.
What renders on screen is what’s on the GPU, so the inspector is showing you the contents of the texture as they exist on the GPU as that’s all the GPU knows about. Most of the time these are in sync, so it’s not a problem; the texture on the GPU only has data because the GPU that was on disk / in RAM is what was sent to the GPU in the first place. CopyTexture() subverts this expectation as the data in the texture can be copied from essentially any arbitrary data on the GPU, including from render textures which as mentioned above the CPU has zero knowledge of.
That’s what ReadPixels() exists for though, to ask the GPU for the data so it can be transferred back to the domain of the CPU.
Basically CopyTexture is great for doing stuff that only needs to happen on the GPU as it leaves all of the work to the GPU and thus is super fast. That’s what it exists for. For most modern cases the CPU doesn’t ever need to know the contents of a texture. Indeed with the usual work flow the contents of a texture asset don’t even stay in CPU side RAM after it’s been sent to the GPU as it’s no longer needed. Texture assets are read from the disk, sent to the GPU, and then flushed from CPU RAM.
There is also the option to use the AsyncGPUReadback class. This can avoid any blocking caused by the GPU Resource Readback when leveraging the AsyncCallback and is also more flexible in terms of supported formats. I, for example, needed to copy data from an RG32 RenderTexture which is not supported by ReadPixels. You still need to call SetPixelData which probably isn’t for free but it should be less blocking overall when using the AsyncCallback to set the Texture2D Data. Haven’t done any performance comparisons though.
Edit: Removed unnecessary Graphics.CopyTexture calls (thanks for pointing that out @GeorgeAdamon )
using UnityEngine.Rendering;
//Required Textures (need to have similar formats obviously)
RenderTexture renderTexture = new RenderTexture(64, 64, 0);
Texture2D texture2D = new Texture2D(64, 64);
//Synchronously
var asyncAction = AsyncGPUReadback.Request(renderTexture, 0);
asyncAction.WaitForCompletion();
texture2D.SetPixelData(asyncAction.GetData<byte>(), 0);
texture2D.Apply();
//Asynchronously
AsyncGPUReadback.Request(renderTexture, 0, (AsyncGPUReadbackRequest asyncAction) =>
{
texture2D.SetPixelData(asyncAction.GetData<byte>(), 0);
texture2D.Apply();
});
I am still curious on it. If I create a Texture2D like Texture2d test = new Texture2D(), and use CopyTexture to copy a renderTexture to it, what will actually happen? I think creating a texture2D is a pure CPU operation, so the texture2d must be in CPU RAM. But the CopyTexture is doing stuff on GPU side?