Можно ли сделать полностью вне экрана в QGLWidget
с Qt без необходимости перерисовывать сцену на экран, таким образом, полностью избегая переворачивания буферов на монитор?
Мне нужно сохранить каждый кадр, сгенерированный в буфере кадров, но, поскольку последовательность состоит из 4000 кадров, а временной интервал на экране 15ms
я потратил 4000*15ms=60s
но мне нужно быть намного быстрее 60-х (вычисления здесь не являются узким местом, это просто обновление проблемы).
Может ли рендеринг вне экрана в фреймбуфере быть быстрее? Можно ли избежать частоты обновления монитора в моем QGLWidget?
Как сделать полностью рендеринг на фреймбуфере без медленного paintGL()
звонки?
Сейчас я предполагаю, что мы говорим о Qt4.
Можно ли отрисовывать полностью за кадром в QGLWidget?
Внеэкранный рендеринг вообще не является задачей, зависящей от оконной системы. Единственная проблема с WGL (по крайней мере) и GLX в большинстве инструментов состоит в том, что вы не можете иметь поверхностный контекст, то есть контекст, который не привязан к объекту рисования, предоставленному оконной системой. Другими словами, вы всегда будете иметь оконную систему кадровый буфер по умолчанию это неизменное, пока существует текущий контекст.
Есть средства для создания контекста, который не требует окно вручную с X11, но обычно это не стоит проблем. Например, для EGL и OpenGL ES этой проблемы не существует, поскольку существует расширение, предназначенное именно для этой проблемы, то есть закадровый рендеринг.
Однако вы можете просто скрыть QGLWidget после того, как был установлен допустимый контекст, и использовать объекты кадрового буфера, чтобы делать все без вмешательства кадрового буфера по умолчанию.
Можно ли избежать частоты обновления монитора в моем QGLWidget?
Нет, насколько мне известно, модуль OpenGL в Qt4 не имеет средств для программного поворота vsync. Вы можете обратиться к SDL или GLFW за что-то подобное (не уверен насчет FreeGLUT).
Тем не менее, вы всегда можете отключить вещи в настройках вашего драйвера. Это также повлияет на QGLWidget (или, лучше сказать, на поведение подкачки базовой оконной системы).
Может ли рендеринг вне экрана в фреймбуфере быть быстрее?
Это действительно не должно иметь значения в конце. Вам понадобятся данные изображения в другом месте, кроме VRAM, поэтому после рендеринга текущего кадра в FBO вам все равно нужно получить изображение. Вы либо перетаскиваете результаты в передний буфер (или в обратный буфер, если вам нужна двойная буферизация и своп), либо вам нужно перечитать материал перед дальнейшей обработкой текущего кадра.
Однако, как и в случае с OpenGL и производительностью, не угадай — профиль!
Как полностью выполнить рендеринг в фреймбуфере без медленных вызовов paintGL ()?
После того, как контекст настроен, вам не нужен виджет совсем. Вы можете сделать всю магию самостоятельно без вмешательства Qt. Единственная причина paintGL()
существует, чтобы предоставить пользователю простой в использовании интерфейс, который гарантированно вызывается, когда виджет должен быть обновлен.
РЕДАКТИРОВАТЬ: Что касается вашего запроса в комментариях, посмотрите этот минимальный пример кода, который должен работать кроссплатформенно без изменений.
#include <iostream>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QApplication>
void renderOffScreen ()
{
std::cout << glGetString(GL_VENDOR) << std::endl;
std::cout << glGetString(GL_RENDERER) << std::endl;
std::cout << glGetString(GL_VERSION) << std::endl;
// do whatever you want here, e.g. setup a FBO,
// render stuff, read the results back until you're done
// pseudocode:
//
// setupFBO();
//
// while(!done)
// {
// renderFrame();
// readBackPixels();
// processImage();
// }
}
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QGLWidget gl;
// after construction, you should have a valid context
// however, it is NOT made current unless show() or
// similar functions are called
if(!gl.isValid ())
{
std::cout << "ERROR: No GL context!" << std::endl;
return -1;
}
// do some off-screen rendering, the widget has never been made visible
gl.makeCurrent (); // ABSOLUTELY CRUCIAL!
renderOffScreen ();
return 0;
}
На моем текущем компьютере программы печатает:
ATI Technologies Inc.
AMD Radeon HD 7900 Series
1.4 (2.1 (4.2.12337 Контекст профиля совместимости 13.101))
Обратите внимание, как QGLWidget
фактически никогда не делается видимым, и обработка событий не происходит. Библиотека Qt OpenGL просто используется для создания контекста. Все остальное делается без вмешательства Qt. Только не забудьте установить область просмотра и прочее в соответствии с вашими потребностями.
Пожалуйста, обратите внимание: Если все, что вам нужно, это какой-то удобный способ настройки контекста, вы можете переключиться на какой-то инструментарий, более легкий, чем Qt4, например FreeGLUT. Лично я обнаружил, что FreeGLUT гораздо более надежен, когда речь идет о настройке действительного контекста именно так, как я хочу, на некотором оборудовании, например. Процессоры Sandy Bridge.
Я нашел решение, связанное с использованием QGLFrameBuffer
объекты и glReadPixels
,
Сначала я инициализирую QGLFrameBuffer
объект в QGLWidget::initializeGL
для того, чтобы иметь действительный контекст GL, где QGLFrameBuffer
могу «врать».
Это первая реализация. Частота кадров 10
раз выше и ничего не обновляет в зависимости от VSync !!
MyGLWidget::MyGLWidget(QWidget *parent) :
// QGLWidget(parent)
QGLWidget( QGLFormat(QGL::SampleBuffers), parent) //this format doesn't matter it's the QGLWidget format on the monitor
{
//some initializations
}void MyGLWidget::initializeGL()
{
qglClearColor(Qt::black);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-10);
this->makeCurrent();
// Initializing frame buffer object
// Here we create a framebuffer object with the smallest necessary precision, i.e. GL_LUMINANCE in order to make
// the subsequent calls to glReadPixels MUCH faster because the internal format is simpler and no casts are needed
QGLFramebufferObjectFormat fboFormat;
fboFormat.setMipmap(false);
fboFormat.setSamples(0);
fboFormat.setInternalTextureFormat(GL_LUMINANCE);
// Create the framebuffer object
fbo = new QGLFramebufferObject(QSize(this->width(),this->height()),fboFormat);
}void MyGLWidget::generateFrames()
{
//keep unsigned int because of possible integer overflow
//when resizing the vector and consequent std::bad_alloc() exceptions
unsigned int slicesNumber = 1000;
unsigned int w = this->width();
unsigned int h = this->height();
// This vector contains all the frames generated as unsigned char.
vector<unsigned char> allFrames;
allFrames.resize(w*h*slicesNumber);
fbo->bind();
// Inside this block the rendering is done on the framebuffer object instead of the MyGLWidget
for ( int i=0; i<slicesNumber; i++ )
{
this->paintGL();
// Read the current frame buffer object
glReadPixels(0, 0, w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, allFrames.data()+i*w*h);
// update scene()
}
fbo->release();
}