How to use dispatch_async in glkview? - ios

I'm learning how to use threading with my iOS Opengl-es app. i tried to modify the opengl game template code:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArrayOES(_vertexArray);
// Render the object with GLKit
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 36);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_async(dispatch_get_main_queue(), ^{
// Render the object again with ES2
glUseProgram(_program);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, _normalMatrix.m);
glDrawArrays(GL_TRIANGLES, 0, 36);
});
});
}
It draws a box at 60fps and another at 1fps if it works well, but when i run the code above, it only draws one box.
I searched the web and can't find what is causing the problem.
Thank you very much for any help!

The code in the dispatch block isn't going to work. By the time it gets executed, all of the OpenGL state for that frame will have long since been destroyed. If you were to put a call to glGetError() in that block, I'm sure it would tell you the same. You need to make sure that all your drawing code is done in that glkView method for the OpenGL state to be valid. When you do that dispatch, you're essentially shunting the execution of that drawing code out of the scope of that method.

Related

iOS Rendering a object to a texture

I'm currently trying to divide the OpenGL ES 2.0 drawing process onto two halves: the first half where I render an object of interest (i.e. a cube or triangle) to a framebuffer that has a texture attached to it, and the second half where I apply that texture onto the face of a shape drawn in another framebuffer (i.e. another cube or triangle).
I cleared the framebuffer binded to the texture with a green color, and have been able to get that color to appear onto a triangle that I've drawn in another framebuffer that has the main renderbuffer attached and that I call [context presentRenderbuffer: renderbuffer] on. However, no matter what I do I'm not able to additionally draw another shape into that texture after I've cleared it to a green background, and render that onto the shape I've drawn.
For some visual reference, currently I'm drawing a square to the screen in my main framebuffer, and then applying a texture that is supposed to have a green background plus a triangle in the middle, but all that I get is this green screen.
It has everything that I currently want, except there is no triangle that is also in the middle. Essentially, this should look like a big green square with a black triangle in the middle of it, where the green and the triangle all came from the texture (the square would have originally been black).
My texture drawing method and main framebuffer drawing methods are included below (without the setup code):
- (BOOL) loadModelToTexture: (GLuint*) tex {
GLuint fb;
GLenum status;
glGenFramebuffers(1, &fb);
// Set up the FBO with one texture attachment
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenTextures(1, tex);
glBindTexture(GL_TEXTURE_2D, *tex);
NSLog(#"Error1: %x", glGetError());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, *tex, 0);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
// Handle error here
NSLog(#"Loading model to texture failed");
return FALSE;
}
glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // Set color's clear-value to red
glClearDepthf(1.0f); // Set depth's clear-value to farthest
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width*self.contentsScale, self.frame.size.height*self.contentsScale);
NSLog(#"Error2: %x", glGetError());
// Update attribute values.
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
NSLog(#"Error3: %x", glGetError());
glDrawArrays(GL_TRIANGLES, 0, 3);
return TRUE;
}
- (void) draw {
[EAGLContext setCurrentContext:context];
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClearColor(0.0f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width*self.contentsScale, self.frame.size.height*self.contentsScale);
// Use shader program.
glUseProgram(program);
// Update attribute values.
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);
//for some reason this is unneeded, but I'm not sure why
glVertexAttribPointer(ATTRIB_TEXTURE_COORD, 2, GL_FLOAT, 0, 0, texCoords);
glEnableVertexAttribArray(ATTRIB_TEXTURE_COORD);
glBindTexture(GL_TEXTURE_2D, textureName0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawArrays(GL_TRIANGLES, 3, 3);
[context presentRenderbuffer:renderbuffer];
}
What other steps do I need to take to get it to draw correctly to the texture/apply correctly to the main framebuffer drawing?
Never mind, it turns out that I was drawing my triangle to the texture, but the triangle just automatically defaulted to the same color as the background texture, so it's a completely different issue.

Framerate dropping when drawing multiple textures in OpenGL ES iOS

Basically what I'm doing is making a simple finger drawing application. I have a single class that takes the input touch points and does all the fun work of turning those touch points into bezier curves, calculating vertices from those, etc. That's all working fine.
The only interesting constraint I'm working with is that I need strokes to blend on on top of each other, but not with themselves. Imagine having a scribbly line that crosses itself and has 50% opacity. Where the line crosses itself, there should be no visible blending (it should all look like the same color). However, the line SHOULD blend with the rest of the drawing below it.
To accomplish this, I'm using two textures. A back texture and a scratch texture. While the line is actively being updated (during the course of the stroke), I disable blending, draw the vertices on the scratch texture, then enable blending, and draw the back texture and scratch texture into my frame buffer. When the stroke is finished, I draw the scratch texture into the back texture, and we're ready to start the next stroke.
This all works very smoothly on a newer device, but on older devices the frame rate takes a severe hit. From some testing, it seems that the biggest performance hit is in drawing the textures to the frame buffer, because they're relatively large textures (due to the iPhone's retina resolution).
Does anybody have any hints on some strategies to work around this? I'm happy to provide more specifics or code, I'm just not sure where to start.
I am using OpenGL ES 2.0, targeting iOS 7.0, but testing on an iPhone 4S
The following is code I'm using to draw into the framebuffers:
- (void)drawRect:(CGRect)rect
{
[self drawRect:rect
ofTexture:_backTex
withOpacity:1.0];
if (_activeSpriteStroke)
{
[self drawStroke:_activeSpriteStroke
intoFrameBuffer:0];
}
}
Those rely on the following few methods:
- (void)drawRect:(CGRect)rect
ofTexture:(GLuint)tex
withOpacity:(CGFloat)opacity
{
_texShader.color = GLKVector4Make(1.0, 1.0, 1.0, opacity);
[_texShader prepareToDraw];
glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArrayOES(_texVertexVAO);
glBindBuffer(GL_ARRAY_BUFFER, _texVertexVBO);
[self bufferTexCoordsForRect:rect];
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArrayOES(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, tex);
}
- (void)drawStroke:(AHSpriteStroke *)stroke
intoFrameBuffer:(GLuint)frameBuffer
{
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
[self renderStroke:stroke
ontoTexture:_scratchTex
inFrameBuffer:_scratchFrameBuffer];
if (frameBuffer == 0)
{
[self bindDrawable];
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
}
[self setScissorRect:_activeSpriteStroke.boundingRect];
glEnable(GL_SCISSOR_TEST);
[self drawRect:self.bounds
ofTexture:_scratchTex
withOpacity:stroke.lineOpacity];
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
}
- (void)renderStroke:(AHSpriteStroke *)stroke
ontoTexture:(GLuint)tex
inFrameBuffer:(GLuint)framebuffer
{
glBindFramebuffer(GL_FRAMEBUFFER, _msFrameBuffer);
glBindTexture(GL_TEXTURE_2D, tex);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
[stroke render];
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, framebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, _msFrameBuffer);
glResolveMultisampleFramebufferAPPLE();
const GLenum discards[] = { GL_COLOR_ATTACHMENT0 };
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, discards);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
And a couple of the helper methods just for completeness so you can follow it:
- (void)bufferTexCoordsForRect:(CGRect)rect
{
AHTextureMap textureMaps[4] =
{
[self textureMapForPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect))
inRect:self.bounds]
};
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(AHTextureMap), textureMaps, GL_DYNAMIC_DRAW);
}
- (AHTextureMap)textureMapForPoint:(CGPoint)point
inRect:(CGRect)outerRect
{
CGPoint pt = CGPointApplyAffineTransform(point, CGAffineTransformMakeScale(self.contentScaleFactor, self.contentScaleFactor));
return (AHTextureMap) { { pt.x, pt.y }, { point.x / outerRect.size.width, 1.0 - (point.y / outerRect.size.height) } };
}
From what I understand you are drawing each quad in a separate draw call.
If your stroke consist of a lot of quads(from sampling the bezier curve) your code will make many draw calls per frame.
Having many draw calls in OpenGL ES 2 on older iOS devices will probably generate a bottle neck on the CPU.
The reason is that draw calls in OpenGL ES 2 can have a lot of overhead in the driver.
The driver tries to organize the draw calls you make into something the GPU can digest and it does this organization using the CPU.
If you intend to draw many quads to simulate a brush stroke you should update a vertex buffer to contain many quads and then draw it with one draw call instead of making a draw call per quad.
You can verify that your bottle neck is in the CPU with the Time Profiler instrument.
You can then check if the CPU is spending most of his time on the OpenGL draw call methods or rather on your own functions.
If the CPU spends most of it's time on the OpenGL draw call methods it is likely because you are making too many draw calls per frame.

glPushMatrix() & glPopMatrix() throw GL_INVALID_OPERATION

i try to draw a waveform from the incoming iphone microphone stream. The extraction of the data was no problem an the drawing works fine. Only when I use the OpenGL Exception Breakpoint xcode throws exceptions at glPushMatrix() & glPopMatrix()with the code GL_INVALID_OPERATION. I searched the internet for some more informations, but the only thing that i found was this:
GL_INVALID_OPERATION is generated if glPushMatrix or glPopMatrix is executed between the execution of glBegin and the corresponding execution of glEnd.
i dont use the commands glBegin oder glEnd, because of that this doesn't help me. Any ideas? What is the problem here? i draw the stuff like this:
- (void)drawPlotWithView:(GLKView*)view drawInRect:(CGRect)rect {
glBindBuffer(GL_ARRAY_BUFFER, _plotVBO);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(XOAudioPlotGLPoint), NULL);
[self.baseEffect prepareToDraw];
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, _plotGraphSize);
glPopMatrix();
[self.baseEffect prepareToDraw];
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
glDrawArrays(GL_TRIANGLE_STRIP, 0, _plotGraphSize);
glPopMatrix();
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
the initialization ist like this:
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.preferredFramesPerSecond = 60;
if (![EAGLContext currentContext]) {
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
} else {
self.context = [EAGLContext currentContext];
}
if (!self.context) {
NSLog(#"Failed to create ES context");
} else {
EAGLContext.currentContext = self.context;
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableMultisample = GLKViewDrawableMultisample4X;
glGenBuffers(1, &_plotVBO);
glLineWidth(2.0f);
glPushMatrix and glPopMatrix refer to the built-in matrix stack from the fixed-function OpenGL pipeline -- that functionality isn't in OpenGL ES 2.0.
However, the way that you're using it looks like it's not really doing anything, and what you are doing is in the wrong order. Drawing with GLKBaseEffect takes three steps:
Set the modelview and projection matrices via properties on your GLKBaseEffect instance. There's no "current matrix" or "matrix mode" implicit state like there is in GLES 1.x; just explicitly named and separately stored properties on GLKBaseEffect. (You're already doing this with the lines where you set self.baseEffect.transform.modelviewMatrix.)
Call prepareToDraw on the GLKBaseEffect instance. This binds the matrices, textures, and other state you've set in GLKBaseEffect for use by the shaders that class generates for you. (You're doing this before setting each matrix, so the matrices you're setting aren't taking effect when you want.)
After all that, perform an OpenGL draw command (glDrawArrays, glDrawElements, etc.) to draw with the state you've set.
The one additional thing you might think about is whether you've (elsewhere) set a different modelview matrix on your baseEffect and are using it for other draw calls. In that case, you might want to save the current matrix before drawing with a different matrix, then restore it afterward. A matrix stack is useful for that, and GLKit provides one in the GLKMatrixStack type and related functions. But if these are your only draw calls with that effect, or your other calls create a matrix from scratch like these ones do, there's no need to save/restore.
Continuing my research on the topic, the answer seems to be that OpenGL ES 2.0 indeed does not support matrix stack or push/pop, see here.

iOS: draw over video with OpenGL

I'm trying to draw some OpenGL graphics over the video from camera.
I've modified Apple's GLCameraRipple sample with code that draws a couple of textured triangles. This code works well in my another OpenGL project (but without GLKit).
Unfortunately, it only works here that way: when my app starts I see screen filled with ClearColor with my textured triangles on it (but no video), and in a moment the screen turns to black and I don't see anything.
Could you explain me what's the problem is?
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.5, 0.0, 0.0, 0.3);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(_program);
glUniform1i(uniforms[UNIFORM_Y], 0);
glUniform1i(uniforms[UNIFORM_UV], 1);
if (_ripple)
{
glDrawElements(GL_TRIANGLE_STRIP, [_ripple getIndexCount], GL_UNSIGNED_SHORT, 0);
}
[self drawAnimations];
}
- (void) drawAnimations{
// Use shader program.
glUseProgram(_texturingProgram);
GLfloat modelviewProj[16];
[self MakeMatrix:modelviewProj
OriginX:100.0
OriginY:100.0
Width:200.0
Height:200.0
Rotation:0.0];
// update uniform values
glUniformMatrix4fv(texturing_uniforms[TEXTURING_UNIFORM_MODEL_VIEW_PROJECTION_MATRIX], 1, GL_FALSE, modelviewProj);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _animationTexture);
glUniform1i(texturing_uniforms[TEXTURING_UNIFORM_TEXTURE], 0);
glVertexAttribPointer(TEXTURING_ATTRIB_VERTEX,3, GL_FLOAT, GL_FALSE, sizeof(vertexDataTextured), &plain[0].vertex);
glEnableVertexAttribArray(TEXTURING_ATTRIB_VERTEX);
glVertexAttribPointer(TEXTURING_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, sizeof(vertexDataTextured), &plain[0].texCoord);
glEnableVertexAttribArray(TEXTURING_ATTRIB_TEX_COORDS);
glDrawArrays(GL_TRIANGLES, 0, 6);
if (![self validateProgram:_texturingProgram]) {
NSLog(#"Failed to validate program: (%d)", _texturingProgram);
}
}
You could place a transparent UIView containing your Open GL drawing layer over another UIView containing the camera preview image layer.

Drawing with GLKit

I am trying to write a game using opengl, but I am having a lot of trouble with the new glkit classes and the default template from iOS.
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(#"Failed to create ES context");
}
if(!renderer)
renderer = [RenderManager sharedManager];
tiles = [[TileSet alloc]init];
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[self setupGL];
}
- (void)setupGL
{
int width = [[self view] bounds].size.width;
int height = [[self view] bounds].size.height;
[EAGLContext setCurrentContext:self.context];
self.effect = [[GLKBaseEffect alloc] init];
self.effect.light0.enabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(0.4f, 0.4f, 0.4f, 1.0f);
//Configure Buffers
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenRenderbuffers(2, &colourRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colourRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colourRenderBuffer);
glGenRenderbuffers(3, &depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBuffer);
//Confirm everything happened awesomely
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
if(status != GL_FRAMEBUFFER_COMPLETE) {
NSLog(#"failed to make complete framebuffer object %x", status);
}
glEnable(GL_DEPTH_TEST);
// Enable the OpenGL states we are going to be using when rendering
glEnableClientState(GL_VERTEX_ARRAY);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
float iva[] = {
0.0,0.0,0.0,
0.0,1.0,0.0,
1.0,1.0,0.0,
1.0,0.0,0.0,
};
glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, iva);
glDrawArrays(GL_POINTS, 0, 4);
}
#end
With this the buffer clears(to a grey colour), but nothing from the vertex array renders. I have no idea what to do from here and due to the age of the technology there is not much information available on how to properly use glkit.
I don't see anything in your setup code that loads your shaders - I presume you are doing this somewhere in your code?
In addition, in your setup code, you are creating your framebuffer. The GLKView does this for you - indeed you are telling the view to use a 24-bit depthbuffer in your viewDidLoad method:
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
So what your glkView:drawInRect: code above is doing is saying: "Bind my handmade framebuffer, and draw some stuff into it". The GLKView then automatically presents itself, but nothing has been drawn into it, you've only drawn into your handmade buffer. Unless you need additional framebuffer objects for tasks such as rendering to texture, then you don't need to concern yourself with framebuffer creation at all - let the GLKView do it automatically.
What you should be doing in your setupGL method (or anywhere you like in the setup) is creating your vertex array object(s) that remember the openGL state required to perform a draw. Then, in the glkView:drawInRect: method you should:
Clear using glClear().
Enable your program.
Bind the vertex array object (or, if you didn't use a VAO, enable
the appropriate vertex attrib pointers).
Draw your data using glDrawArrays() or glDrawElements().
The GLKView automatically sets its context as current, and binds its framebuffer object before each draw cycle.
Perhaps try to think of GLKView more like a regular UIView. It handles most of the openGL code behind the scenes for you, leaving you to simply tell it what it needs to draw. It has its drawRect: code just like a regular UIView - with a regular UIView in drawRect: you just tell it what it should draw, for example using Core Graphics functions - you don't then tell it to present itself.
The GLKViewController is then best thought of as handling the mechanics of the rendering loop behind the scenes. You don't need to implement the timers, or even worry about pausing the animation on your application entering the background. You just need to override the update or glkViewControllerUpdate: method (depending on whether you're subclassing or delegating) to update the state of the openGL objects or view matrix.
I made a post about the way to set up a basic project template using GLKit. You can find it here:
Steve Zissou's Programming Blog
I haven't used the GLKit yet, but it seems that you do not present your framebuffer after drawing into it.
In an application using OpenGL ES 2 under iOs but without GLKit, I use to call the following code at the end of the rendering loop.
if(context) {
[EAGLContext setCurrentContext:context];
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
As I said I haven't used GLKit yet so I hope this might be useful.
I think you forgot to call
[self.effect prepareToDraw];
just before
glDrawArrays(GL_POINTS, 0, 4);
As GLKit mimics the OpenGL ES 1.1 rendering pipeline, you do not need to include the routines to define Shader. GLKit actually does this for you, if you wish to use basic pipeline like OpenGL ES1.1

Resources