# OpenGL ES 1.1 strange slow drawing by zooming on iPhone

I have created a view (derived from UIScrollView) where the cells are drawn and zoom occurs. Everything is drawn with OpenGL ES 1.1. Zoom is specified by adding of a calculated new distance which is passed by fingers (sliding apart or moving together), which is multiplied by a constant coefficient:

fNewZoom += fDistance * fCoef;


fDistance may have negative value at minimization

There are 4 arrays of cells, each array contains cells with definite size. At zooming the most optimal cells by size are chosen for drawing and are drawn with the OpenGL ES 1.1. The initialization of all four textures takes place at the start while initialization of the drawing view.

Initialization of the view:

+ (Class) layerClass
{
return [CAEAGLLayer class];
}

- (id)initWithCoder:(NSCoder*)coder
{
if((self = [super initWithCoder:coder]))
{
CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;

eaglLayer.opaque = NO;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if(!m_context || ![EAGLContext setCurrentContext:m_context] || ![self createFramebuffer])
{
[self release];
return nil;
}

[self InitGLTextures];
}

return self;
}

Drawing specifically in function "- (void)drawView":

- (void)layoutSubviews
{
[self InvalidateView];
}

- (void)InvalidateView
{
[EAGLContext setCurrentContext:m_context];
[self destroyFramebuffer];
[self createFramebuffer];
[self drawView];             //drawing view
}

- (BOOL)createFramebuffer
{
glGenFramebuffersOES(1, &m_viewFramebuffer);
glGenRenderbuffersOES(1, &m_viewRenderbuffer);

glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_viewRenderbuffer);
[m_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, m_viewRenderbuffer);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &m_nBackingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &m_nBackingHeight);

if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
{
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}

return YES;
}

- (void)destroyFramebuffer
{
glDeleteFramebuffersOES(1, &m_viewFramebuffer);
m_viewFramebuffer = nil;
glDeleteRenderbuffersOES(1, &m_viewRenderbuffer);
m_viewRenderbuffer = nil;
}

//TEST DRAWING VIEW !!!!
- (void)drawView
{
const int       nClues = 3; // = 900
const ImgElDimensionsF  cellDimensions = _skinMgr.GetCellInfo();
const CGPoint       offset = [self contentOffset];

float   fDrawX = 0;
float   fDrawY = 0;
int nCount1 = 1;

[self BeginDrawing];
//
for (int nRow = 0; nRow < nClues; nRow++)
{
for (int nColl = 0; nColl < nClues; nColl++)
{
fDrawX += cellDimensions.fWidth;
}

fDrawX = 0;
fDrawY += cellDimensions.fHeight;
}
//
[self EndDrawing];
}


functions used for drawing:

- (void)BeginDrawing
{
m_pCellTextureArr = _skinMgr.GL_GetProperlyCellTextInfo();

glViewport(0, 0, m_nBackingWidth, m_nBackingHeight);
glMatrixMode(GL_PROJECTION);
glOrthof(0.0f, (GLfloat)m_nBackingWidth, 0.0f, (GLfloat)m_nBackingHeight, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
// Clears the view with black
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

// Make sure that you are drawing to the current context
[EAGLContext setCurrentContext:m_context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_viewFramebuffer);
glClear(GL_COLOR_BUFFER_BIT);
//correct matrix pos
glTranslatef(0, (CGFloat)m_nBackingHeight, 0);

}

- (void)EndDrawing
{
ImgSize curImgSize = _skinMgr.GetCurrentImgSize();

glBindTexture(GL_TEXTURE_2D, m_cellsTextureArr[curImgSize]);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);

{
glVertexPointer(2, GL_FLOAT, 0, m_spriteVerticesBack);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, m_spriteTexcoordsBack);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//
}
}


With the help of this function the coordinate array of texture vertices for drawing is formed where m_spriteVerticesBack and m_spriteTexcoordsBack are dynamic arrays, seleсted by means of "new" function (m_spriteVerticesBack = new float[MAX_COUNT_CELLS * 12] )

- (BOOL)AddNewCellType:(ImgCellType)cellType PosX:(float)posX PosY:(float)posY
{
ImgElDimensionsF    cellDimensions = _skinMgr.GL_GetCurrerntCellDimensions(cellType);
ImgTextureInfoF     imgCellInfo = m_pCellTextureArr[cellType];

return FALSE;

const int idx = m_nCurAddBackCell * ELEMENTS_PER_CELL;
//////////
//*-------
//|  3--4
//|  |  |
//|  1--2
//*
// 1X
m_spriteVerticesBack[idx + 0] = posX;
// 1Y
m_spriteVerticesBack[idx + 1] =  -posY - cellDimensions.fHeight;
// 2X
m_spriteVerticesBack[idx + 2] = posX + cellDimensions.fWidth;
// 3Y
m_spriteVerticesBack[idx + 3] = -posY - cellDimensions.fHeight;
// 3X
m_spriteVerticesBack[idx + 4] = posX;
// 3Y
m_spriteVerticesBack[idx + 5] = -posY;
// 2X
m_spriteVerticesBack[idx + 6] = posX + cellDimensions.fWidth;
// 3Y
m_spriteVerticesBack[idx + 7] = -posY - cellDimensions.fHeight;
// 3X
m_spriteVerticesBack[idx + 8] = posX;
// 3Y
m_spriteVerticesBack[idx + 9] = -posY;
// 4X
m_spriteVerticesBack[idx + 10] = posX + cellDimensions.fWidth;
// 4Y
m_spriteVerticesBack[idx + 11] = -posY;

//////////
//1--2
//|  |
//3--4
//
// 1X
m_spriteTexcoordsBack[idx + 0] = imgCellInfo.fX;
// 1Y
m_spriteTexcoordsBack[idx + 1] = imgCellInfo.fY + imgCellInfo.fHeight;
// 2X
m_spriteTexcoordsBack[idx + 2] = imgCellInfo.fX + imgCellInfo.fWidth;
// 2Y
m_spriteTexcoordsBack[idx + 3] = imgCellInfo.fY + imgCellInfo.fHeight;
// 3X
m_spriteTexcoordsBack[idx + 4] = imgCellInfo.fX;
// 3Y
m_spriteTexcoordsBack[idx + 5] = imgCellInfo.fY;
// 2X
m_spriteTexcoordsBack[idx + 6] = imgCellInfo.fX + imgCellInfo.fWidth;
// 2Y
m_spriteTexcoordsBack[idx + 7] = imgCellInfo.fY + imgCellInfo.fHeight;
// 3X
m_spriteTexcoordsBack[idx + 8] = imgCellInfo.fX;
// 3Y
m_spriteTexcoordsBack[idx + 9] = imgCellInfo.fY;
// 4X
m_spriteTexcoordsBack[idx + 10] = imgCellInfo.fX + imgCellInfo.fWidth;
// 4Y
m_spriteTexcoordsBack[idx + 11] = imgCellInfo.fY;
//
return TRUE;
}


The problem is that indifferently how many cells I draw - 9 (field size 3 х 3) or 900 (field size 30 х 30), the speed of drawing at zoom on (iPhone 2G) is the same!!! What decelerates drawing? Drawing is performed like jerking. What was my mistake? What have I omited? I want the increment or minimization to become gradually, how can I achieve that?

• Just as an FYI -- you should make your call to EAGLContext setCurrentContext: before any of your GL calls. You are currently doing in the middle of your BeginDrawing method. Also, if you are only using one EAGLContext for the app, you can simply set it once after it is created and never call it again as long as it remains valid. – Dennis Munsie May 16 '11 at 16:34
• One more FYI -- glBindFramebufferOES() only needs to be called once per context, so you shouldn't be calling it once per frame. – Dennis Munsie May 16 '11 at 16:41

I don't see in your code where you are calling drawRect: (also related, you have it in your example as drawView: -- the method is drawRect:) explicitly, which leads me to believe that you are letting the OS call you when it thinks it is needed. By default, UIViews will keep the contents of the backing CALayer in memory, so when the OS needs to scroll your view around, it simply blits the contents of the CALayer again instead of calling drawView:. What you should be doing is calling setNeedsDisplay anytime you have determined that you need to zoom or change the contents. This tells the OS that it should be call drawRect: at the next chance it gets.

Also, you should not extend UIScrollView for this. You should extend UIView and then place that view inside of a UIScrollView. That is how the API was intended to work. You are basically mucking around the innards of the UIScrollView and replacing it's CALayer with a CAEAGLLayer, which is not what it is expecting. Even if it works now, it may be broken with a future SDK release.

My recommendation for now would be to get it working properly without the UIScrollView, just to make sure you have everything in order. Once you get that working, drop it back inside a UIScrollView and make sure it still works.

I'm going to go out on a limb and say that the problem might be related to your view deriving from UIScrollView that you're rendering into. You should be handling scrolling yourself inside a stationary EAGLView.

• Depending on what version of iOS he is running on, the UIScrollView probably isn't having an effect. For simple scenes like this (looks like about 18 triangles), it's not going to matter. – Dennis Munsie May 16 '11 at 16:39

I'm going to go out on a limb and say that the problem might be related to your view deriving from UIScrollView that you're rendering into. You should be handling scrolling yourself inside a stationary EAGLView.

I have written earlier that the same situation appears when I use zoom in EAGLView which is derived from UIView (like is shown in many examples of iPhone Developer SDK documentation). Zoom speed is identical, it is very low and it does not depend on how many squares i draw, 9 or 900!!! So what can I do to make zoom bumpless and quick? Can you give any code example or any of such examples in the Internet?