Texture2d.readPixels not working with iOS 6

I’ve made an iOS app with Unity 3.5.6 and QCAR for iPad. The app uses readPixels to take a screenshot of the AR scene and has worked great unless I try it on an iPad with iOS 6, then it only makes a black texture. I get the warning mentioned in the Unity 3.5.6 change log saying “ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame.” , but I get this error on an iOS 5.x device too and readpixels takes the screenshot. I’m calling readpixels in OnGUI() when a button is clicked, and use a co-routine to wait for the end of the current frame. I’ve tried it without the co-routine, with the co-routine between readPixels and Apply(), and tried calling readPixels in Update(), which captured only the background, no AR elements. Anyone else dealt with this?

void OnGUI(){
		
		GUI.Box(new Rect(0, Screen.height - 125, Screen.width, 150), bottomBar, bottom);
		
		// make screen capture button
		if(GUI.Button(new Rect(309, 910, 150, 100),cameraBtn,cameraIcon)){
			audio.Play();
			CaptureImage();
			clicked = true;
			
			Application.LoadLevel("Preview");
		}
		
		// alert box
		if(clicked){
			GUI.Box(new Rect( 282, 425, 204, 80), alert, alertGS);
		} // end alert box
		
	}// end OnGUI()

	
	void CaptureImage(){
			
			clicked = true;
		
			// wait for end of current frame
			StartCoroutine(waitForEnd());
			
			// create texture to hold image
			tex = new Texture2D(width, height, TextureFormat.RGB24, false);
			
			// capture the image
			tex.ReadPixels(new Rect(0, 125, width, height), 0,0);
			tex.Apply();
		
			Application.LoadLevel("Preview");
			
	} // end CaptureImage	 

	
	
	IEnumerator waitForEnd(){
		
		yield return new WaitForEndOfFrame();
		
	} // end waitForEnd

CaptureImage supposed to be coroutine.

You need to change StartCoroutine(waitForEnd()); to yield return StartCoroutine(waitForEnd());, otherwise it doesn’t wait for waitForEnd to finish and just continues on. Also you don’t need waitForEnd at all, you can simply call WaitForEndOfFrame directly from CaptureImage.

Code:

IEnumerator CaptureImage(){
     clicked = true;

     // wait for end of current frame
     yield return new WaitForEndOfFrame();

     // create texture to hold image
     tex = new Texture2D(width, height, TextureFormat.RGB24, false);

     // capture the image
     tex.ReadPixels(new Rect(0, 125, width, height), 0,0);
     tex.Apply();

     Application.LoadLevel("Preview");

} // end CaptureImage

Also be careful when pasting Javascrip coroutine example code into C# code, that’s how you end up with StartCoroutine instead of yield return StartCoroutine. Javascript does some thing behind your back (I don’t like it :))

It seems that this is an iOS 6 bug. There have been a few bug reports generated about it and it is discussed on the Apple Developer Forums. The suggested fix is to use retained backing on the EAGLView. I ended up changing the boolean in the drawableProperties of the CAEAGLLayer in AppController.mm from FALSE to TRUE. See below. Apple cautions that this fix can affect performance but I don’t see any problems and several others have fixed the bug with this without issue.

layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
// change FALSE to TRUE
[NSNumber numberWithBool:TRUE], kEAGLDrawablePropertyRetainedBacking,
colorFormat, kEAGLDrawablePropertyColorFormat, nil ];

Try QualitySettings.antiAliasing = 0