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.
|