最近本人在写离线光线追踪渲染器,但是Qt::QtConcurrent的功能有限,所以就尝试使用了一下,顺便分享一些经验。

TBB里面的parallel_for非常适合光线追踪渲染器,而QtConcurrent没有这个函数。

平台

  1. Qt:Qt 5.9.4 MSVC x64
  2. TBB:Threading Building Blocks 2018 Update 5
  3. CPU:inter i7-3630QM

配置TBB

首先去https://www.threadingbuildingblocks.org/下载TBB,之后会链接到github:https://github.com/01org/tbb/releases 选择对应平台下载。

文件说明

bin:里面的ia32与inter64文件夹分别放着x32与x64的dll文件。虽然网上的教程(配置VS)有所以设置path环境变量与启动路径里设置tbbvars.bat。但是QtCreator貌似做不到这些。

include:c++头文件

lib:里面的ia32与inter64文件夹分别放着x32与x64的lib文件

配置过程

  1. 添加库文件:在Qt项目栏中(显示所有工程文件的那栏)的工程图标中右键-添加库-外部库,选择库文件(tbb.lib)。之后在pro文件中将debug模式的文件名从-ltbbd改成-ltbb_debug。
  2. 添加头文件:在pro中加入INCLUDEPATH += $$PWD/XXXX/include,也就是tbb里的include文件夹路径。
  3. 执行QMake
  4. 将bin里对应平台的文件放到生成程序的目录下,即完成所有配置工作。

我的配置

这里我直接新建了一个名为“tbb”的文件夹放在工程目录里了。

INCLUDEPATH += $$PWD/tbb/include

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/tbb/lib/intel64/vc14/ -ltbb
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/tbb/lib/intel64/vc14/ -ltbb_debug INCLUDEPATH += $$PWD/tbb/lib/intel64/vc14
DEPENDPATH += $$PWD/tbb/lib/intel64/vc14

函数使用

任务组

tbb::task_group g;
g.run([]{TestPrint(); });
g.run([]{TestPrint(); });
g.run([]{TestPrint(); });
g.wait();

parallel_for与blocked_range、blocked_range2d

tbb::parallel_for(1, 10, [](int i){std::cout << i << std::endl; });

/*
官方案例里用的是构建一个类,编写构造函数以及重载()操作符,其实就是构建了一个仿函数,(lambda也是通过类似的方式实现的)。
所以我们这里可以直接用lambda来编写程序。parallel_for其中一种方式就是接受一个rande类型(指定我们的range),然后将其传递给后面的函数,所以后面函数需要有一个range类型的形参。
*/
//size_t改成int也是没问题的
tbb::parallel_for(blocked_range<size_t>(0, 10), [](blocked_range<size_t>& r)
{
for (size_t i = r.begin(); i != r.end(); ++i)
std::cout << i << std::endl;
});
//parallel_for配合blocked_range2d会对图像处理有很大的帮助
//blocked_range2d的参数说明:
//(x起始值,x结束值,x步进值,y起始值,y结束值,y步进值)
tbb::parallel_for( tbb::blocked_range2d<int>(0, nx, 1, 0, ny, 1),
[&](const tbb::blocked_range2d<int>& r)
{
for( int i=r.rows().begin(); i!=r.rows().end(); ++i ){
for( int j=r.cols().begin(); j!=r.cols().end(); ++j ) {
std::cout << i <<j<< std::endl;
}
}
}

需要注意的细节

tbb的任务组因为需要使用wait(),会阻塞GUI线程,所以如果有GUI更新需求,则需要使用QtConcurrent::run。

task_group g;
g.run(
//QtConcurrent::run(
[this](){
Point2D sp;//采样点坐标
Point2D pp;//pixel上的采样点
int nx = setting->imageWidth;
int ny = setting->imageHeight;
int allPixelNum=nx*ny;
int currentPixelNum=0; Vector3D lower_left_corner(-2.0, -1.0, -1.0);
Vector3D horizontal(4.0, 0.0, 0.0);
Vector3D vertical(0.0, 2.0, 0.0);
Point3D origin(0.0, 0.0, 0.0); tbb::parallel_for( tbb::blocked_range2d<int>(0, nx, 1, 0, ny, 1),
[&](const tbb::blocked_range2d<int>& r)
{
for( int i=r.rows().begin(); i!=r.rows().end(); ++i ){
for( int j=r.cols().begin(); j!=r.cols().end(); ++j ) {
RGBColor pixelColor;
Ray ray;
for(int k=0;k<setting->numSamples;k++){
sp=setting->samplerPtr->sampleUnitSquare();
float u = float(i+sp.x) / float(nx);
float v = float(j+sp.y) / float(ny);
ray.origin=origin;
ray.direction=lower_left_corner+u*horizontal+v*vertical; pixelColor+= tracer_ptr->trace_ray(ray);
}
pixelColor/=setting->numSamples;
currentPixelNum++; emit pixelComplete(i,j,currentPixelNum*100/allPixelNum,QColor( int(255.99*pixelColor.r), int(255.99*pixelColor.g), int(255.99*pixelColor.b)));
}
}
});
g.wait();
emit renderComplete();});

以上代码是在渲染完一个像素后通过信号槽传递到GUI上,更新结果显示,这样做很可能会降低渲染的效率(因为信号槽的机制,这也是Qt的网络库不行的原因之一)。不过渲染每像素时间超过一定量的时候,这种损耗也可以忽略不计(1min以内渲染1024*1024这种明显会降低渲染效率)。

进过测试tbb::task_group会阻塞GUI线程,从而导致GUI假死,所以有GUI需求的则需要使用QtConcurrent::run,如果没有GUI还是建议使用task_group。

经过思考,本人通过渲染经过时间与像素总数的比值,来调整GUI线程更新渲染结果的间隔,从而解决因长时间渲染(30Min以上),GUI线程的无效更新而造成的损耗。

通过这种方式渲染就不会被GUI所拖累,同时GUI也不会被卡死。不过需要在渲染过程中使用自旋锁来解决因为渲染过快而造成的渲染不完全的问题。

    tbb::spin_mutex mutex;
tbb::parallel_for( tbb::blocked_range2d<int>(0, nx, 1, 0, ny, 1),
[&](const tbb::blocked_range2d<int>& r)
{
for( int i=r.rows().begin(); i!=r.rows().end(); ++i ){
for( int j=r.cols().begin(); j!=r.cols().end(); ++j ) {
RGBColor pixelColor;
Ray ray;
for(int k=0;k<setting->numSamples;k++){
sp=setting->samplerPtr->sampleUnitSquare();
float u = float(i+sp.x) / float(nx);
float v = float(j+sp.y) / float(ny);
ray.origin=origin;
ray.direction=lower_left_corner+u*horizontal+v*vertical; pixelColor+= tracer_ptr->trace_ray(ray);
}
mutex.lock();
pixelColor/=setting->numSamples;
image->setPixelColor(i,j,QColor( int(255.99*pixelColor.r), int(255.99*pixelColor.g), int(255.99*pixelColor.b)));
currentPixelNum++;
progress=currentPixelNum*100/allPixelNum;
mutex.unlock();
}
}
});
emit renderComplete();});

在Qt中配置TBB以及简单实用的更多相关文章

  1. OpenCV2学习笔记03:Qt中配置OpenCV环境

    在Qt中开发基于OpenCV的应用时,需要配置对应函数库到环境变量,这时候我们需要使用到qmake能够识别的变量来指定环境变量. INCLUDEPATH: 用于指定搜索头文件到文件夹路径. LIBS: ...

  2. 对QT中QBitArray类进行简单剖析

    我们知道Qt中的QBitArray类支持在位(bit)的层次上进行数据操作.本文剖析该类在二进制文件读写时的一些要点.另外,在Qt中,QDataStream类对于二进制文件的读写提供了诸多便利,需要注 ...

  3. Ubuntu下 QT中配置ROS-Kinetic

    打开qtcreater自动加载ros环境,通过修改*.desktop文件 gedit ~/.local/share/applications/qtcreator.desktop 将其中Exec=XXX ...

  4. Ubuntu中在QT中配置OpenGL

    之前搞实验室项目,博客有些天没有更新.现在学习需要,开始搞OpenGL+Ubuntu+QT. 搞了整整一天,由于是首次使用ubuntu,所以这ubuntu下配置qt和Opengl环境时走了很多的弯路, ...

  5. Qt中QScrollArea类的简单使用心得

           平台:windows 64位        Qt版本:5.5.1 MinGW 32bit 根据自己目前的需求简单说下怎么在QScrollArea滚动窗口中实现多个控件的滚动显示,先看看最 ...

  6. 2.关于QT中数据库操作,简单数据库连接操作,数据库的增删改查,QSqlTableModel和QTableView,事务操作,关于QItemDelegate 代理

     Linux下的qt安装,命令时:sudoapt-get install qt-sdk 安装mysql数据库,安装方法参考博客:http://blog.csdn.net/tototuzuoquan ...

  7. python中的sockeserver模块简单实用

    1. socketserver模块简介 在python的socket编程中,实用socket模块的时候,是不能实现多个连接的,当然如果加入其它的模块是可以的,例如select模块,在这里见到的介绍下s ...

  8. C++STL中的vector的简单实用

    [原创] 使用C++STL中的vector, #include <stdio.h> #include<stdlib.h> #include<vector> usin ...

  9. Qt 中配置 c99的问题

    Qt 5.3 版本 报错原因是c99标准问题的话,可以尝试下面方法 打开项目中xxx.pro工程文件 加入如下语句: QMAKE_CFLAGS += -std=c99

随机推荐

  1. nginx服务器的基本配置

    nginx作为反向代理搭建服务器的优点. 处理响应请求很快:单次请求会得到更快的响应.在高峰期,Nginx 可以比其它的 Web 服务器更快的响应请求 高并发连接:理论上,Nginx 支持的并发连接上 ...

  2. Nginx常用的平滑重启

    之前在做运维工作中,经常需要添加虚拟主机,或者添加修改配置文件,但是测试环境还好,随便玩,如果是生产环境的话,既要保证配置不出问题,有不能中断服务.如果是这样的话,就需要对配置文件进行语法检测以及平滑 ...

  3. org.hibernate.ObjectNotFoundException: No row with the given identifier exists解决办法

    hibernate-取消关联外键引用数据丢失抛异常的设置@NotFound hibernate项目里面配了很多many-to-one的关联,后台在查询数据时已经作了健全性判断,但还是经常抛出对象找不到 ...

  4. ZooKeeper-集群模式安装

    下载地址:https://zookeeper.apache.org/releases.html 至少需要准备三台节点(这里为h136.h138.h140),ZooKeeper 需要 JDK,关于 JD ...

  5. ArcGis恢复初始设置(默认设置、出厂设置)的方法

    警告:下面的操作涉及更改操作系统的重要组成部分.必要时,请咨询计算机系统专业人士. 重命名 ESRI 文件夹即对 ArcGIS 恢复出厂设置,因此必须重新安装当前安装的所有第三方工具.自定义脚本和自定 ...

  6. 散度、旋度与 Laplacian

    $$\bex -\lap {\bf u}=\rot \rot {\bf u}-\n \Div {\bf u}. \eex$$

  7. 查看和设置MySQL数据库字符集(转)

    查看和设置MySQL数据库字符集作者:scorpio 2008-01-21 10:05:17 标签: 杂谈 Liunx下修改MySQL字符集:1.查找MySQL的cnf文件的位置find / -ina ...

  8. C# - 多线程(基础)

    多线程 基础(Multithreading) 一些基本的关于线程和与其相关的概念 位)的变量赋值,这个操作就是原子性的.因为它可以一次性填充64位的二进制数据到栈上,属于一步完成,不会发生断裂.而假如 ...

  9. 网络知识 - 简易的自定义Web服务器

    简易的自定义Web服务器 基于浏览器向服务端发起请求 两台主机各自的进程之间相互通信,需要协议.IP地址和端口号,IP表示了主机的网络地址,而端口号则表示了主机上的某个进程的地址,IP加Port统称为 ...

  10. 网络(最大)流初步+二分图初步 (浅谈EK,Dinic, Hungarian method:]

    本文中  N为点数,M为边数: EK: (brute_force) : 每次bfs暴力找到一条增广路,更新流量,代码如下 : 时间复杂度:O(NM²): #include<bits/stdc++ ...