2012-06-08 80 views
0

我在使用OpenGL組件在iPad上崩潰的應用程序遇到問題。該應用程序拋出內存警告並崩潰,但似乎沒有使用那麼多的內存。我錯過了什麼嗎?內存警告後在iOS上運行OpenGL崩潰

該應用程序基於Vuforia增強現實系統(借鑑ImageTargets示例)。我有約40種不同的模型需要包含在我的應用程序中,所以爲了節省內存,我在應用程序中動態加載對象(並渲染紋理等),因爲我需要它們。我試圖複製UIScrollView延遲加載的想法。這三個4mb分配是我已經加載到內存中的紋理,當用戶選擇不同的模型顯示時,這些紋理已準備好。

這裏有什麼奇怪的東西?

Instruments - leaks

我不知道很多在所有關於OpenGL的(爲什麼我選擇了Vurforia引擎的原因之一)。這個屏幕下面的任何內容應該關注我嗎?請注意,Vurforia的ImageTagets示例應用程序還具有未初始化紋理數據(每幀大約一個),所以我不認爲這是問題。

Instruments - OpenGL ES analyzer

任何幫助,將不勝感激!

這裏是生成的3D對象(在EAGLView)的代碼:

// Load the textures for use by OpenGL 
-(void)loadATexture:(int)texNumber { 

if (texNumber >= 0 && texNumber < [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 < [textures count]; ++i) { 

     if (i < targetIndex - 1 || i > targetIndex + 1) { 
      [textures replaceObjectAtIndex:i withObject:@""]; 
     } 
    } 


    // Render - Generate the OpenGL texture objects 
    GLuint nID; 
    Texture *texture = [textures objectAtIndex:texNumber]; 
    glGenTextures(1, &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 < [tempTextureList count]; ++i) { 

     if (i < targetIndex - 1 || i > targetIndex + 1) { 
      Object3D *obj3D = [[Object3D alloc] init]; 
      [objects3D replaceObjectAtIndex:i withObject:obj3D]; 
      [obj3D release]; 
     } 
    } 


    if (QCAR::GL_20 & qUtils.QCARFlags) { 
     [self initShaders]; 
    } 


    currentlyChangingTextures = NO; 
} 

}

這裏是代碼中的紋理對象。

- (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 < height; ++i) { 
    memcpy(pngData + rowSize * i, pixels + rowSize * (height - 1 - i), width * channels); 
} 

return YES; 
} 

回答

1

看到一些代碼會有所幫助,但我可以做一些gusses:

我有大約40種不同的型號,我需要在我的應用程序包括,所以在內存中保存的利益,我加載這些對象(以及渲染紋理等)在應用程序中動態顯示,因爲我需要它們。我試圖複製UIScrollView延遲加載的想法。這三個4mb分配是我已經加載到內存中的紋理,當用戶選擇不同的模型顯示時,這些紋理已準備好。 (...)

這種方法並不理想;如果內存沒有正確釋放,這很可能是你的問題的原因。最終,如果你沒有采取適當的預防措施,你的內存將耗盡,然後你的進程就會死亡。很可能您使用的引擎存在一些內存泄漏,並由您的訪問方案暴露。

今天的操作系統不區分內存和存儲。對他們來說,這只是內存,所有的地址空間都受到塊存儲系統的支持(如果實際上連接了一些存儲設備並不重要)。

所以這裏是你應該做的:代替閱讀 - 將你的模型存儲到內存中,你應該記憶它們的映射(mmap)。這告訴操作系統「這部分存儲應該在地址空間中可見」,並且操作系統內核將在它們到期時執行所有必要的傳輸。

請注意,Vurforia的ImageTagets示例應用程序還具有未初始化的紋理數據(每幀大約一個),所以我不認爲這是問題所在。

這是一個強有力的指標,即OpenGL紋理對象沒有被正確刪除。

任何幫助將不勝感激!

我的建議:像1970年代那樣停止編程。今天的電腦和操作系統的工作方式不同。另請參閱http://www.varnish-cache.org/trac/wiki/ArchitectNotes

+0

感謝您的輸入。思考的食物! –

2

可能性是,您沒有看到應用程序的真實內存使用情況。正如我在this answer中解釋的那樣,Allocations儀器隱藏了來自OpenGL ES的內存使用情況,因此您無法使用它來測量應用程序的大小。相反,使用Memory Monitor工具,我敢打賭,它會顯示你的應用程序使用的RAM比你想象的要多得多。這是人們在使用Instruments優化iOS上的OpenGL ES時碰到的常見問題。

如果您擔心內存中可能積聚了哪些對象或資源,可以使用分配工具的堆積鏡頭功能來標識在您的應用程序內執行重複任務時分配但從未刪除的特定資源。這就是我追蹤紋理和未正確刪除的其他項目的方式。

+0

謝謝你。 Textures.mm中的dealloc方法必須進行修改才能正確刪除紋理。 –