I have a EAGLView that I wish to convert into a UIImage. I can do this with the solution posted here:
How to get UIImage from EAGLView?
However, I can only accomplish this if a small amount of time has gone by between the creation of the EAGLView and the UIImage.
This code, which creates a EAGLView, and then a UIImageView right afterwards, does not work:
EAGLView *EAGLphoto = [[EAGLView alloc] initWithImage:photo.workAreaImage];
[theWorkArea.photoArea1 addSubview:EAGLphoto];
//I put a glfinish() here but it didn't help
UIImageView *photoView = [[UIImageView alloc] initWithImage:[self glToUIImage]];
//glToUIImage is taken from the link above
[theWorkArea.photoArea2 addSubview:photoView];
I’m assuming the UIImageView tries to get created before the EAGLView is finished being created. I tried to put a glfinish() in between but it did nothing. When I run the code, the EAGLView shows up fine but the UIImageView shows up as black.
However, this modified version of the above code works:
EAGLView *EAGLphoto = [[EAGLView alloc] initWithImage:photo.workAreaImage];
[theWorkArea.photoArea1 addSubview:EAGLphoto];
[self performSelector:@selector(getUIImage) withObject:nil afterDelay:0.1];
- (void)getUIImage {
UIImageView *photoView = [[UIImageView alloc] initWithImage:[self glToUIImage]];
//glToUIImage is taken from the link above
[theWorkArea.photoArea2 addSubview:photoView];
}
Am I using glfinish() incorrectly? Is there a better method than my hack?
Usually when you make visible changes to UIKit objects instead of updating instantly they merely flag themselves to make those changes in the future, then do the whole set of changes as a batch next time you relinquish the main thread (by returning to the runloop). That’s not a misfeature or a failure in the implementation, it’s actually usually what you implicitly expect, being the reason you can write code like:
And not worry that every so often the view will visibly adopt the new frame before adopting the other changes. As a rule, you want the things you do to views to appear to be atomic.
Occasionally you run into a situation, as you have here, where the fact that changes you’ve already requested haven’t come into effect yet is exposed.
frameandsomeOtherPropertywould return their new values in the above example so that you don’t care whether the changes took effect immediately or not, but based on your observation aboutperformSelector:withObject:afterDelay:it seems likely that you’ve stumbled upon a situation where the change doesn’t pretend to be immediate.EAGLViewis difficult to diagnose because it’s not really a UIKit feature. TheCAEAGLLayerthat it’s built upon is, butEAGLViewis just the name Apple have adopted for a custom UIView subclass built onCAEAGLLayerin various example projects. They’ve not been consistent about the interface or implementation ofEAGLViewacross their examples, but at a guess I’d say that probably it’s creating the OpenGL frame buffer object (that is, the thing that OpenGL uses to store numbers related to pixels, allowingglReadPixelsto work) only when asked to lay itself out, and it doesn’t lay itself out until UIKit asks it to do so. Which isn’t until you’ve dropped out to the runloop.That diagnosis can be confirmed by checking the code of the
EAGLViewthat you have. If there are a bunch of calls to things likeglGenFramebuffersthat are triggered as a result oflayoutSubviews,drawRector some other method that isn’t called directly by the relevantinitthen that proves it.Assuming that diagnosis to be correct, you could adapt
EAGLViewbut I think probably the best solution is just to stick toperformSelector:withObject:afterDelay:0. This isn’t a race condition, so that solution isn’t in the slightest bit flakey or unreliable, it’s just a slightly roundabout way of saying “let UIKit catch up with all of those instructions, given that I know that’ll let EAGLView get into a meaningful state, then continue with my stuff”.