我是Xcode编程的新手,我尝试使用OpenGL创建iPhone游戏,支持60 FPS的视网膜显示,但运行速度太慢。我基于developer.apple上的GLSprite示例。我已经尽力优化了它,但它一直在运行<在模拟器上30 FPS(我还没有在真实设备上测试它 - 也许它更快?)。瓶颈似乎是绘制多边形 - 我使用了非常小的纹理(256x256 PNG)和像素格式(RGBA4444);我禁用混合;我已将所有转换代码移至加载阶段,希望获得更好的性能;一切都没有成功。我保留了一个存储该步骤所有内容的顶点数组,然后使用GL_TRIANGLES绘制一个函数调用 - 因为我认为它比调用多个glDrawArrays更快。当我达到大约120个顶点(每个矩形精灵6个)时,它开始滞后,但在很多地方,我已经读过iPhone甚至可以处理数百万个顶点。以下代码有什么问题? OpenGL是在iPhone上渲染图形的最快方式吗?如果没有,我还应该使用什么?
OpenGL加载代码,在开头只调用一次:
glViewport(0, 0, backingWidth, backingHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D,texture[0]); //Binds a texture loaded previously with the code given below
glVertexPointer(3, GL_FLOAT, 0, vertexes); //The array holding the vertexes
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, uvCoord); //The array holding the uv coordinates
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
纹理加载方法:
- (void)loadSprite:(NSString*)filename intoPos:(int)pos { //Loads a texture within the bundle, at the given position in an array storing all textures (but I actually just use one at a time)
CGImageRef spriteImage;
CGContextRef spriteContext;
GLubyte *spriteData;
size_t width, height;
// Sets up matrices and transforms for OpenGL ES
glViewport(0, 0, backingWidth, backingHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
// Clears the view with black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Sets up pointers and enables states needed for using vertex arrays and textures
glVertexPointer(2, GL_FLOAT, 0, spriteVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, spriteTexcoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Creates a Core Graphics image from an image file
spriteImage = [UIImage imageNamed:filename].CGImage;
// Get the width and height of the image
width = CGImageGetWidth(spriteImage);
height = CGImageGetHeight(spriteImage);
textureWidth[pos]=width;
textureHeight[pos]=height;
NSLog(@"Width %lu; Height %lu",width,height);
// Texture dimensions must be a power of 2. If you write an application that allows users to supply an image,
// you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2.
if(spriteImage) {
// Allocated memory needed for the bitmap context
spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
// Uses the bitmap creation function provided by the Core Graphics framework.
spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
// After you create the context, you can draw the sprite image to the context.
CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), spriteImage);
// You don't need the context at this point, so you need to release it to avoid memory leaks.
CGContextRelease(spriteContext);
// Use OpenGL ES to generate a name for the texture.
glGenTextures(1, &texture[pos]);
// Bind the texture name.
glBindTexture(GL_TEXTURE_2D, texture[pos]);
curTexture=pos;
if (1) { //This should convert pixel format
NSLog(@"convert to 4444");
void* tempData;
unsigned int* inPixel32;
unsigned short* outPixel16;
tempData = malloc(height * width * 2);
inPixel32 = (unsigned int*)spriteData;
outPixel16 = (unsigned short*)tempData;
NSUInteger i;
for(i = 0; i < width * height; ++i, ++inPixel32)
*outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, tempData);
free(tempData);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
}
// Set the texture parameters to use a minifying filter and a linear filer (weighted average)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Specify a 2D texture image, providing the a pointer to the image data in memory
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
// Release the image data
free(spriteData);
// Enable use of the texture
glEnable(GL_TEXTURE_2D);
// Set a blending function to use
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Enable blending
glEnable(GL_BLEND);
}
每个游戏循环调用的实际绘图代码:
glDrawArrays(GL_TRIANGLES, 0, vertexIndex); //vertexIndex is the maximum number of vertexes at this loop
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
答案 0 :(得分:7)
根据OpenGL programming guide for iOS:
重要在Simulator中渲染OpenGL ES的性能与实际设备上OpenGL ES的性能无关。 模拟器提供了一个优化的软件光栅化器 Macintosh的矢量处理功能的优势 电脑。因此,您的OpenGL ES代码可能会运行得更快或更慢 iOS模拟器(取决于您的计算机和您正在绘制的内容) 而不是在实际的设备上。始终分析和优化您的绘图 在真实设备上的代码,永远不要假设模拟器反映 现实世界的表现。
模拟器不能可靠地分析OpenGL应用程序的性能。您需要在真实硬件上运行/配置文件。
当我达到大约120个顶点(每个顶点6个)时,它开始滞后 矩形精灵),但在很多地方我都看过iPhone可以 处理甚至数百万个顶点。
详细说明你的这个评论:顶点的数量不是唯一影响OpenGL性能的变量。例如,只有一个三角形(3个顶点),你可以在整个屏幕上绘制像素。这显然比绘制仅覆盖几个像素的小三角形需要更多的计算。表示绘制多个像素的容量的度量标准称为 fill-rate 。
如果您的顶点在屏幕上表示大三角形,则填充率可能是您的性能瓶颈,而不是顶点变换。由于iOS模拟器确实使用了软件光栅化器,尽管经过优化,它可能比实际的专用硬件慢。
您应该在优化之前分析您的应用程序,以了解您的实际性能瓶颈是什么; this document可以帮助您。