Hi there,
i’m trying to use OpenGL for video frame processing (inside a filter for frameserving). For this purpose, i wrote following class for an offscreen OpenGL context on windows:
OGLContext.h
#pragma once
#include <GLEW/glew.h>
#include <GLEW/wglew.h>
#include <GL/glu.h>
#include <string>
class OGLContext
{
public:
OGLContext(unsigned int, unsigned int, GLenum, unsigned char);
~OGLContext();
void Activate(bool);
void ReadPixels(GLubyte*);
void DrawPixels(GLubyte*);
private:
// Windows resources
std::wstring inst_name; // Unique class name
HWND hwnd;
HDC hdc;
HGLRC ctx;
// FBO resources
GLuint tex_color, fbo_transfer, rbo_color, rbo_depth_stencil, fbo_render;
// Context
unsigned int width, height;
GLenum colorspace;
};
OGLContext.cpp
#include "OGLContext.h"
#include "resources.h" // holds DLL module handle 'void *dll_module'
//Window callback
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){return DefWindowProc(hwnd, msg, wParam, lParam);}
// Create and activate OpenGL context
OGLContext::OGLContext(unsigned int width, unsigned int height, GLenum colorspace, unsigned char antiAliasing) : width(width), height(height), colorspace(colorspace){
// Find unique instance name
WNDCLASSEX wcx;
this->inst_name = L"YoukaOffscreen00";
for(unsigned char i = 0; i <= 100; i++){
if(i == 100)
throw "Cannot use more than 100 instances!";
inst_name[14] = 48 + (i/10);
inst_name[15] = 48 + (i%10);
if(!GetClassInfoEx(reinterpret_cast<HINSTANCE>(dll_module), this->inst_name.c_str(), &wcx))
break;
}
// Window class
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_OWNDC;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = reinterpret_cast<HINSTANCE>(dll_module);
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wcx.lpszMenuName = NULL;
wcx.lpszClassName = this->inst_name.c_str();
wcx.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
RegisterClassEx(&wcx);
// Create window
this->hwnd = CreateWindowEx(0, this->inst_name.c_str(), this->inst_name.c_str(), WS_POPUP, 0, 0, this->width, this->height, NULL, NULL, reinterpret_cast<HINSTANCE>(dll_module), 0);
// Get window context
this->hdc = GetDC(this->hwnd);
// Set window context pixel format
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cRedBits = 8;
pfd.cGreenBits = 8;
pfd.cBlueBits = 8;
pfd.cAlphaBits = 8;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pformat = ChoosePixelFormat(this->hdc, &pfd);
if(!SetPixelFormat(this->hdc, pformat, &pfd)){
ReleaseDC(this->hwnd, this->hdc);
DestroyWindow(this->hwnd);
UnregisterClass(this->inst_name.c_str(), reinterpret_cast<HINSTANCE>(dll_module));
throw "Couldn't find a fitting pixel format!";
}
// Create OGL context
this->ctx = wglCreateContext(this->hdc);
// Initialize glew for OpenGL >1.1 and check needed version & extensions
this->Activate(true);
if(glewInit() || !GLEW_VERSION_2_1 || !GLEW_ARB_framebuffer_object){
this->Activate(false);
wglDeleteContext(this->ctx);
ReleaseDC(this->hwnd, this->hdc);
DestroyWindow(this->hwnd);
UnregisterClass(this->inst_name.c_str(), reinterpret_cast<HINSTANCE>(dll_module));
throw "Couldn't initialize GLEW or OpenGL 2.1 & ARB_framebuffer_object isn't supported!";
}
// Create transfer FBO
glGenTextures(1, &this->tex_color); // Color
glBindTexture(GL_TEXTURE_2D, this->tex_color);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this->width, this->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &this->fbo_transfer); // Attach
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo_transfer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->tex_color, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &this->fbo_transfer);
glDeleteTextures(1, &this->tex_color);
this->Activate(false);
wglDeleteContext(this->ctx);
ReleaseDC(this->hwnd, this->hdc);
DestroyWindow(this->hwnd);
UnregisterClass(this->inst_name.c_str(), reinterpret_cast<HINSTANCE>(dll_module));
throw "Bad framebuffer status!";
}
// Create render FBO
glGenRenderbuffers(1, &this->rbo_color); // Color
glBindRenderbuffer(GL_RENDERBUFFER, this->rbo_color);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, antiAliasing, GL_RGBA, this->width, this->height);
glGenRenderbuffers(1, &this->rbo_depth_stencil); // Depth & stencil
glBindRenderbuffer(GL_RENDERBUFFER, this->rbo_depth_stencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, antiAliasing, GL_DEPTH_STENCIL, this->width, this->height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &this->fbo_render); // Attach
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo_render);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, this->rbo_color);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->rbo_depth_stencil);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &this->fbo_render);
glDeleteRenderbuffers(1, &this->rbo_color);
glDeleteRenderbuffers(1, &this->rbo_depth_stencil);
glDeleteFramebuffers(1, &this->fbo_transfer);
glDeleteTextures(1, &this->tex_color);
this->Activate(false);
wglDeleteContext(this->ctx);
ReleaseDC(this->hwnd, this->hdc);
DestroyWindow(this->hwnd);
UnregisterClass(this->inst_name.c_str(), reinterpret_cast<HINSTANCE>(dll_module));
throw "Bad framebuffer status!";
}
// All done; deactivate context for now
this->Activate(false);
}
// Deactivate and destroy OpenGL context
OGLContext::~OGLContext(){
// Free FBOs
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &this->fbo_render);
glDeleteRenderbuffers(1, &this->rbo_color);
glDeleteRenderbuffers(1, &this->rbo_depth_stencil);
glDeleteFramebuffers(1, &this->fbo_transfer);
glDeleteTextures(1, &this->tex_color);
// Free OGL context
this->Activate(false);
wglDeleteContext(this->ctx);
// Free window context
ReleaseDC(this->hwnd, this->hdc);
// Free window
DestroyWindow(this->hwnd);
// Unregister window class
UnregisterClass(this->inst_name.c_str(), reinterpret_cast<HINSTANCE>(dll_module));
}
// (De)Activates OpenGL context for current thread
void OGLContext::Activate(bool active){
if(active)
wglMakeCurrent(this->hdc, this->ctx);
else
wglMakeCurrent(this->hdc, NULL);
}
// Reads image from framebuffer
void OGLContext::ReadPixels(GLubyte *image){
glBindFramebuffer(GL_READ_FRAMEBUFFER, this->fbo_render);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->fbo_transfer);
glBlitFramebuffer(0, 0, this->width, this->height, 0, 0, this->width, this->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo_render);
glBindTexture(GL_TEXTURE_2D, this->tex_color);
glGetTexImage(GL_TEXTURE_2D, 0, this->colorspace, GL_UNSIGNED_BYTE, image);
glBindTexture(GL_TEXTURE_2D, 0);
}
// Sends image to framebuffer
void OGLContext::DrawPixels(GLubyte *image){
glBindTexture(GL_TEXTURE_2D, this->tex_color);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, this->width, this->height, this->colorspace, GL_UNSIGNED_BYTE, image);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, this->fbo_transfer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->fbo_render);
glBlitFramebuffer(0, 0, this->width, this->height, 0, 0, this->width, this->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo_render);
}
It’s important for me to render with multisampling and having a good performance. Regrettably, pixel transfer by member functions ReadPixels and DrawPixels is extremely slow, so streaming a video with 24 frames per second hangs a lot (by not more than simple pixel transfer per frame, no drawing).
In comparison: before, i tried it with one single FBO without multisampling and glReadPixels+glDrawPixels for pixel transfer - much better performance, no hanging.
I don’t want to require multisampled textures, but is there an alternative for better performance?