Карта плитки использует много ресурсов ЦП с OpenGL и SDL

Я работал над методом рисования карты на основе тайлов с OpenGL и SDL. И я, наконец, закодировал, но когда я запускаю базовую программу, где она рисует карту тайлов 25x16, и я проверяю использование ЦП, она говорит, что потребляет 25%, но без рисования карты потребляет намного 1% ЦП.

Итак, существует еще один способ нарисовать карту или почему так много ресурсов ЦП.

Это код для рисования карты.

void CMapManager::drawMap(Map *map)
{
    vector<ImagePtr> tempImages = CGameApplication::getInstance()->getGameApp()->getImages();
    GLuint texture = tempImages.at(1)->getTexture();

    glColor3f(1.0f, 1.0f, 1.0f);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindTexture( GL_TEXTURE_2D, texture );
    glBegin( GL_QUADS );

        for (int i = 0; i < map->getHeight(); i++)
        {
            for (int j = 0; j < map->getWidth(); j++)
            {
                ImagePtr imgDraw = tempImages.at(map->getMapTiles()[i][j]->getTypeTile());

                glTexCoord2i( 0, 0 );
                glVertex3f( imgDraw->getPosX() + (imgDraw->getWidth()*j), imgDraw->getPosY() + (imgDraw->getHeight()*i), 0.f );

                //Bottom-left vertex (corner)
                glTexCoord2i( 1, 0 );
                glVertex3f( imgDraw->getOffsetX() + (imgDraw->getWidth()*j), imgDraw->getPosY() + (imgDraw->getHeight()*i), 0.f );

                //Bottom-right vertex (corner)
                glTexCoord2i( 1, 1 );
                glVertex3f( imgDraw->getOffsetX() + (imgDraw->getWidth()*j), imgDraw->getOffsetY() + (imgDraw->getHeight()*i), 0.f );

                //Top-right vertex (corner)
                glTexCoord2i( 0, 1 );
                glVertex3f( imgDraw->getPosX() + (imgDraw->getWidth()*j),  imgDraw->getOffsetY() + (imgDraw->getHeight()*i), 0.f );
            }
        }       

    glEnd();

    glDisable(GL_BLEND);

}

И в этом методе я вызываю функцию:

void CGameApplication::renderApplication()
{       
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
    glEnable(GL_TEXTURE_2D);

    vector<ImagePtr> tempImages = GApp->getImages();
    vector<ImagePtr>::iterator iterImage;

    for (iterImage = tempImages.begin(); iterImage != tempImages.end(); ++iterImage)
    {
        CImageM->drawSprites( (*iterImage)->getTexture(), (*iterImage)->getPosX(), (*iterImage)->getPosY(), 
                            (*iterImage)->getOffsetX(), (*iterImage)->getOffsetY() );
    }

    vector<TextPtr> tempTexts = GApp->getTexts();
    vector<TextPtr>::iterator iterText;

    for (iterText = tempTexts.begin(); iterText != tempTexts.end(); ++iterText)
    {
        CTextM->drawFonts( (*iterText) );
    }

    CMapM->drawMap(GApp->getCurrentMap());

    glDisable(GL_TEXTURE_2D);

}

Я уже установил таймер, который после этой функции:

    GameApplication->getCKeyboardHandler()->inputLogic();
    GameApplication->renderApplication();

    SDL_GL_SwapBuffers();

    GameApplication->getGameApp()->getTimer()->delay();

И функция задержки:

void Timer::delay()
{
    if( this->getTicks() < 1000 / FRAMES_PER_SECOND )
    {
        SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - this->getTicks() );
    }
}

Константа FRAMES_PER_SECOND в данный момент равна 5.

И функция для преобразования изображения в текстуру GL:

GLuint CImageManager::imageToGLTexture(std::string name)
{
    GLuint texture;     
    SDL_Surface *surface;   
    GLenum texture_format;
    GLint  nOfColors;

    if ( (surface = IMG_Load(name.c_str())) ) { 

        // Check that the image's width is a power of 2
        if ( (surface->w & (surface->w - 1)) != 0 ) {
            printf("warning: image.bmp's width is not a power of 2\n");
        }

        // Also check if the height is a power of 2
        if ( (surface->h & (surface->h - 1)) != 0 ) {
            printf("warning: image.bmp's height is not a power of 2\n");
        }

        // get the number of channels in the SDL surface
        nOfColors = surface->format->BytesPerPixel;
        if (nOfColors == 4)     // contains an alpha channel
        {
            if (surface->format->Rmask == 0x000000ff)
                    texture_format = GL_RGBA;
            else
                    texture_format = GL_BGRA_EXT;
        } 

        else if (nOfColors == 3)     // no alpha channel
        {
            if (surface->format->Rmask == 0x000000ff)
                    texture_format = GL_RGB;
            else
                    texture_format = GL_BGR_EXT;
        } 

        else {
            printf("warning: the image is not truecolor..  this will probably break\n");
            // this error should not go unhandled
        }

        SDL_SetAlpha(surface, 0, 0);

        // Have OpenGL generate a texture object handle for us
        glGenTextures( 1, &texture );

        // Bind the texture object
        glBindTexture( GL_TEXTURE_2D, texture );

        // Set the texture's stretching properties
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

        // Edit the texture object's image data using the information SDL_Surface gives us
        glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
                              texture_format, GL_UNSIGNED_BYTE, surface->pixels );
    } 

    else {
        printf("SDL could not load the image: %s\n", SDL_GetError());
        SDL_Quit();
        exit(1);
    }    

    if ( surface ) { 
        SDL_FreeSurface( surface );
    }

    return texture;
}

Заранее спасибо за помощь.


person oscar.rpr    schedule 24.06.2011    source источник
comment
Это не так уж и плохо... попробуйте запустить другие 3D-приложения и посмотрите на загрузку процессора во время рендеринга.   -  person AJG85    schedule 24.06.2011
comment
Я запускаю некоторые базовые 3D-приложения и использую 20% ЦП, 15% ЦП, максимум 30% ЦП, но именно поэтому я беспокоюсь, что мое приложение 2D и оно еще не закончено, что это означает, что оно будет использовать немного больше ЦП . Так что это нормально?   -  person oscar.rpr    schedule 24.06.2011
comment
Преждевременная оптимизация — это дьявол. Проектируйте, кодируйте, тестируйте, а затем профилируйте, чтобы определить, какие области кода можно оптимизировать после завершения проекта.   -  person AJG85    schedule 24.06.2011
comment
Спасибо буду иметь это в виду.   -  person oscar.rpr    schedule 24.06.2011


Ответы (1)


В конце концов, избегайте изменений состояния. Объедините все свои плитки в одну текстуру и визуализируйте, используя только один блок glBegin/glEnd.

Если вы не хотите вносить много изменений, попробуйте отображать списки. OpenGL сможет оптимизировать ваши вызовы, но нет гарантии, что он будет работать намного быстрее.

Если ваша карта не сильно меняется, используйте VBO. Это самый быстрый способ.

person Piotr Praszmo    schedule 24.06.2011
comment
Но если я объединим все плитки в одну текстуру, мне придется регенерировать эту текстуру в каждом цикле, потому что некоторые плитки должны измениться. Например, если главный герой уничтожит элемент карты или что-то в этом роде, этот тайл он уже не отрисует, поэтому объединить все тайлы в одну текстуру будет дороговато??? или я ошибаюсь? ...... К сожалению, карта меняется почти постоянно, поэтому VBO или списки отображения мне не очень помогают. Спасибо за ответ. - person oscar.rpr; 24.06.2011
comment
@oscar.rpr: я имел в виду что-то вроде это . Все изображения находятся на одной текстуре, поэтому вам не нужно вызывать glBindTexture. Вместо этого используйте glTexCoord2i, чтобы выбрать правильную часть изображения. - person Piotr Praszmo; 24.06.2011
comment
Если меняются только текстуры, VBO не должны меняться вместе с ними, если только вы не обновляете UV с текстурами. По моему опыту, UV-обновления в целом будут самым быстрым способом анимации. Кроме того, если вы не меняете VBO каждой карты каждый тик, они, вероятно, все равно будут работать быстрее. Вы должны исследовать это. - person Michael Dorgan; 24.06.2011
comment
@oscar Он имеет в виду создание единой текстуры всех плиток, а затем индексирование этой текстуры, которую вы сохраняете загруженной, чтобы рисовать ее части. Вам нужно будет поддерживать какой-то массив индексов для позиций плитки для вашей карты. Изменение плиток для кратеров от взрывов или чего-то еще будет таким же простым, как обновление индекса в массиве карт. Эта концепция также хорошо работает для спрайтов, где все кадры анимации входят в одну текстуру, поэтому для бега, ходьбы, атаки, смерти и т. д. требуется только одна текстура. Рассмотрите также пул текстур, чтобы текстуры загружались только один раз и только тогда, когда это необходимо. - person AJG85; 24.06.2011
comment
@Banthar: я уже имел в виду, что есть набор плиток в одной текстуре и используйте glTexCoord, чтобы выбрать, какую плитку из набора рисовать. Но даже в этом случае я должен выполнять блок glBegin и glEnd каждый раз, когда рисую тайл, я прав?? @Michael Dorgan: текстуры плиток не изменятся, у меня будет текстура, в которой хранится набор плиток, поэтому сама карта не изменится, вместо этого, например, изменится вектор плиток. отрисовки в позиции матрицы [1][2] плитки с индексом 3, на следующей итерации в этой позиции будет отрисовываться плитка с индексом 4. - person oscar.rpr; 24.06.2011
comment
@oscar.rpr: Нет. Вам нужен один glBegin/glEnd для отрисовки всех ваших тайлов. Вы можете отправить более 4 вершин в паре начала/конца. При рендеринге с GL_QUADS каждый набор из 4 вершин будет рассматриваться как отдельный четырехугольник. Это значительно улучшит производительность. - person Nicol Bolas; 24.06.2011
comment
@Nicol Bolas: Итак, я мог бы сделать цикл внутри блока glBegin/glEnd для всех моих позиций вместо жесткого кодирования позиций плиток? Спасибо. - person oscar.rpr; 24.06.2011
comment
@oscar.rpr: Да, это идея. У меня есть ответ на очень похожий вопрос, который более подробно описан: stackoverflow.com/questions/6352235/ - person Nicol Bolas; 24.06.2011
comment
Итак, я пробую метод, который вы предлагаете, но если производительность не улучшится, я перейду на VBO?..... Спасибо. - person oscar.rpr; 24.06.2011
comment
В основном посте обновляю метод отрисовки тайловой карты, а тестил только с одной текстурой, заполняющей всю карту и производительность почти одинаковая, я что-то не так делаю? - person oscar.rpr; 24.06.2011
comment
@oscar.rpr Вы должны получить хоть какую-то прибавку. Вы уверены, что не делаете что-то, отнимающее много времени в другом месте? Попробуйте закомментировать только вызовы отрисовки gl* и посмотрите, насколько быстрее это станет. - person Piotr Praszmo; 24.06.2011
comment
@Banthar: комментирование функций рисования в renderApplication показывает использование ЦП на уровне 1%, когда я комментирую только функцию рисования карты, использование ЦП составляет максимум 2%. Но когда я выполняю все розыгрыши для карты, доходит до 25%. - person oscar.rpr; 24.06.2011
comment
Наконец нашел проблему, судя по всему была эта строка: map->getMapTiles() в функции drawMap. Поскольку эта функция вызывается на каждой итерации, профилировщик Visual Studio 2010 сообщает мне, что потребляет большую часть ЦП моего приложения. Поэтому я объявляю это в начале drawMap: std::vector‹std::vector‹TilePtr›› mapT = map-›getMapTiles(); и вызовы стека функций в этой части сведены к минимуму. Спасибо всем за помощь, и я надеюсь, что кто-то найдет это полезным. - person oscar.rpr; 24.06.2011