Lightmass源码分析之 与Swarm交互
烘焙基本流程
理想的情况下,一个烘焙渲染器从功能上来说,大体可以分为三块
导入数据:在工作开始时接受宿主程序或任务发起方的指令参数,从宿主程序或任务发起方的提供的数据中导入自己所需要的场景几何数据结构、材质数据和渲染参数。
渲染数据\生成结果:在导入数据完成之后对几何数据、材质资源进行预处理,生成适合渲染算法加速计算的数据结构,并利用这些预处理之后的数据进行渲染和计算以生成中间结果和最终结果。
导出数据:对于一个通用渲染器来说,生成的数据往往和宿主程序或任务发起方所需的结果在数据组织形式上并不相同,这时就需要把自己生成好的结果数据按需要的格式导出给宿主程序。
Lightmass与Swarm Agent
LightMass从功能上来说,除了以上三个部分外,在前面还有额外的一步——从UE编辑器中导出场景相关的几何和材质数据到中间文件中,从而LightMass在执行渲染时不依赖UE4 Editor。不依赖UE4 Editor而只依赖中间文件的好处有三个:
1. LightMass可以独立部署到其它机器上而不依赖完整的UE编辑器,从而轻量级实现分布式渲染成为可能。
2. 如果你有自定义的数据(非UE4引擎,非UE4数据格式)需要LightMass进行渲染,只需要实现LightMass对应的数据格式导出,并配套的实现基本的FSwarmInterface接口和其基本协议就可以做到。这就使U3D的用户和自研引擎使用LightMass进行分布式渲染成为可能。
3. 因为UE4 Editor在渲染光照图时会把数据导出为中间格式,也同样的为自定义烘焙渲染器变得更加便利。只需要你的渲染器能识别中间文件格式且实现基本的Swram协议,就可以实现自定义的Lightmap烘焙而不依赖LightMass.
因为分布式支持的原因,LightMass工作流不能被设计成完整的线性流程。LightMass和渲染任务发起方之间的数据交换是通过中间方FSwarmInterface接口转接Swarm Agent进行的。
对于FSwarmInterface,我们可以把它看成一个通用意义上的网络接口的本地代理。
我们先看一下FSwarmInterface用于数据交换的主要接口
OpenConnection 打开一个连接Swarm Agent的新连接,可以提供一个回调函数,这个回调函数用于处理从Swarm Agent接收到的消息。
CloseConnection 关闭一个已经存在的Swarm Agent连接
AddChannel 添加一个存在的文件到Cache里 ,文件会被复制到Cache中
OpenChannel 打开一个具名的Cache数据通道
CloseChannel 关闭一个已经打开的Cache数据通道
WriteChannel 把数据写入指定数据通道中
ReadChannel 从指定的数据通道中读取数据
LightMass的导入数据通过ReadChannel完成具体的数据读取的。具体的实现在LightmassScene.cpp中的FScene::Import中实现,LightMass中间场景文件的数据格式下一篇文章我们继续介绍。
LightMass的数据导出由WriteChannel完成,具体的代码实现参见LightingSystem.cpp中的TCompleteStaticLightingList<StaticLightingDataType>::ApplyAndClear函数。该函数最终会通过FLightmassSolverExporter::ExportResults完成具体的每一张LightMap的导出。LightMap的编码在前文已述及。LightMass生成的其它文件格式我们在后面文章里也会进行简要介绍。
LightMass在完成数据导入之后,会等待SwarmAgent给它分配渲染任务,在收到渲染任务之后才开始正式的渲染工作。同一时间可能有多个分布式的LightMass在工作,所以LightMass中的每个线程每次只请求一个任务,在做完当前任务之后则继续请求下一个直到所有任务完成。
无论是任务从任务发起方分发给LightMass或者是LightMass向Swarm Agent请求任务,都是通过FSwarmInterface的消息和任务接口完成的。
FSwarmInterface消息和任务相关的接口如下
SendMessage 接口给Swarm Agent发送消息,这些消息即Swarm定义的网络协议,这些协议既可能是直接由Swarm Agent处理,也可能由Swarm Agent转发给LightMass或UE4编辑器处理,或者兼而有之,同时由我端进行消息处理,如信息,警告,错误等都是通过消息发关给Swarm Agent和UE4编辑器同时处理的。
OpenJob 打开或创建一个新工作,一个工作由一个或多个任务(Task)组成,一个任务执行可能有其它外在的依赖(Dependency)
CloseJob 关闭任务,释放当前任务所分配的资源
BeginJobSpecification 开始为Job指定依赖,随后即可以开始为Job添加新的任务
EndJobSpecification 当前Job不再有新的任务添加
AddTask 为当前Job一个任务
渲染任务发布对任务发起方来说,是通过一次BeginJobSpecification和多次AddTask调用来完成。添加任务序列,SwarmCoordinator为可选片段
具体的开始Job和添加任务实现可以参见Editor下FLightmassProcessor类的BeginRun函数
对LightMass来说,接受任务则是通过SendMessage请求任务,再在Callback中接收到具体的Task进行处理。LightMass的线程任务获取可以参见FStaticLightingSystem类的ThreadLoop函数的实现。它通过FStaticLightingSystem::ThreadGetNextMapping转调FLightmassSwarm::RequestTask来获取任务,而RequestTask中的任务队列就是由FSwarmInterface回调填充的。LightMass请求任务序列,SwarmCoordinator为可选片段
LightMass请求任务实现参见FLightmassSwarm类的RequestTask和SwarmCallback函数
往期推荐