- Home /
 
Graphics.CopyTexture copies wrong mip on Windows 10 build
Hey folks,
so I have this super simple example code that runs fine on the editor but behaves weird on a build:
 // _tex is a 2D Texture with mipmap displayed on left quad
 leftQuad.material.mainTexture = _tex;
 // Create a texture without a mipmap
 var tex = new Texture2D(_tex.width, _tex.height, _tex.graphicsFormat, 0, TextureCreationFlags.None);
 // copy full resolution texture (mip 0) and show it on right quad
 Graphics.CopyTexture(_tex, 0, 0, tex, 0, 0);
 rightQuad.material.mainTexture = tex;
 
               Te result looks like this: 
In other words: whenever I try to access _tex.mip[0] it behaves as if it was _tex.mip[1] meaning the mip with only half the width/height. I have tried this in a compute shader, using GetPixels and using Graphics.CopyTextureas above. Always the same result!
I have tested all this using:
Unity 2020.2.3f1
Unity 2020.2.2f1
Any idea? Help would be highly appreciated.
The code looks right, and in theory this shouldn't be possible; not only does the target texture not have mipmaps but also you cannot copy between textures of different resolutions (i.e. between different mips of a texture). Looking at the image in the build, the right image does look sharper to me so maybe there is some weird platform-specific GPU transform nonsense going on that is resulting in the image being drawn at half the required viewport. I would try using the CopyTexture() region overload and see what happens.
That's an interesting thought though the smaller image is definetely using a smaller resolution (less sharp if you look closer in the scene). The problem wtih this overload is that I cannot upscale the region hence I get the following error on both, Editor and Build:
Here is what I've tried:
 // Same behavior as before:
 Graphics.CopyTexture(_tex, 0, 0, 0, 0, _tex.width, tex.height, tex, 0, 0, 0, 0);
 
 // $$anonymous$$s to this error on Editor and Build: 
 // Graphics.CopyTexture called with region not fitting in source
 // element (srcX 256, srcY 256, srcWidth 512, srcHeight 512, src$$anonymous$$ip 0)
 Graphics.CopyTexture(_tex, 0, 0, _tex.width/2, _tex.height/2, _tex.width, tex.height, tex, 0, 0, 0, 0);
                  Answer by jonaskunze · Feb 05, 2021 at 07:25 PM
I just found the reason for this weird behaviour!
Explanation and Workaround 1
Project Settings > Quality > Texture Quality
This setting was Half Res. Setting it to Full Res fixes the issue. But this way you cannot reduce the texture resolution for better performance settings!
Deeper understanding and Workaround 2
What's weird: The quality setting still does not explain the difference between editor and build. There is also a variable that allows you to check for this seeting and it is 1 in both cases for me:
QualitySettings.masterTextureLimit
This value is 0 for Full Resolution, 1 for Half Resolution etc. In other words: it defines the smallest mip level that is used when rendering textures and it seems to be used by CopyTexture internaly. We can use that to "fix" the issue:
 // The scale factor is 2^QualitySettings.masterTextureLimit:
 var scaleFactor = 1 << QualitySettings.masterTextureLimit;
 var width = _tex.width / scaleFactor;
 var height = _tex.width / scaleFactor;
 // Create a texture with the reduced size and no mipmap
 var tex = new Texture2D(width, height, _tex.graphicsFormat, 0, TextureCreationFlags.None);
 
 // Temporarily set the texture Limit to 0 so taht CopyTexture behaves properly
 var texLimit = QualitySettings.masterTextureLimit;
 QualitySettings.masterTextureLimit = 0;
 Graphics.CopyTexture(_tex, 0, texLimit, tex, 0, 0);
 QualitySettings.masterTextureLimit = texLimit;
 
               Compute Shader
The original reason I came accross this was that I got the exact same weird behavior using a very simple compute shader. I was able to fix it using that same workaround. Here is a working version that might help you too:
 /// C#
 CubemapRenderTexture = new RenderTexture(_tex.width, _tex.height, 0, _tex.graphicsFormat, 0);
 {
     CubemapRenderTexture.dimension = TextureDimension.Tex2D;
     CubemapRenderTexture.enableRandomWrite = true;
     CubemapRenderTexture.useMipMap = false;
     CubemapRenderTexture.Create();
 }
 int kernelIndex = computeShader.FindKernel("CSMain");
 computeShader.SetTexture(kernelIndex, "INPUT", _tex);
 computeShader.SetTexture(kernelIndex, "OUTPUT", CubemapRenderTexture);
 
 
 // Run the kernel with textureLimit==0
 var texLimit = QualitySettings.masterTextureLimit;
 QualitySettings.masterTextureLimit = 0;
 computeShader.Dispatch(kernelIndex, _tex.width, _tex.height, 1);
 QualitySettings.masterTextureLimit = texLimit;
 
 
 /// .compute file
 #pragma kernel CSMain
 
 // Inputs
 Texture2D<float4> INPUT;
 RWTexture2D<float4> OUTPUT;
 
 [numthreads(1,1,1)]
 void CSMain(uint3 id : SV_DispatchThreadID)
 {
     OUTPUT[id.xy] = INPUT[id.xy]; // Same as INPUT.mip[0][id.xy] => full resolution texture
 }
 
               Wrap up
To me this looks like a bug. However it is really weird that I cannot find much about this online though I only do very basic stuff and this should happen to a lot of people out there. So maybe I missed something essential here?
Fyi: I've filed a bug report: http://fogbugz.unity3d.com/default.asp?1312568_d9rfr8bdr8od1sj7
Your answer
 
             Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Particle alpha blended proplem please help me 0 Answers
Setting color for compute shader structure trough c# 0 Answers