的渲染逻辑_OpenGL多线程——渲染与逻辑分离详细结构(附代码)

上一篇写了两个OpenGL逻辑与渲染分离的多线程错误架构示例,这篇来写写正确的。这篇并不像上篇是纯理论,而是会附一些代码,但代码里可能有不够优雅或有内存风险的地方,希望大家多多批评~

上一篇在这里:

Frandium:OpenGL渲染线程与逻辑线程分离的错误架构示例​zhuanlan.zhihu.com

阅读此文前你应该知道

  • OpenGL(基于glfw和glad库)
  • pthread或其他你熟悉的多线程库
  • C++
  • OpenGL多线程渲染的基本事实(见上一篇)

阅读此文后你会了解

  • 使用pthread创建和进行线程同步
  • 基于双队列swap的渲染与逻辑分离架构设计
  • OpenGL多线程的坑和注意点

架构纵览

要实现多线程渲染,就需要使用一个缓冲队列存储渲染指令。逻辑线程向其中添加指令,渲染线程消费。由于传统生产者-消费者模式要求在入队出队时都要加锁,这会导致逻辑线程和渲染线程不停地被挂起唤醒(或忙等),效率很低,因此我们希望逻辑线程生产指令和渲染线程消费指令不要相互竞争。这启发我们使用双队列方案:使用两个队列,一个供逻辑线程添加指令,一个供渲染线程取出指令。在每帧结束后(或开始前),交换两个队列。也就是说,渲染永远比逻辑慢1帧。整体的系统结构图如下:

c04f1df5fb4595ba5e2cd16ba645cca3.png
整体结构图

注意到Mono::Init()回调是在渲染线程初始化后才执行的。因为init中会存在一些绑定VBO、编译Shader之类的操作,因此一定要放在渲染context生成之后。

接下来依次说明同步的实现、渲染队列的实现细节和内存有关的问题。

同步

同步是使用pthread中barrier实现的。由于每一帧各个线程执行顺序不确定,而我们希望barrier在被各个线程wait barrier之前就已经被初始化好。因此我们使用两个barrier,一开始都初始化。接下来每一帧让主线程把下一帧要用到的barrier初始化,然后大家一起wait这一帧的barrier。具体操作的方法就是维护一个帧计数器,然后对2取余,经典的ping-pong操作。

主线程:

void Engine::Run(){
    
#ifndef MULTITHREAD
  // 单线程的逻辑
#endif 
#ifdef MULTITHREAD
 int frame_count = 0;
 pthread_t logical, render;
 // 2 barriers to synchronize main thread, logical thread and render thread;
 pthread_barrier_t barriers[2];
 pthread_barrier_init(barriers, NULL, 3);
 pthread_barrier_init(barriers + 1, NULL, 3);
 
 int ret = pthread_create(&logical, NULL, LogicalThread, &barriers);
 if (ret < 0) {
    
 std::cout << "Failed to create logical thread." << std::endl;
 return;
 }
 
 ret = pthread_create(&render, NULL, RenderThread, &barriers);
 if (ret < 0) {
    
 std::cout << "Failed to create render thread." << std::endl;
 return;
 }
 
 pthread_barrier_wait(barriers + frame_count % 2);
#endif
 
 std::cout << "m
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Qt中使用OpenGL进行多线程渲染可以提高渲染效率和用户体验。下面是一个基本的示例,演示了如何在Qt中使用OpenGL进行多线程渲染: 1. 首先,在Qt中创建一个新的OpenGL窗口类,继承自QOpenGLWidget。 ```cppclass MyGLWidget : public QOpenGLWidget{ public: MyGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {} protected: void initializeGL() override { // 初始化OpenGL环境 } void resizeGL(int w, int h) override { // 处理窗口大小变化事件 } void paintGL() override { // 执行OpenGL渲染操作 } }; ``` 2. 接下来,在主窗口类中创建一个新的线程,并在该线程中进行OpenGL渲染操作。 ```cppclass MainWindow : public QMainWindow{ public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { // 创建OpenGL窗口 m_glWidget = new MyGLWidget(this); setCentralWidget(m_glWidget); // 创建渲染线程 m_renderThread = new QThread(this); m_glWidget->moveToThread(m_renderThread); // 连接信号槽,触发渲染操作 connect(m_renderThread, &QThread::started, m_glWidget, &MyGLWidget::update); connect(m_glWidget, &MyGLWidget::frameSwapped, m_renderThread, &QThread::quit); connect(m_glWidget, &MyGLWidget::destroyed, m_renderThread, &QThread::quit); // 启动渲染线程 m_renderThread->start(); } private: MyGLWidget *m_glWidget; QThread *m_renderThread; }; ``` 在上述代码中,我们在主窗口类的构造函数中创建了一个OpenGL窗口,并将其移动到一个新的线程中。然后,我们连接了一些信号槽,以触发渲染操作,并在窗口销毁时停止渲染线程。 这样,我们就可以在Qt中使用OpenGL进行多线程渲染了。当启动应用程序时,渲染线程将开始执行OpenGL渲染操作,而主线程仍然可以响应用户交互事件。请注意,这只是一个简单的示例,你可能需要根据你的具体需求进行更多的代码调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值