posit labs posit labs - 2 months ago 9
C# Question

iOS hangs when calling Texture2D.readPixels

I'm attempting to draw a

RenderTexture
into a
Texture2D
with the goal of saving it to disk. This approach has been working in the OSX editor, as well as on Android.

I don't see any errors in the XCode console, and my app becomes completely frozen when I call
Texture2D.ReadPixels()


Here's a summary of the code:

// declaring variables...
RenderTexture outputTexture;
RenderTextureFormat RTFormat = RenderTextureFormat.ARGB32;

// use an appropriate format for render textures
if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)){
RTFormat = RenderTextureFormat.ARGBFloat;
}else if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)){
RTFormat = RenderTextureFormat.ARGBHalf;
}

// create instance of output texture
outputTexture = new RenderTexture (res.x, res.y, 0, RTFormat);

// in Update, draw stuff to outputTexture
Graphics.Blit (outputTexture, canvasTexture);
Graphics.Blit (canvasTexture, outputTexture, material);

// later... user wants to save the image
// draw rendertexture to a Texture2D so we can write to disk
RenderTexture.active = outputTexture;
tmpTexture = new Texture2D (outputTexture.width, outputTexture.height, TextureFormat.ARGB32, false);
tmpTexture.ReadPixels (new Rect (0, 0, outputTexture.width, outputTexture.height), 0, 0, false);
tmpTexture.Apply ();
RenderTexture.active = null;


I have tried using a variety of
RenderTextureFormat
and
TextureFormat
, but nothing seems to work!

Answer

I believe this is being caused by your render texture format call. Something similar happened to be before.

This bit of code assigns a default texture format, then alters the default format if the currently executing environment supports it (my comments are added)

//set a default render texture of  RenderTextureFormat.ARGB32;
RenderTextureFormat RTFormat = RenderTextureFormat.ARGB32;

// if my system supports it, switch to either ARGBFloat or ARGBHalf

// use an appropriate format for render textures
if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)){
    RTFormat = RenderTextureFormat.ARGBFloat;
}else if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)){
    RTFormat = RenderTextureFormat.ARGBHalf;
}

However, when you actually later define your temp texture to fill with ReadPixels(), you only define it one way (again, my comments added)

//define a new tmpTexture container, ALWAYS with a TextureFormat of ARGB32
tmpTexture = new Texture2D (outputTexture.width, outputTexture.height, TextureFormat.ARGB32, false);

Thus on some systems (whichever supports the format), you're trying to readPixels() from one texture format into another. This is probably causing your issue.

You can fix this by also dynamically changing the format of the destination texture. So in the first section, you would change it to:

RenderTextureFormat RTFormat = RenderTextureFormat.ARGB32;

//add another variable here for the destination Texture format
var destinationFormat = TextureFormat.ARGB32;


// use an appropriate format for render textures
if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)){
    RTFormat = RenderTextureFormat.ARGBFloat;

    //also set destination format
    destinationFormat = TextureFormat.RGBAFloat;
}else if(SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)){
    RTFormat = RenderTextureFormat.ARGBHalf;

    //also set destination format
    destinationFormat = TextureFormat.RGBAHalf;
}

and then of course, later consume the dynamically set format when declaring the destination object:

//define a new tmpTexture container, with a dynamically set destination format that always matches the input texture
tmpTexture = new Texture2D (outputTexture.width, outputTexture.height, destinationFormat, false);

Let me know if you still have issues in the comments.