最近本人在写离线光线追踪渲染器,但是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. IIS运行一段时间不响应的解决方法

    查找事件记录,提示为应用程序池 'DefaultAppPool' 提供服务的进程关闭时间超过了限制 解决方案: Internet 信息服务(IIS)管理器->应用程序池->DefaultA ...

  2. [JDK8]性能优化之使用LongAdder替换AtomicLong

    如果让你实现一个计数器,有点经验的同学可以很快的想到使用AtomicInteger或者AtomicLong进行简单的封装. 因为计数器操作涉及到内存的可见性和线程之间的竞争,而Atomic***的实现 ...

  3. 第九节: EF的性能篇(二) 之 Z.EntityFramework.Extensions程序集解决EF的性能问题

    一. 综述 该模块主要介绍:EF的性能优化插件Z.EntityFramework.Extensions,该插件收费. (一). 简介 1. 相关网站:http://www.zzzprojects.co ...

  4. webpack学习笔记——项目引入zepto及tap事件失效的解决

    先要npm下来zepto:npm install zepto 然后npm下来exports-loader和script-loader 配置如下: JavaScript // webpack.confi ...

  5. swoole简单demo测试

    测试代码 1.server.php: <?php $serv = new swoole_server("0.0.0.0", 9502); $serv->on('conn ...

  6. Entity Framework查询

    Entity Framework是个好东西,虽然没有Hibernate功能强大,但使用更简便.今天整理一下常见SQL如何用EF来表达,Func形式和Linq形式都会列出来(本人更喜欢Func形式). ...

  7. angularjs指令中的compile与link函数详解补充

    通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你 ...

  8. Linux 首先基本包安装(vim啊什么的),源,源优化,项目架构介绍, (LNMuWsgi)Django项目相关软件mysql,redies,python(相关模块)安装配置测试

    内容 补充: 查看已启动服务的端口 netstat -tulnp |grep (方式1) ss -tulnp|grep (方式2) 前期铺垫: . Linux要能上网 . 掌握Linux软件包安装方法 ...

  9. Nginx中if语句中的判断条件

    一.if语句中的判断条件(nginx) 1.正则表达式匹配: ==:等值比较; ~:与指定正则表达式模式匹配时返回“真”,判断匹配与否时区分字符大小写: ~*:与指定正则表达式模式匹配时返回“真”,判 ...

  10. PID控制器开发笔记之十二:模糊PID控制器的实现

    在现实控制中,被控系统并非是线性时不变的,往往需要动态调整PID的参数,而模糊控制正好能够满足这一需求,所以在接下来的这一节我们将讨论模糊PID控制器的相关问题.模糊PID控制器是将模糊算法与PID控 ...