forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   C++ (http://forum.boolean.name/forumdisplay.php?f=22)
-   -   OpenGL PBO (http://forum.boolean.name/showthread.php?t=17760)

unself 16.01.2013 16:35

OpenGL PBO
 
Потребовалось написать приложение, которое максимально быстро сможет вывести массив пикселей на экран. Недолгие поиски привели меня к PBO. Примеров в интернете много, поэтому с этим трудностей не возникло. Приведу лишь некоторые основные методы:

Создание OpenGL контекста
Код:

void CreateGLContext(Window *window)
{
  //---------------------------------------------------------------------
  // Дескриптор контекста окна
  window->DC = GetDC(window->hWnd);
  if (  !window->DC)
  {
    exit(-1);
  }
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  PIXELFORMATDESCRIPTOR pfd =
  {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW |
    PFD_SUPPORT_OPENGL |
    PFD_DOUBLEBUFFER,        //Flags
    PFD_TYPE_RGBA,            //The kind of framebuffer. RGBA or palette.
    32,                      //Colordepth of the framebuffer.
    0, 0, 0, 0, 0, 0,
    0,
    0,
    0,
    0, 0, 0, 0,
    24,                      //Number of bits for the depthbuffer
    8,                        //Number of bits for the stencilbuffer
    0,                        //Number of Aux buffers in the framebuffer.
    0,
    0,
    0, 0, 0
  };

  // Если ни один из системных форматов не подходит или его невозможно применить к окн
  int pixelFormat = ChoosePixelFormat(window->DC, &pfd);
  if (  !pixelFormat ||
        !SetPixelFormat(window->DC, pixelFormat, &pfd))
  {
    exit(-1);
  }
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  // Создаем контекст воспроизведения OpenGL
  window->hGLRC = wglCreateContext(window->DC);
  // Устанавливаем его текущим для окна
  if (  !window->hGLRC ||
        !wglMakeCurrent(window->DC, window->hGLRC))
  {
    exit(-1);
  }
  //---------------------------------------------------------------------

  UpdateWindow(window->hWnd);
  ShowWindow(window->hWnd, SW_SHOWDEFAULT);
}



Установка ViewPort'a
Код:

void SetViewPort(int &w, int &h)
{
  glClearColor    ( 0.0, 0.0, 0.0, 1.0 );
  glViewport      (0, 0, w, h);
  glMatrixMode    (GL_PROJECTION);
  glLoadIdentity  ();
  glOrtho        (0, w, 0, h, -1.0f, 1.0f);
  //glEnable(GL_DEPTH_TEST);
}



После загрузки массива пикселей (32-битное изображение), я генерирую индексы, выделяю память в видеопамяти и устанавливаю параметры фильтрации / наложения
Код:

  // Генерируем индекс текстуры
  //---------------------------------------------------------------------
  glGenTextures(1, &surface->glTextureID);
  if (  !surface->glTextureID ) {
    FreeSurface(surface);
    return (nullptr);
  }
  //---------------------------------------------------------------------


  // PBOs is simply an array of bytes in memory
  //---------------------------------------------------------------------
  glGenBuffers(1, &surface->pboID);
  if (  !surface->pboID) {
    FreeSurface(surface);
    return (nullptr);
  }
  //---------------------------------------------------------------------

  // GL_PIXEL_PACK_BUFFER_ is for transferring pixel data from OpenGL
  // to your application, and GL_PIXEL_UNPACK_BUFFER_ means transferring
  // pixel data from an application to OpenGL
  //---------------------------------------------------------------------
  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pboID);  // GL_PIXEL_UNPACK_BUFFER

  // Allocate a memory space
  // glBufferData creates a new data store and copy pixel data for the buffer object currently bound to target
  glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->byteCount, (const void*)surface->pixels, GL_STREAM_DRAW);
  if (  glGetError() == GL_OUT_OF_MEMORY) {
    // GL is unable to create a data store with the specified size
    FreeSurface(surface);
    return (nullptr);
  }
  glReleaseBuffer();
  //---------------------------------------------------------------------
 
  //---------------------------------------------------------------------
  // Выбираем предварительно загруженное изображение
  glBindTexture    (GL_TEXTURE_2D, surface->glTextureID);

  // Установим параметры фильтрации
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  // Параметры наложения
  glTexParameterf  (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); // GL_CLAMP
  glTexParameterf  (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // GL_REPEAT
 
  //Constant GL_BGRA available if the GL version is 1.2 or greater
  glTexImage2D      (GL_TEXTURE_2D, 0, GL_RGBA, surface->width, surface->height, 0, surface->pixelFormat, surface->internalPixelFormat, nullptr);
 
  glReleaseTexture  ();



И сам метод отрисовки изображения
Код:

void DrawSurface(Surface *surface, iRectangle *target)
{
  //---------------------------------------------------------------------
  glEnable          (GL_TEXTURE_2D);
  glBindTexture    (GL_TEXTURE_2D, surface->glTextureID);
  // GL_PIXEL_PACK_BUFFER_ is for transferring pixel data from OpenGL
  // to your application, and GL_PIXEL_UNPACK_BUFFER_ means transferring
  // pixel data from an application to OpenGL
  glBindBuffer      (GL_PIXEL_UNPACK_BUFFER, surface->pboID); // GL_PIXEL_UNPACK_BUFFER_
                                                          // glBindBuffer()
  //---------------------------------------------------------------------

  // slowly then glMapBuffer
  //glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, surface->byteCount, surface->pixels);

  //glBufferData(GL_PIXEL_PACK_BUFFER, surface->byteCount, nullptr, GL_STREAM_DRAW);
  GLubyte* ptr =    (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); // GL_WRITE_ONLY_
  if(ptr)
  {
    // update data directly on the mapped buffer
    //---------------------------------------------------------------------
    memcpy(ptr, surface->pixels, surface->byteCount);
    //memset((unsigned char*)ptr, (unsigned char)0, surface->byteCount);
    glUnmapBuffer  (GL_PIXEL_UNPACK_BUFFER); // glUnmapBuffer
    //---------------------------------------------------------------------
  }

  //---------------------------------------------------------------------
  // Копируем данные с PBO
  glTexSubImage2D  (GL_TEXTURE_2D, 0, 0, 0, surface->width, surface->height, surface->pixelFormat, surface->internalPixelFormat, nullptr);
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  glBegin          (GL_QUADS);
  //---------------------------------------------------------------------
  glTexCoord2i      (0, 0); glVertex2i(target->x, target->y);
  glTexCoord2i      (1, 0); glVertex2i(target->x + target->w, target->y);
  glTexCoord2i      (1, 1); glVertex2i(target->x + target->w, target->y + target->h);
  glTexCoord2i      (0, 1); glVertex2i(target->x, target->y + target->h);
  //---------------------------------------------------------------------
  glEnd            ();
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  glReleaseBuffer  ();
  glReleaseTexture  ();
  glDisable        (GL_TEXTURE_2D);
  //---------------------------------------------------------------------
}


Для удобно доступа к отдельным пикселям изображения я определил для себя два макроса
Код:

#define GET_PIXEL(y, x)        *(src  + (y << img1->widthShift) + x)
#define SET_PIXEL(y, x, color)  *(dest + (y * img2->width) + x) = color

Вот тут я столкнулся с проблемой. Есть метод, который рендерит изображение и заносит пиксели в массив
Код:

void RenderImage3(Surface *img1, Surface *img2, Camera &cam)
{
  //---------------------------------------------------------------------
  unsigned int *src  = (unsigned int*)img1->pixels;
  unsigned int *dest  = (unsigned int*)img2->pixels;
  //---------------------------------------------------------------------

  /* Rendering ...*/

  unsigned int get = GET_PIXEL(((int)y0 & SRC_HEIGHT_MASK), ((int)x0 & SRC_WIDTH_MASK));
  SET_PIXEL((y + HALF_SCR_HEIGHT), (x + HALF_SCR_WIDTH), get);
}

При попытке измерить производительность, я получил FPS в районе 200 кадров



Поначалу меня это не смущало (грешил на неоптимизированный алгоритм), пока не подставил случайный цвет
Код:

void RenderImage3(Surface *img1, Surface *img2, Camera &cam)
{
  //---------------------------------------------------------------------
  unsigned int *src  = (unsigned int*)img1->pixels;
  unsigned int *dest  = (unsigned int*)img2->pixels;
  //---------------------------------------------------------------------

  /* Rendering ...*/

  unsigned int get = GET_PIXEL(((int)y0 & SRC_HEIGHT_MASK), ((int)x0 & SRC_WIDTH_MASK));
  SET_PIXEL((y + HALF_SCR_HEIGHT), (x + HALF_SCR_WIDTH), (unsigned int)0xFF00FF00);
}

И получил производительность более 1000 FPS



То есть, разница в скорости работы оказалось более чем в 5 раз, при том, что в первом случае, цвет пикселя берется из текстуры, а во втором, это случайно заданный цвет. В остальном это абсолютно одинаковые функции. Логически это объяснить сложно, складывается впечатление, что OpenGL придирчив к выбору цвета:-). Есть подозрения, что я не правильно установил параметры OpenGL. Куда дальше двигаться мне не ясно. Пожалуйста, помогите разобраться.

jimon 16.01.2013 19:34

Ответ: OpenGL PBO
 
unself
1) компилятор смотрит что переменная get не используется
2) компилятор смотрит что расчёт значения переменной get никак не влияет на состояние программы (нет сайд эффектов)
3) компилятор вырезает расчёт переменной get из программы

отсюда и буст - во втором случае нет чтения текстуры и установка цвета сильно оптимизируется

unself 16.01.2013 19:57

Ответ: OpenGL PBO
 
jimon, спасибо большое. Отключил оптимизацию в компиляторе и получил одинаковые результаты. Даже немного расстроился :)


Часовой пояс GMT +4, время: 04:55.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Перевод: zCarot