Hi again everybody!
This will be a little article to give support to my full tutorial about OpenGL. In this article I'll talk about the EGL API and the EAGL (the EGL API implemented by Apple). We'll see how to set up an OpenGL's application to comunicate with the device's windowing system.
I'll focus on Objective-C and iOS, but I'll talk about the setup in others devices and languages too. So let's start!
At a glance
As I said in the first part of my full tutorial about OpenGL (click here to see), OpenGL is not responsible by manage the windowing system of each device that support it. OpenGL relinquished this responsability. So, to make the bridge between OpenGL's render output and the device's screen, Khronos group created the EGL API.
Remember that EGL can be modified by the vendors to fit exactly what they need to their devices and windowing systems. So my big advice in this article is: ALWAYS CONSULT THE EGL INSTRUCTIONS FROM YOUR VENDORS.
Setup EGL API
OK, now we'll enter in a EGL's comum area, independently to the vendors's implementation, the EGL's logics is always needed.
The first thing that EGL needs to know is where we want to display our content, normally it is done with the function eglGetDisplay, which returns a EGLDisplay data type. A constant EGLDEFAULTDISPLAY is always implemented by the vendors to return their default display. Right after this you call eglInitialize to initialize the EGL API, this function will return a boolean data type to inform the status. So normally you start with the code:
EGLDisplay display;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (eglInitialize(display, NULL, NULL))
{
// Proceed with your code.
}
The NULL, NULL parameters are pointers, which you can get the major and minor version of the current EGL's implementation. I don't want to go deeply here, the important thing is understand this step. The EGL API need to know where is the display to initialize, just it!
OK, the next step is the configuration. Once EGL know the display, it can prepare the bridge between OpenGL's output and the device's screen. To start constructing this bridge, the EGL needs some configurations, which involve the color format, colors individually, the sizes, the transparency, the samples per pixel, pixel format and many others. By default EGL provide a lot of functions to this step, like eglGetConfigs oreglChooseConfigs. Here the interference of the vendors is more intense, because they need to provide a bunch of configurations about their systems and devices. I'll not describe it here, there are so many systems and languages. So again, always consult your vendors.
The last step is more simple. After all configurations, you instruct EGL API to create a render surface, this means, the surface on which your OpenGL's render output will be placed. You can choose between a On-Screen or a Off-Screen surface. The first means your render output will go directly to the device's screen, the Off-Screen means that your render output will go to a buffer or to a image file, a snapshot, or anything like that. You can define the surface by using eglCreateWindowSurface oreglCreatePbufferSurface, both will return an EGLSurface.
But here a little advice, if you want to place your render's output onto a Off-Screen surface, is better you use a frame buffer directly with OpenGL's API instead to create a EGL's PBuffer. Is faster and cost less for your application.
Are we ready now?
Not yet. This is just the platform's side of the bridge, EGL has the other side of the bridge, the OpenGL's side.
EGL Context
Until now, EGL knows all that it need about the windowing system, but now EGL needs to know all about our OpenGL usage. Deeply, EGL works with 2 frame buffers. Do you remember what is frame buffer from the first part of OpenGL's tutorial, right? (click here if not). The EGL API use these 2 frame buffer to place the OpenGL's render output into your desired surface.
To make it properly, EGL needs to know where are the OpenGL's buffers which you want to render. This step is very simple. All that we need is create a EGL context and make it the current context (yes, we can create many contexts). Once we made a context the current context, the subsequent Frame Buffer we use in OpenGL will be used by EGL context. Only one Frame Buffer can be used at time (the next part of OpenGL's tutorial will talk deeply about this). Usually, the EGL functions to context are:
EGL Context |
---|
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint* attribList)
|
EGLBoolean eglmakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)
|
Seems a little confused? Don't worry. I'm talking superficially about this. The point here is to you understand which an EGL's Context represent the OpenGL's side of the bridge. The most important advice I can give you is: Look for the vendors documentation of EGL API.
Rendering with EGL Context
Once the EGL knows about the windowing system and has a context to know about our OpenGL application, we can make our render's output be presented onto the desired surface.
The render step is very very simple. Do you remember I said EGL works with 2 internal frame buffers? Now is time to use that. All that we need is instruct the EGL to swap the buffers. Swap? Why?
While EGL present one frame buffer to your desired surface, the other one stay on back waiting for a new render output. The buffer on the back will be filled with all renders you did until the next call to EGL swap the buffers and when you do this, EGL brings the back buffer to the front and present the final render's output onto the desired surface, while the old front buffer goes to the back to reinitiate the process.
This technique is a great boost on our application's performance, because the our final surface will not be notified every time we execute a render command. The final surface will be notified only when we finish all the renders commands. Another improvement is because the buffer on the back will receive the render's outputs faster than a device's screen, for example.
This is the function to swap the buffers and present the render output onto the desired surface.
Swap the EGL's buffers |
---|
EGLBoolean eglSwapBuffers(EGLDisplay display, EGLSurface surface)
|
OK, this is all about the EGL making the bridge between the OpenGL's core and the windowing systems. I know that seems many steps to just output a simple render. But look from far, is just a little setup:
- Initialize the EGL API with the display and surface given by your vendor.
- Make the configurations to your desired surface.
- Create a context and make it your current context.
- Ask for your EGL context to swap its internal buffers.
Now I'll talk about EGL implementation by Apple, which has a lot of changes from the original, many changes!
EAGL - The Apple's EGL API
The EAGL (which we are used to pronounce Eagle) is the implementation by Apple of EGL API. The most important thing to know about EAGL is which the Apple doesn't allow us to render directly to the screen. We must to work with frame and render buffers.
But why the Apple changed EGL so much? Well, you know the Apple's approach, Cocoa Framework is almost like another language, it has infinities rules, hard rules! If you change the place of one little screw, all the framework will down! So the Apple made that changes to fit the EGL API into the Cocoa's rules.
Using EAGL, we must to create a color render buffer and draw our render's output into it. Plus, we must to bind that color render buffer to a special Apple's layer called CAEAGLLayer. And finally to make a render, we need to ask the context to present that color render buffer. In deeply, this command will swap the buffer just like original EGL context, but our code changes a lot. Let's see.
Setup EAGL API
The first thing we need to do is create a sub-class from UIVIew. Inside this new class, we need to change its Core Animation layer. Doing this is equivalent in the original EGL API to initialize the API, take the display from the windowing system and define our surface. So, in this equivalence we'll have EGLDisplay = the UIWindow which has our custom view and EGLSurface = our sub-class from UIView.
Our code will be like this:
#import <UIKit/UIKit.h>
@interface CustomView : UIView
@end
@implementation CustomView
// Overrides the layerClass from UIView.
+ (Class) layerClass
{
// Instead to return a normal CALayer, we return the special CAEAGLLayer.
// Inside this CAEAGLLayer the Apple did the most important part
// of their custom EGL's implementation.
return [CAEAGLLayer class];
}
@end
Now we need to make the step of configure the EGL API defining the color format, the sizes and others. In EAGL we do this step by setting the properties of our CAEAGLLayer. So let's code:
@implementation CustomView
...
- (id) initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Assign a variable to be our CAEAGLLayer temporary.
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
// Construct a dictionary with our configurations.
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGB565, kEAGLDrawablePropertyColorFormat,
nil];
// Set the properties to CAEALLayer.
[eaglLayer setOpaque:YES];
[eaglLayer setDrawableProperties:dict];
// Other initializations...
}
return self;
}
...
@end
EAGL Context
Following the EGL API steps, the next one is about the context. Here again the Apple changes almost all to fit the EGL API into their Cocoa Framework. The EAGL API gives to us only two Objective-C classes, one of then is EAGLContext. The EAGLContext is equivalent to EGLContext.
Here I have to admit, even changing everything the Apple made this step easer than EGL API. All that you need is allocate a new instance of EAGLContext initializing it with your desired OpenGL ES's version (1.x or 2.x) and call a method to make that context the current one. Here you don't have to inform again the EGLDisplay, the EGLSurface, the EGLContext and the others annoying parameters as in the origial EGL API.
@interface CustomView : UIView
{
EAGLContext *_context;
}
@end
@implementation CustomView
...
- (id) initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Other initializations...
// Create a EAGLContext using the OpenGL ES 2.x.
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
// Make our new context, the current context.
[EAGLContext setCurrentContext:_context];
}
return self;
}
...
@end
Rendering with EAGL Context
Next step!
By following the above EGL's steps the next one is the swap the buffers (rendering). But here the Apple changed the thing a lot. As I said before, to use EAGL we must to create at least a color render buffer, this necessarily needs a frame buffer too. So in the reality the next step should be the buffers creations. I could describe this step here, but as this article is just a middle point between the first and the second part of my full tutorial, describe it is not my intention here. I'll let this discussion to the next part of that tutorial.
So let's go to the final EAGL's step, the rendering.
I told you Apple uses a color render buffer instead to directly swap the context's buffers. By doing this, Apple simplified the last step, the swap of the buffers. Instead to swap the buffers informing the desired display and surface, we just ask our EAGLContext to present its render buffer.
@interface CustomView : UIView
{
EAGLContext *_context;
}
- (void) makeRender;
@end
@implementation CustomView
...
- (void) makeRender
{
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
...
@end
The param GL_RENDERBUFFER to presentRenderbuffer is a constant and is just for internal usage. This call should occurs just after we perform all our changes to our 3D objects, like translates or rotations.
Well, this is all the basic instructions about EGL and EAGL APIs. Obviously using EAGL, you could take a more OOP approach, by separating your custom UIView of the other class which hold the EAGLContext instance and makes the final presentation.
Now, as we are used to, let's remember the steps using the EAGL.
- Make a sub-class from UIView and override the layerClass method to return an instance of CAEAGLLayer;
- Set up the properties to the CAEAGLLayer;
- Create an instance from EAGLContext;
- Present a color render buffer onto the screen by using the EAGLContext.
Conclusion
Well done, my friends.
Think in this article like an abstract class! Is a little step, but is also so much annoying to cover this in the second part of my OpenGL's tutorial.
If you were to save only a single sentence of this entire article, the phrase would be: "Consult the EGL instruction from your vendors!". The vendors don't interfere inside OpenGL, so I can talk about it independently from the system or language. But the EGL API is the bridge between a unified OpenGL API and all the systems which support OpenGL. So the interference from the vendors on the EGL is bigger! Just like the Apple did!
Thanks for reading!
See you in the next part of OpenGL's tutorial!
scrolling="no" src="http://db-in.com/downloads/apple/tribute_to_jobs.html" width="100%" height="130px" style="text-rendering: optimizelegibility; box-sizing: border-box;">