OpenGL: рендеринг видео с использованием PBO, создающий рывок

У меня есть набор RGB-фреймов, которые я показываю, используя концепцию PBO openGL. Ниже мой код:

#include <stdio.h>
#include <stdlib.h>
#include "glew.h"
#include "glfw.h"
#include "glaux.h"
#include "logodata.h"

PFNGLGENBUFFERSARBPROC pglGenBuffersARB = 0;                     // VBO Name Generation Procedure
PFNGLBINDBUFFERARBPROC pglBindBufferARB = 0;                     // VBO Bind Procedure
PFNGLBUFFERDATAARBPROC pglBufferDataARB = 0;                     // VBO Data Loading Procedure
PFNGLBUFFERSUBDATAARBPROC pglBufferSubDataARB = 0;               // VBO Sub Data Loading Procedure
PFNGLDELETEBUFFERSARBPROC pglDeleteBuffersARB = 0;               // VBO Deletion Procedure
PFNGLGETBUFFERPARAMETERIVARBPROC pglGetBufferParameterivARB = 0; // return various parameters of VBO
PFNGLMAPBUFFERARBPROC pglMapBufferARB = 0;                       // map VBO procedure
PFNGLUNMAPBUFFERARBPROC pglUnmapBufferARB = 0;                   // unmap VBO procedure
#define glGenBuffersARB           pglGenBuffersARB
#define glBindBufferARB           pglBindBufferARB
#define glBufferDataARB           pglBufferDataARB
#define glBufferSubDataARB        pglBufferSubDataARB
#define glDeleteBuffersARB        pglDeleteBuffersARB
#define glGetBufferParameterivARB pglGetBufferParameterivARB
#define glMapBufferARB            pglMapBufferARB
#define glUnmapBufferARB          pglUnmapBufferARB

int index;
int pboSupported;
int pboMode;
GLuint  pixBuffObjs[2];
HDC hDC = NULL;
GLuint  texture;
char *FileName;
unsigned char *guibuffer;
AUX_RGBImageRec texture1;
unsigned long long pos=0;
GLuint myPBO;
GLuint logoPBO;
unsigned char *logoBuff;

void initGL(void)
{
        int maxSz;
        int maxwidth = 416;
        int maxheight = 240;

        if( !glfwInit() )
        {
            exit( EXIT_FAILURE );
        }


        // if( !glfwOpenWindow(4096, 2118, 0,0,0,0,0,0, GLFW_WINDOW ) )
        if( !glfwOpenWindow(maxwidth, maxheight, 0,0,0,0,0,0, GLFW_WINDOW  ) ) //GLFW_FULLSCREEN
        {
            glfwTerminate();
            exit( EXIT_FAILURE );
        }

        glfwSetWindowTitle("sample");

        glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
        glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
        glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
        glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
        glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
        glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
        glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
        glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");

        // check once again PBO extension
        if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
           glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
        {
            pboSupported = 1;
            pboMode = 1;    // using 1 PBO
            printf( "Video card supports GL_ARB_pixel_buffer_object.");
            glGenBuffersARB(1, &pixBuffObjs[0]);
        }
        else
        {
            pboSupported = 0;
            pboMode = 0;    // without PBO
            printf("Video card does NOT support GL_ARB_pixel_buffer_object.");
        }

        glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSz);

        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // This Will Clear The Background Color To Black
        glClearDepth(1.0);                          // Enables Clearing Of The Depth Buffer
        glDepthFunc(GL_LESS);                       // The Type Of Depth Test To Do
        glEnable(GL_DEPTH_TEST);                    // Enables Depth Testing
        glShadeModel(GL_SMOOTH);                    // Enables Smooth Color Shading


        glMatrixMode(GL_PROJECTION);
        //glLoadIdentity();



        hDC= wglGetCurrentDC();
#if 1
        { // TSS
            HWND hCurrentWindow = GetActiveWindow();
            char szTitle[256]="sample";
            //SetWindowText(hCurrentWindow, );
            // SetWindowLongA (hCurrentWindow , GWL_STYLE, (GetWindowLongA (hCurrentWindow , GWL_STYLE) & ~(WS_CAPTION)));
            SetWindowLongA (hCurrentWindow, GWL_STYLE, (WS_VISIBLE));
        }
#endif
        glEnable(GL_TEXTURE_2D);
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

}

int GL_Disply()
{
    FILE *fptr=fopen("F:\\myRGBvideo.rgb","rb");
    fseek(fptr,pos,SEEK_SET);
    fread(guibuffer,sizeof(unsigned char),sizeof(unsigned char)*416*240*3,fptr);
    pos+=416*240*3;
    texture1.sizeX =416;
    texture1.sizeY =240;
    texture1.data = guibuffer;

    glDepthFunc(GL_ALWAYS);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDisable(GL_LIGHTING);

    //glEnable(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
#if 0
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture1.sizeX, texture1.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, guibuffer);
#else
    glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, myPBO);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture1.sizeX, texture1.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
#endif

    glBegin(GL_QUADS);

    //glNormal3f( 0.0f, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f,  0.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f,  0.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  0.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  0.0f);

    glEnd();

    //disp logo
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, LOGO_WIDTH, LOGO_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, logoBuff);

    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.8f,  0.8f,  0.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.97f,  0.8f,  0.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.97f, 0.97f,  0.0f);
    glTexCoord2f(0.0f, 0.0f);glVertex3f(0.8f, 0.97f,  0.0f);
    glEnd();

    glDisable(GL_BLEND); 
    glEnable(GL_DEPTH_TEST);

    // Swap front and back rendering buffers
    glfwSwapBuffers();
    //glDeleteTextures(1, &texture);
    fclose(fptr);

}
int main(int argc, char *argv[])
{
    initGL(); // GL initialization

#if 0
    /* CPU memory allocation using C - malloc */
    guibuffer=(unsigned char*)malloc(sizeof(unsigned char)*416*240*3);
#else
    /*GPU memory allocation using C*/
    glGenBuffersARB(1, &myPBO);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, myPBO);
    glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 416*240*3, NULL, GL_STREAM_DRAW_ARB);


#endif
        //Allocating memory for RGBA logo data present in logodata.h
    logoBuff=(unsigned char*)malloc(400*312*4*sizeof(unsigned char));
    memcpy(logoBuff,TELLogo_RGB,400*312*4);
    for(index=0;index<200;index++)
    {
        guibuffer=(unsigned char*)glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
        glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);

        printf("frame %d displayed\r",index);
        GL_Disply();

    }
    free(logoBuff);
    return 0;
}

Когда приведенный выше код запускается в системе, в которой ниже указаны данные графической карты, дисплей работает нормально и работает нормально:

Первая система:

Graphics card: Nvidia GEForce 580
OpenGL Version: 4.3

Вторая система:

Graphics card: NVidia GEForce 310
OpenGL Version: 3.3

Третья система:

Graphics card: MSI
OpenGL Version: 4.2

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

Graphics card: Intel HD Graphics 4000
openGL Version: 4.0

В чем может быть проблема, которая вызывает эту проблему?

2 ответа

Ваш код серьезно сломан. Если это работает вообще, то только случайно:

for(index=0;index<200;index++)
{
    guibuffer=(unsigned char*)glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
    glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);

    printf("frame %d displayed\r",index);
    GL_Disply();

}

Ваш код затем пишет в guibuffer в GL_Display, а затем пытается получить данные в PBO для обновления текстуры.

Вы должны знать, что после glUnmapBufferARB() указатель, полученный при сопоставлении, становится недействительным. Запись в это просто неопределенное поведение. Возможно, вам повезет, поскольку драйвер nvidia может использовать некоторую клиентскую память для буфера, а указатель случайно продолжает указывать на реальный буфер, но это будет более или менее неясным совпадением. Обратите внимание, что вы также не можете просто оставить отображенный буфер все время, так как пока он отображается, вы не можете использовать объект GL. По крайней мере, не без совершенно нового расширения GL_ARB_BUFFER_STORAGE (в ядре начиная с 4.4), и даже там, вам придется вручную синхронизировать доступ клиента и GL - и основная проблема останется той же, в любом случае.

Чтобы сделать это правильно, простейшим решением было бы сделать для каждого кадра следующее: отобразить буфер, прочитать файл в int, разархивировать его, обновить текстуру, нарисовать.

Тем не менее, это, вероятно, будет немного быстрее, чем вообще не использовать PBO, но производительность не будет оптимальной. Вы можете улучшить это, осиротев буфер, прежде чем отобразить его. Он сообщит GL о том, что вы больше не заботитесь о содержимом буфера (все ожидающие операции GL, работающие с этими данными, конечно же, будут завершены) и позволит GL немедленно предоставить новое хранилище, в то время как содержимое старого буфера могут быть использованы внутри, если они необходимы. Чтобы сделать сиротство, просто используйте glBufferData() с правильным размером и NULL как указатель данных снова. Сиротство также может быть сделано с использованием более современного (и на самом деле предпочтительного) glMapBufferRange() функция с GL_MAP_INVALIDATE_BUFFER_BIT бит доступа установлен. Посмотрите расширение GL_MAP_BUFFER_RANGE для деталей.

Другое решение для полностью асинхронных обновлений ресурсов - использование кольца PBO, чтобы вы могли загрузить следующий кадр в другое PBO, пока графический процессор все еще получает данные из текущего PBO. Это в основном будет работать как метод сиротства, но будет оказывать меньшее давление на GL и особенно на управление буферами, поэтому следует ожидать меньших накладных расходов (хотя это вряд ли будет актуально в вашем случае использования).

Помимо недостатков в вашей логике PBO, я хотел бы упомянуть еще несколько моментов:

  1. Ты используешь GL_ELEMENT_ARRAY_BUFFER, Вполне законно позже использовать этот буфер как GL_PIXEL_UNPACK_BUFFER, но GL может использовать эту информацию для оптимизации для ожидаемого использования (особенно путем выбора расположения буфера в VRAM или в памяти клиента).
  2. Вы сопоставляете свой буфер с GL_READ_WRITE где GL_WRITE_ONLY достаточно и может быть более эффективным путем (и фактически потребовалось бы для glMapBufferRange)
  3. Как уже указывал datenwolf в другом ответе, используйте glTexSubImage2D() вместо glTexImage2D() для обновления текстур.

В вашей функции отображения вы звоните glTexImage2D, Эта функция выполняет полное перераспределение / размещение объекта текстуры. В зависимости от деталей реализации драйвера это может вызвать внутреннюю сборку мусора, если со временем накопится слишком много объектов текстуры. Поскольку вы намереваетесь отобразить поток кадров одинакового формата и размера, вам просто нужно заменить данные текстуры, используя glTexSubImage2D вместо.

Вызов glTexImage2D только один раз, чтобы инициализировать объект текстуры, а затем использовать glTexSubImage2D загрузить новые данные. Чтобы упростить кодирование, инициализируйте объект текстуры, вызвав glTexImage2D с нулевым указателем для параметра данных, в то время как PBO не привязано.

Другие вопросы по тегам