2

我正在努力在 QT4 中创建一个无头控制台应用程序,该应用程序执行一些 OpenGL 渲染,然后通过 websocket 将结果通过网络发送出去。我运行了所有的渲染和网络代码(假设我有一个 GUI),但是我在转换到无头应用程序时遇到了一些麻烦。是否可以在没有窗口的情况下创建 QGLContext?

在网上阅读的次数不多,但从我收集到的内容中,您可以创建一个 QGLPixelBuffer,它是一个有效的 QPaintDevice。它似乎创建了自己的私有 QGLContext 用于硬件加速绘图。我在这条路线上遇到的问题是我需要访问它的底层 QGLContext 以便我可以与另一个线程共享它(用于将 DMA 纹理快速传输出渲染场景的网络线程)。下面包括一个小型原型。有任何想法吗?

应用程序.h

/**
@file
@author     Nikolaus Karpinsky
*/

#ifndef _APPLICATION_H_
#define _APPLICATION_H_

#include <QCoreApplication>
#include <QTimer>

#include "MainController.h"

#endif  // _APPLICATION_H_

应用程序.cpp

#include "Application.h"

int main(int argc, char **argv)
{
//  Setup our console application with an event loop
QCoreApplication app(argc, argv);

//  Create and initialize our controller
MainController controller;
controller.Init();
QObject::connect(&controller, SIGNAL( Finished() ), &app, SLOT( quit() ), Qt::QueuedConnection);

//  This will put start on top of the event loop
QTimer::singleShot(0, &controller, SLOT( Start() ));

// Finally start up the event loop
return app.exec(); 
}

主控制器.h

/**
 @file
 @author        Nikolaus Karpinsky
 */

#ifndef _MAIN_CONTROLLER_H_
#define _MAIN_CONTROLLER_H_

#include <QObject>
#include <QGLWidget>
#include <QGLPixelBuffer>
#include <QGLFramebufferObject>
#include <memory>

using namespace std;

class MainController : public QObject
{
  Q_OBJECT

private:
  unqiue_ptr<QGLPixelBuffer> m_mainBuffer;
  //unique_ptr<QGLContext> m_mainContext;

public:
  MainController();
  void Init(void);

public slots:
  void Start(void);
  void Close(void);

signals:
  void Finished(void);
};

#endif  // _MAIN_CONTROLLER_H_

主控制器.cpp

#include "MainController.h"

MainController::MainController() : QObject()
{ }

void MainController::Init(void)
{
  m_mainBuffer = unique_ptr<QGLPixelBuffer>(new QGLPixelBuffer(800, 600));
  bool has      = buffer->hasOpenGLPbuffers();
  bool current  = buffer->makeCurrent();
  bool valid    = buffer->isValid();

  //  Now I need to get access to the context to share it with additional threads
  // m_mainContext = unique_ptr<QGLContext>(new QGLContext(buffer.getContext()));
}

void MainController::Start(void)
{
}

void MainController::Close(void)
{
  //  This will tell the event loop that we are done and close the app
  emit( Finished() );
}
4

2 回答 2

2

是否可以在没有窗口的情况下创建 QGLContext?

是的,但有一个问题……</p>

在网上阅读的次数不多,但从我收集到的内容中,您可以创建一个 QGLPixelBuffer,它是一个有效的 QPaintDevice。

是的,但是 PBuffer 仍然需要与 GPU 通信。在 Linux 中,与 GPU 通信的常用方式是通过 X 服务器。所以你实际上需要一个 X 服务器,使用 GPU 驱动程序,启动活动 VT,以便 PBuffer 可以在 GPU 上工作。

希望很快会有一个新的 GPU 的 ABI/API,它允许您在 GPU 上创建屏幕外渲染上下文,而无需 X 服务器。

我在这条路线上遇到的问题是我需要访问它的底层 QGLContext 以便我可以与另一个线程共享它(用于将 DMA 纹理快速传输出渲染场景的网络线程)。

不幸的是,Qt 开发人员对 OpenGL 的了解似乎很有限。一些使用 OpenGL 完全可能的事情并且明确指定的事情在 Qt 中是不可能的,没有明显的原因。例如,您可以使用单个可绘制对象拥有任意数量的上下文。但是您也可以通过重新绑定在任意数量的(兼容的)drawable 上使用单个上下文。Qt 和明显的设计缺陷都不支持。我自己现在也在为此苦苦挣扎。

于 2013-01-15T21:36:40.770 回答
1

好的,所以在玩了一些之后,我找到了一个足够好的解决方案。目前我正在将我的应用程序作为 QApplication 与 QCoreApplication 运行。从这里我在 GUI 线程上创建一个 QGLWidget,然后立即在创建和初始化 OpenGL 上下文的小部件上调用 updateGL()。假设我从来没有让应用程序可见,它运行“无头”但仍然从 X 或资源管理器获得 QT 所需的窗口句柄。

再深入一点,我还需要在其他线程上共享 QGLContexts。要从 QGLWidget 复制上下文,我只需使用已弃用的构造函数创建一个 QGLWidget,您可以在其中指定画家 (QGLWidget),然后使用 QGLWidgets 上下文创建。我在下面发布了一个示例来显示。使用它,我可以在一个线程中编写纹理并在另一个线程中处理,同时保持应用程序“无头”。感谢 datenwolf 的帮助。

shared_ptr<QGLContext> MainController::MakeSharedContext(void)
{
  shared_ptr<QGLContext> sharedContext(nullptr);

  if(nullptr != m_mainContext)
  {
    // m_mainContext is: shared_ptr<QGLWidget>
    sharedContext = shared_ptr<QGLContext>( new QGLContext(m_mainContext->format(), m_mainContext.get( ) ) );
    bool created = sharedContext->create( m_mainContext->context( ) );
    Utils::AssertOrThrowIfFalse(created, "Unable to create a shared OpenGL context" );
  }

  return sharedContext;
}
于 2013-01-17T21:03:58.080 回答