Search code examples
iosiphoneopengl-esglkit

Why do I need to call glClear in drawInRect?


I have a simple GLKViewController where i made the screen white:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

    GLKView *view = (GLKView *)self.view;
    view.context = self.context;

    [EAGLContext setCurrentContext:self.context];

    glClearColor(1, 1, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT);
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
}

This doesn't work, however.

But, if I move the glClear call to drawInRect, then it works:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    glClear(GL_COLOR_BUFFER_BIT);
}

Why is that? Why must glClear be called in drawInRect but glClearColor not?


Solution

  • The context on the GPU holds lots of values, states which are preserved until changed. One of them is the clear color which consists of 4 floating values. Whenever you set these values they will persist as the same values and can be reused until you change them again.

    So the clear color can be set any time after the context is created and set. This value will be used by glClear to set the color bound buffers components (pixels) to this value. For this to work a frame buffer must be bound at least.

    Calling the glClear in the viewDidLoad might or might not be valid as this call needs a frame buffer to be bound so it knows what buffer to clear to the color previously set. By "might or might not be valid" I mean since you are using convenience classes from the GLKit you really have no control or even idea of the pipeline these classes are using. At the time you call that function you have no idea if your buffer is bound or even created. If you created this manually you would create a context, create a frame buffer and attach render buffers, then you may clear those buffers and all works as expected.

    So the glClear can be called any time and in some cases even multiple times in the drawRect method. What this call results to is the same as drawing a full buffer size rect with the color set by the glClearColor, the difference is it is much faster as it skips a few steps in the openGL draw pipeline plus you may clear more then the color buffer (a depth buffer for instance).

    Your render buffer that you clear can be best presented as a 2 dimensional array of pixels or something like char buffer[width][height][4] as it has 4 color components (RGBA) usually. The buffer data are again preserved and can be redrawn over and over again, you may draw to different peaces or even present a certain part by using glViewport. If you clear the buffer on each draw frame call it means you will need to redraw all the elements again which is usually the case but if you want to keep drawing stuff to the buffer you must skip the clear call. For instance if you want to draw a circle every time a user taps on the screen you would skip the clear call. But in that case you would also want to lose the timer (display link) to keep calling the draw method, when a user taps the screen you simply draw the circle and present the buffer to the screen, there is no reason to keep refreshing it.

    So why do you need to call the glClear in drawInRect? You do not, you can call it at any point and any method you want, all you need to make sure is you have the correct context set and the correct frame buffer bound. Although I agree this can be an issue if you do not have a direct access to these elements which in your case is using GLKView.