I am rendering a scene that has to get rebuilt whenever there is an orientation change in order to fill the screen appropriately. The scene renders a few vertex array objects of regular colored quads and an array of textured vertex array objects. All of these drawing elements including any shaders and associated arrays of vertices, indices, etc are included in a class, so I just blow away the class each time and do a new alloc/init on rotation.
When the scene is first created, everything looks fine. However for some reason and in no discernible pattern, weird triangles may show up on any subsequent rotation. For a particular scene, the occurrence seems to be deterministic every time it is loaded. However, from scene to scene, sometimes the artifacts show up, and sometimes everything looks lovely.
I can't figure out whether it is how I am breaking my memory down (even in ARC, I need to release some of it) or if it is how I am creating my VAOs. However, since the problem only occurs when I tearDown the scene and rebuild it... and then I start spinning my wheels again with that thought process.
I have quite a bit of code so here is some (hopefully) relevant code.
Here is some drawing code for colored quad VAO. I'm adding vertices to an NSMutableArray because I don't know how many quads there will be.
- (void) buildNode : (NSDictionary *) nodeDictionary
bounds : (BoundsGL) bounds
{
if (!nodeDictionary)
{
return;
}
NSString *jobidstr = [nodeDictionary objectForKey:kNodeJobidKey];
NSInteger jobid = jobidstr == NULL ? -1 : [jobidstr intValue];
NSString *colorHexStr = [nodeDictionary objectForKey:kNodeColorKey];
ColorGL color = HexidecimalStringToColorGL(colorHexStr);
//
// indices
[_coloredQuadIndices addObject:[NSNumber numberWithInt:_coloredQuadVertices.count]];
[_coloredQuadIndices addObject:[NSNumber numberWithInt:_coloredQuadVertices.count+1]];
[_coloredQuadIndices addObject:[NSNumber numberWithInt:_coloredQuadVertices.count+2]];
[_coloredQuadIndices addObject:[NSNumber numberWithInt:_coloredQuadVertices.count+2]];
[_coloredQuadIndices addObject:[NSNumber numberWithInt:_coloredQuadVertices.count+1]];
[_coloredQuadIndices addObject:[NSNumber numberWithInt:_coloredQuadVertices.count+3]];
//
// vertices
GLfloat x2 = bounds.pos.x + bounds.size.w;
GLfloat y2 = bounds.pos.y + bounds.size.h;
PointGL blp = {bounds.pos.x, bounds.pos.y};
PointGL brp = {x2, bounds.pos.y};
PointGL tlp = {bounds.pos.x, y2};
PointGL trp = {x2, y2};
ColoredVertex bl = { .pos = blp, .color = color };
ColoredVertex br = { .pos = brp, .color = color };
ColoredVertex tl = { .pos = tlp, .color = color };
ColoredVertex tr = { .pos = trp, .color = color };
NSValue *tlv = [NSValue valueWithBytes:&tl objCType:@encode(ColoredVertex)];
NSValue *blv = [NSValue valueWithBytes:&bl objCType:@encode(ColoredVertex)];
NSValue *trv = [NSValue valueWithBytes:&tr objCType:@encode(ColoredVertex)];
NSValue *brv = [NSValue valueWithBytes:&br objCType:@encode(ColoredVertex)];
[_coloredQuadVertices addObject:tlv];
[_coloredQuadVertices addObject:blv];
[_coloredQuadVertices addObject:trv];
[_coloredQuadVertices addObject:brv];
}
some code to create the VAO for textured quads:
#import "TexturedQuadVAO.h"
#import "Texture.h"
#import "ShaderUtil.h"
@interface TexturedQuadVAO()
{
GLuint _textureUniformLocation;
}
@property (strong, nonatomic) Texture *texture;
@end
@implementation TexturedQuadVAO
@synthesize vaoid = _vaoid;
@synthesize vertexBuffer = _vertexBuffer;
@synthesize vcount = _vcount;
@synthesize vsize = _vsize;
@synthesize indexBuffer = _indexBuffer;
@synthesize icount = _icount;
@synthesize isize = _isize;
@synthesize texture = _texture;
- (id) initWithData : (TexturedVertex *) vertices
numberOfVertices : (NSUInteger) vcount
verticesSize : (size_t) vsize
indices : (GLushort *) indices
numberOfIndices : (NSUInteger) icount
indicesSize : (size_t) isize
viewSize : (CGSize) viewSize
text : (NSString *) text
textureSize : (SizeGL) textureSize
foregroundColor : (UIColor *) fgcolor
backgroundColor : (UIColor *) bgcolor
textureUniformLocation : (GLuint) textureUniformLocation
{
self = [super init];
if (self)
{
//
// geometry
self.vcount = vcount;
self.vsize = vsize;
self.icount = icount;
self.isize = isize;
//
// texture
self.texture = [[Texture alloc] init];
UIFont *font = [UIFont fontWithName:@"Arial" size:24];
SizeGL tSize = {textureSize.w * 2.5, textureSize.h * 2.5};
UIImage *img = createTextImage(viewSize, text, tSize, font, fgcolor, bgcolor);
[self.texture setupTextureWithImage:img];
//
// for shaders
_textureUniformLocation = textureUniformLocation;
[self createVertexArrayObject:vertices indices:indices];
}
return self;
}
- (void) createVertexArrayObject : (TexturedVertex *) vertices
indices : (GLushort *) indices
{
checkGLError(@" TexturedQuadVAO createVertexArrayObject ");
// vertex array object
glGenVertexArraysOES(1, &_vaoid);
glBindVertexArrayOES(_vaoid);
//
// vertices buffer
GLuint vb;
glGenBuffers(1, &vb);
self.vertexBuffer = vb;
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, self.vsize, vertices, GL_STATIC_DRAW); // copy data into buffer object
// position vertex attribute
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (const GLvoid *) offsetof(TexturedVertex, pos));
// color vertex attribute
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TexturedVertex), (const GLvoid *) offsetof(TexturedVertex, color));
// textures vertex attribute
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (const GLvoid *) offsetof(TexturedVertex, texcoord));
//
// index data buffer
GLuint ib;
glGenBuffers(1, &ib);
self.indexBuffer = ib;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.isize, indices, GL_STATIC_DRAW); // copy index data into buffer object
//
// clear binds
glBindVertexArrayOES(0); // ok to unbind for now, and bind when we want to render
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
checkGLError(@"2 TexturedQuadVAO createVertexArrayObject ");
}
- (void) render
{
checkGLError(@" TexturedQuadVAO ");
glBindVertexArrayOES(_vaoid);
checkGLError(@"2 TexturedQuadVAO ");
glActiveTexture(GL_TEXTURE0);
checkGLError(@"3 TexturedQuadVAO ");
[self.texture bind];
checkGLError(@"4 TexturedQuadVAO ");
glUniform1i(_textureUniformLocation, 0);
checkGLError(@"5 TexturedQuadVAO ");
//glDrawElements(GL_TRIANGLE_STRIP, self.vsize/sizeof(GLshort), GL_UNSIGNED_SHORT, 0);
glDrawElements(GL_TRIANGLE_STRIP, self.vsize/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
checkGLError(@"6 TexturedQuadVAO ");
[self.texture unbind];
checkGLError(@"7 TexturedQuadVAO ");
glBindVertexArrayOES(0);
checkGLError(@"8 TexturedQuadVAO ");
}
- (void) tearDown
{
[self.texture deleteTexture];
glDeleteBuffers(1, &_vertexBuffer);
glDeleteBuffers(1, &_indexBuffer);
glDeleteVertexArraysOES(1, &_vaoid);
}
@end
my teardown code:
- (void) tearDown
{
// VAOs
for (int i=0; i<_texturedQuadArray.count; i++)
{
TexturedVAO *vao = [_texturedQuadArray objectAtIndex:i];
[vao tearDown];
vao = nil;
}
[_texturedQuadArray removeAllObjects];
if (_coloredQuadVAO)
{
[_coloredQuadVAO tearDown];
_coloredQuadVAO = nil;
}
if (_coloredQuadOutlinesVAO)
{
[_coloredQuadOutlinesVAO tearDown];
_coloredQuadOutlinesVAO = nil;
}
// shaders
if (_shaders.colorShader)
{
glDeleteProgram(_shaders.colorShader);
_shaders.colorShader = 0;
}
if (_shaders.textureShader)
{
glDeleteProgram(_shaders.textureShader);
_shaders.textureShader = 0;
}
checkGLError(@"Main tearDown");
}
I have scoured the web and tried everything I could think of to ferret out the issue for the last several days and it is starting to become an enormous bummer. Any ideas would be very much appreciated!
Update: I found the problem!
As I had suspected, it was something incredibly trivial that I spent so many days trying to find - in all the wrong places!
The culprit was this line:
glDrawElements(GL_TRIANGLE_STRIP, self.vsize/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
I glazed over this line a zillion times before I noticed this messed up calculation and immediately changed it to:
glDrawElements(GL_TRIANGLE_STRIP, self.icount, GL_UNSIGNED_SHORT, 0);
i.e. I changed to count to the actual indice count.
As I had thought, it was something incredibly trivial that I spent so many days trying to find - in all the wrong places!
The culprit was this line in the 2nd group of code for TexturedQuadVAOs:
glDrawElements(GL_TRIANGLE_STRIP, self.vsize/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
I glazed over this line a zillion times before I noticed this messed up calculation and immediately changed it to:
glDrawElements(GL_TRIANGLE_STRIP, self.icount, GL_UNSIGNED_SHORT, 0);
i.e. I changed to count to the actual indice count.