iOS OpenGL слишком медленный - PullRequest
0 голосов
/ 22 января 2012

Я новичок в программировании XCode и пытаюсь создать игру для iPhone с использованием OpenGL с поддержкой дисплея сетчатки со скоростью 60 FPS, но она работает слишком медленно. Я основал это на примере GLSprite на developer.apple. Я уже оптимизировал его, как мог, но он продолжает работать <30 FPS на симуляторе (я еще не тестировал его на реальном устройстве - может быть, он быстрее?). Узким местом является рисование полигонов - я использовал очень маленькие текстуры (256x256 PNG) и пиксельные форматы (RGBA4444); Я отключил смешивание; Я переместил весь код преобразования в фазу загрузки, надеясь на лучшую производительность; все безуспешно. Я сохраняю массив вершин, который хранит все для этого шага, а затем рисует, используя GL_TRIANGLES с одним вызовом функции - потому что я думаю, что это быстрее, чем вызов нескольких glDrawArrays. Он начинает отставать, когда я достигаю около 120 вершин (по 6 на каждый прямоугольный спрайт), но во многих местах, которые я читал, iPhone может обрабатывать даже миллионы вершин. Что не так с кодом ниже? Является ли OpenGL самым быстрым способом рендеринга графики на iPhone? Если нет, что еще я должен использовать? </p>

Код загрузки 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];

1 Ответ

7 голосов
/ 22 января 2012

Согласно руководству по программированию OpenGL для iOS :

Важно Производительность рендеринга OpenGL ES в симуляторе не имеет отношения к производительности OpenGL ES на реальном устройстве. Симулятор предоставляет оптимизированный программный растеризатор, который принимает Преимущество возможностей векторной обработки вашего Macintosh компьютер. В результате ваш код OpenGL ES может работать быстрее или медленнее в Симулятор iOS (в зависимости от вашего компьютера и того, что вы рисуете) чем на реальном устройстве. Всегда профилируйте и оптимизируйте ваш рисунок код на реальном устройстве и никогда не предполагайте, что симулятор отражает реальная производительность.

Симулятор не является надежным для профилирования производительности приложений OpenGL. Вам нужно будет запустить / profile на реальном оборудовании.

Это начинает отставать, когда я достигаю около 120 вершин (6 для каждого прямоугольный спрайт) но во многих местах я читал айфон можно обрабатывать даже миллионы вершин.

Чтобы уточнить этот комментарий: количество вершин - не единственная переменная, влияющая на производительность OpenGL. Например, с одним треугольником (3 вершинами) вы можете рисовать пиксели на всем экране. Это, очевидно, требует больше вычислений, чем рисование маленького треугольника, охватывающего всего несколько пикселей. Метрика, представляющая способность рисовать много пикселей, известна как fill-rate .

Если ваши вершины представляют большие треугольники на экране, вероятно, что скорость заполнения является узким местом вашей производительности, а не преобразованием вершин. Поскольку симулятор iOS использует программный растеризатор, хотя и оптимизируется, он, вероятно, медленнее, чем фактическое специализированное оборудование.

Вы должны профилировать свое приложение, чтобы знать, каково ваше реальное узкое место в производительности перед оптимизацией; этот документ может вам помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...