Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   forum.boolean.name > Программирование игр для компьютеров > C++

Ответ
 
Опции темы
Старый 16.01.2013, 16:35   #1
unself
AnyKey`щик
 
Регистрация: 30.12.2011
Сообщений: 12
Написано одно полезное сообщение
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. Куда дальше двигаться мне не ясно. Пожалуйста, помогите разобраться.

Последний раз редактировалось unself, 16.01.2013 в 18:47.
(Offline)
 
Ответить с цитированием
Старый 16.01.2013, 19:34   #2
jimon
 
Сообщений: n/a
Ответ: OpenGL PBO

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

отсюда и буст - во втором случае нет чтения текстуры и установка цвета сильно оптимизируется
 
Ответить с цитированием
Старый 16.01.2013, 19:57   #3
unself
AnyKey`щик
 
Регистрация: 30.12.2011
Сообщений: 12
Написано одно полезное сообщение
Ответ: OpenGL PBO

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


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


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


vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com