- 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.CopyTexture
as 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