最近本人在写离线光线追踪渲染器,但是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. ECShop安装及错误修复

    ecshop 商城的安装及出现错误的解决 听语音 | 浏览:600 | 更新:2016-03-04 16:02 | 标签:错误 安装 电子商务 1 2 3 4 5 6 7 分步阅读 昨天第一次安装ec ...

  2. PowerDesigner生成pdm(适用Mysql)

    废话不多说,直接开始: 1.首先安装所需要的驱动以及应用程序 ①和② 是 Mysql数据库连接驱动 ,根据PowerDesigner的位数来选择下载 下载地址:https://dev.mysql.co ...

  3. SpringBoot系列: JdbcTemplate 快速入门

    对于一些小的项目, 我们没有必要使用MyBatis/JPA/Hibernate等重量级技术, 直接使用Spring JDBC 即可, Spring JDBC 是对 jdbc的简单封装, 很容易掌握. ...

  4. 2019最新迅为-i.MX6Q开发板资料目录

            迅为IMX6开发板: Android4.4系统  Linux + Qt5.7系统  Ubuntu12.04系统 部分案例:HMI:3D打印机:医疗设备:工控机:触控一体机:车载终端 核 ...

  5. django drf 基础学习2

    DRF基本程序调用一 models初步编写  1 编写model.py    from django.db import models 导入    class dbinfo(models.Model) ...

  6. 激活函数——sigmoid函数(理解)

    0 - 定义 $Sigmoid$函数是一个在生物学中常见的S型函数,也称为$S$型生长曲线.在信息科学中,由于其单增以及反函数单增等性质,$Sigmoid$函数常被用作神经网络的阈值函数,将变量映射到 ...

  7. POJ 3190

    POJ3190                               摊位预订.时间限制:1000MS内存限制:65536K.共提交材料:12959份接受:4 546名特别法官.描述. 哦,那些 ...

  8. ssh-copy-id 拷贝用户秘钥

    生成秘钥 ssh-keygen -t [rsa|dsa] 将会生成密钥文件和私钥文件 id_rsa,id_rsa.pub或id_dsa,id_dsa.pub 将 .pub 文件复制到B机器的 .ssh ...

  9. 【原创】大叔经验分享(26)hive通过外部表读写elasticsearch数据

    hive通过外部表读写elasticsearch数据,和读写hbase数据差不多,差别是需要下载elasticsearch-hadoop-hive-6.6.2.jar,然后使用其中的EsStorage ...

  10. 【原创】大叔问题定位分享(30)mesos agent启动失败:Failed to perform recovery: Incompatible agent info detected

    mesos agent启动失败,报错如下: Feb 15 22:03:18 server1.bj mesos-slave[1190]: E0215 22:03:18.622994 1192 slave ...