最近本人在写离线光线追踪渲染器,但是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. 用IntelliJ IDEA 开发Spring+SpringMVC+Mybatis框架 分步搭建一:建立MAVEN Web项目

    一:创建maven web项目er

  2. C#动态操作DataTable(新增行、列、查询行、列等)

    public void CreateTable() { //创建表 DataTable dt = new DataTable(); //1.添加列 dt.Columns.Add("Name& ...

  3. django系列 2 :启动应用,目录结构解读

    来源:https://docs.djangoproject.com/en/2.1/intro/tutorial01/ 该教程是创建一个用于投票的网页. 1.使用命令创建site 进入要创建site的目 ...

  4. openstack项目【day24】:KVM部署

    本节内容 虚拟化支持 软件准备 检查CPU虚拟化支持 安装软件包 激活并启动libvirtd服务 网络模式 配置桥接网络 验证网络 尝试连接Hypervisor 创建虚拟机 虚拟机操作 一.虚拟化支持 ...

  5. DirectX11 With Windows SDK--12 深度/模板状态、平面镜反射绘制

    前言 深度/模板测试使用的是与后备缓冲区同等分辨率大小的缓冲区,每个元素的一部分连续位用于深度测试,其余的则用作模板测试.两个测试的目的都是为了能够根据深度/模板状态需求的设置来选择需要绘制的像素. ...

  6. python学习04

    数据类型-list,tuple 1) 1.1.list的表现方法:[1,2,3,4,5,6] 1.2.计算list的长度用 len() 1.3 list中的索引 a =[1,2,3,4,5] a[1] ...

  7. 在JS中如何判断所输入的是一个数、整数、正数、非数值?

    1.判断是否为一个数字: Number(num)不为 NaN,说明为数字 2. 判断一个数为正数: var num=prompt("请输入:"); if(Number(num)&g ...

  8. django drf 基础学习2

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

  9. WebService - 术语介绍

    一.WebService是什么? 1. 基于Web的服务:服务器端整出一些资源让客户端应用访问(获取数据) 2. 一个跨语言.跨平台的规范(抽象) 3. 多个跨平台.跨语言的应用间通信整合的方案(实际 ...

  10. Easy RM to MP3 Converter栈溢出定位及漏洞利用

    本文主要是Easy RM to MP3 Converter(MFC++编写)栈溢出的定位及windows下shellcode编写的一些心得. 用到的工具及漏洞程序下载地址https://github. ...