目录
程序的主入口:
构建BVH:
渲染循环:
核心功能:
多线程加速:
泛型单例:
消息队列:
线程池:
线程同步:
学习资料:
上一篇记录:
光追渲染器开发记录:开发环境配置Cmake+Vcpkg进行集成_This is MX的博客-CSDN博客https://blog.csdn.net/m0_56399931/article/details/123835101下一篇记录:
光追渲染器开发记录:BVH加速结构构建与射线求交_This is MX的博客-CSDN博客https://blog.csdn.net/m0_56399931/article/details/124145240
前记:个人水平有限,如果有改进的建议欢迎提出-------------------------------------------博主:mx
渲染器目前主入口长这样:
后续添加修改内容肯定会进行更改。
我们的主入口主要做了以下几件事:
· 构建场景
· 初始化渲染器
· 建立空间切分
· 侦听输入
· 渲染tick
截取了核心的一部分:
这里我采用games101里面的递归构建方法,核心就是:获取到最大的轴,根据那个轴进行排序切分递归构建。
先看图:
渲染循环主要做两个事情:
· Path Tracing 获取屏幕的每个像素颜色
· PostProcess后处理
这一部分肯定需要进行多线程加速。
为了使用多线程加速
这里多线程,采用了单例的线程池。
这个比较简单,就是采用的懒汉版单理模式,然后c++11是要求编译器保证内部静态变量的线程安全性,所以这个单例也是线程安全的,并且泛型能够适配多种类。
这个核心是把消息队列适配多种任务,并且需要是线程安全的,所以我做的是把原生的std::queue进行改造,对齐进行加锁,保证在调用的时候不会错误。
这个其实就比较难。主要是工作线程和消息封装这两个部分。
工作线程:
首先我们使用一个封装的工作线程来调用消息队列里面的消息,他会在线程池运行的时候,不断获取消息队列的任务进行执行,如果消息队列里面没有任务了,就会阻塞当前线程,直到有任务被唤醒。
消息封装:
我们封装的函数应该做到以下两点:
这里我们涉及几个知识:
· typename... Args:这个是C++11引入的可变模版参数(variadic templates)
· decltype:decltype(表达式)能够根据表达式推断出类型
· 尾返回类型(trailing return type):例如这样的来自动推断返回类型:
template
auto add2(T x, U y) -> decltype(x+y){
return x + y;
}
· std::future :提供了一种用于访问异步操作结果的机制。我们可以使用std::future的wait()方法来设置屏障,阻塞线程,实现线程同步。并最终使用std::future的get()方法来获得执行结果。
· std::function :它是一种通用、多态的函数封装,它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作,它也是对 C++中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),简而言之,std::function 就是函数的容器。
· std::bind :它可以将函数f和参数args绑定起来,留下一部分在真正调用时确定(当然,你也可以直接指定全部参数,在调用时不再指定。)。这个特性其实很适合做回调函数。
· std::forward:这个主要涉及万能引用、完美转发以及引用折叠。
· · 完美转发:
指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。
· · 万能引用(universal reference)
只有在发生类型推导的时候 “&&” 才代表 universal reference
如果用来初始化universal reference的表达式是一个左值,那么universal reference就变成lvalue reference。
如果用来初始化universal reference的表达式是一个右值,那么universal reference就变成rvalue reference。
· · 引用折叠
引用折叠只有两条规则:
一个 rvalue reference to an rvalue reference 会变成 (“折叠为”) 一个 rvalue reference.
所有其他种类的"引用的引用"(i.e., 组合当中含有lvalue reference) 都会折叠为 lvalue reference.
了解了这些之后我们再来看这个消息的封装:
核心其实是:自动推断出返回的future,用这个future来进行结果的获取。函数体是主要先绑定参数,然后将其封装成一个void()类型的函数方便调用,然后将其提交到任务队列,并唤醒一个线程。
这里提一下我们的消息队列也将每日任务统一的封装成了std::function
因为我希望用单例控制所有线程,这就要求,Path Tracing 获取完屏幕的所有像素颜色,再进行PostProcess后处理,对于我们的线程池来说就得要判断所有任务完成,主线程再继续进行。那就往线程池加这样一个Join的功能。
怎么做呢?
一个循环计数器message_num,每提交一个任务++,每个任务彻底运行完成--,主线程阻塞一下,message_num=0的时候再将其唤醒。
games101 光追渲染器
1.基于C++11实现线程池 - 知乎
2.C++中的单例模式_bob62856的博客-CSDN博客_c++ 单例模式