title: Crash running OpenGL on iOS after memory warning
body: <p>I am having trouble with an app with an OpenGL component crashing on iPad. The app throws a memory warning and crashes, but it doesn't appear to be using that much memory. Am I missing something?</p>
  
  <p>The app is based on the Vuforia augmented reality system (borrows heavily from the ImageTargets sample). I have about 40 different models I need to include in my app, so in the interests of memory conservation I am loading the objects (and rendering textures etc) dynamically in the app as I need them. I tried to copy the UIScrollView lazy loading idea. The three 4mb allocations are the textures I have loaded into memory ready for when the user selects a different model to display.</p>
  
  <p>Anything odd in here?</p>
  
  <p><img src="https://i.stack.imgur.com/hrw4P.jpg" alt="Instruments - leaks"></p>
  
  <p>I don't know much at all about OpenGL (part of the reason why I chose the Vurforia engine). Anything in this screen shot below that should concern me? Note that Vurforia's ImageTagets sample app also has Uninitialized Texture Data (about one per frame), so I don't think this is the problem.</p>
  
  <p><img src="https://i.stack.imgur.com/8yCB2.jpg" alt="Instruments - OpenGL ES analyzer"></p>
  
  <p>Any help would be appreciated!!</p>
  
  <p>Here is the code that generates the 3D objects (in EAGLView):</p>
  
  <pre><code>// Load the textures for use by OpenGL
  -(void)loadATexture:(int)texNumber {
  
  if (texNumber &gt;= 0 &amp;&amp; texNumber &lt; [tempTextureList count]) {
      currentlyChangingTextures = YES;
  
      [textureList removeAllObjects];
      [textureList addObject:[tempTextureList objectAtIndex:texNumber]];
  
      Texture *tex = [[Texture alloc] init];
      NSString *file = [textureList objectAtIndex:0];
  
      [tex loadImage:file];
  
  
  
      [textures replaceObjectAtIndex:texNumber withObject:tex];
      [tex release];
  
      // Remove all old textures outside of the one we're interested in and the two on either side of the picker.
      for (int i = 0; i &lt; [textures count]; ++i) {
  
          if (i &lt; targetIndex - 1 || i &gt; targetIndex + 1) {
              [textures replaceObjectAtIndex:i withObject:@""];
          }
      }
  
  
      // Render - Generate the OpenGL texture objects
      GLuint nID;
      Texture *texture = [textures objectAtIndex:texNumber];
      glGenTextures(1, &amp;nID);
      [texture setTextureID: nID];
      glBindTexture(GL_TEXTURE_2D, nID);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [texture width], [texture height], 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)[texture pngData]);
  
  
      // Set up objects using the above textures.
      Object3D *obj3D = [[Object3D alloc] init];
  
      obj3D.numVertices = rugNumVerts;
      obj3D.vertices = rugVerts;
      obj3D.normals = rugNormals;
      obj3D.texCoords = rugTexCoords;
  
      obj3D.texture = [textures objectAtIndex:texNumber];
  
      [objects3D replaceObjectAtIndex:texNumber withObject:obj3D];
      [obj3D release];
  
      // Remove all objects except the one currently visible and the ones on either side of the picker.
      for (int i = 0; i &lt; [tempTextureList count]; ++i) {
  
          if (i &lt; targetIndex - 1 || i &gt; targetIndex + 1) {
              Object3D *obj3D = [[Object3D alloc] init];
              [objects3D replaceObjectAtIndex:i withObject:obj3D];
              [obj3D release];
          }
      }
  
  
      if (QCAR::GL_20 &amp; qUtils.QCARFlags) {
          [self initShaders];
      }
  
  
      currentlyChangingTextures = NO;
  }
  </code></pre>
  
  <p>}</p>
  
  <p>Here is the code in the textures object.</p>
  
  <pre><code>- (id)init
  {
  self = [super init];
  pngData = NULL;
  
  return self;
  }
  
  
  - (BOOL)loadImage:(NSString*)filename
  {
  BOOL ret = NO;
  
  // Build the full path of the image file
  NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
  NSString* fullPath = [resourcePath stringByAppendingPathComponent:filename];
  
  // Create a UIImage with the contents of the file
  UIImage* uiImage = [UIImage imageWithContentsOfFile:fullPath];
  
  if (uiImage) {
      // Get the inner CGImage from the UIImage wrapper
      CGImageRef cgImage = uiImage.CGImage;
  
      // Get the image size
      width = CGImageGetWidth(cgImage);
      height = CGImageGetHeight(cgImage);
  
      // Record the number of channels
      channels = CGImageGetBitsPerPixel(cgImage)/CGImageGetBitsPerComponent(cgImage);
  
      // Generate a CFData object from the CGImage object (a CFData object represents an area of memory)
      CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
  
      // Copy the image data for use by Open GL
      ret = [self copyImageDataForOpenGL: imageData];
      CFRelease(imageData);
  }
  
  return ret;
  }
  
  
  - (void)dealloc
  {
  if (pngData) {
      delete[] pngData;
  }
  
  [super dealloc];
  }
  
  @end
  
  
  @implementation Texture (TexturePrivateMethods)
  
  - (BOOL)copyImageDataForOpenGL:(CFDataRef)imageData
  {    
  if (pngData) {
      delete[] pngData;
  }
  
  pngData = new unsigned char[width * height * channels];
  const int rowSize = width * channels;
  const unsigned char* pixels = (unsigned char*)CFDataGetBytePtr(imageData);
  
  // Copy the row data from bottom to top
  for (int i = 0; i &lt; height; ++i) {
      memcpy(pngData + rowSize * i, pixels + rowSize * (height - 1 - i), width * channels);
  }
  
  return YES;
  }
  </code></pre>