我正在开发一个基于虚幻的开源无人机模拟(Microsoft AirSim),我正在尝试捕捉并保存连接到无人机的相机中的图像。下面的图片让我们了解游戏的外观。底部最右边的视图是相机的实际视图,另外两个是同一图像的处理版本。
现在以这种方式设置它的方式:有一个相机资产,通过代码作为捕获组件读取。屏幕截图中的三个视图链接到此捕获组件。随着无人机在游戏中飞来飞去,视图流没有任何问题。但是在录制屏幕截图时,当前代码会从此捕获组件设置TextureRenderTargetResource,然后调用ReadPixels并将该数据保存为图像(请参阅下面的代码流程)。使用ReadPixels(),直接阻止游戏线程并大幅减慢整个游戏的速度:当我开始录制时,从大约120 FPS下降到小于10 FPS。
bool saveImage() {
USceneCaptureComponent2D* capture = getCaptureComponent(camera_type, true);
FTextureRenderTargetResource* RenderResource = capture->TextureTarget->GameThread_GetRenderTargetResource();
width = capture->TextureTarget->GetSurfaceWidth();
height = capture->TextureTarget->GetSurfaceHeight();
TArray<FColor> imageColor;
imageColor.AddUninitialized(width * height);
RenderResource->ReadPixels(bmp);
}
看看this article,看起来很明显ReadPixels()“将阻止游戏线程,直到渲染线程赶上”。本文包含用于读取像素的“非阻塞”方法的示例代码(通过删除FlushRenderingCommands()并使用RenderCommandFence标志来确定任务何时完成),但它并未显着提高性能:保存的图像略高,但游戏线程仍然只能以20 FPS运行,因此很难控制无人机。有没有更高效的异步方法可以实现我想要做的事情,也许,比如说,在一个单独的线程中?我也有点困惑为什么代码在屏幕上尽可能快地流式传输这些图像没有问题,但保存图像似乎更复杂。即使图像仅以15 Hz左右的速度保存到磁盘上也没关系,只要它不会过多地干扰游戏的原生FPS。
答案 0 :(得分:1)
(...)似乎很明显,ReadPixels()&#34;将阻止游戏线程,直到渲染线程赶上并且#34;
使用尝试访问和复制GPU纹理数据的函数总是很痛苦。我发现这些功能的完成时间取决于硬件或驱动程序版本。您可以尝试在不同的(更快或更新)GPU平台上运行代码。
返回代码:为了绕过单线程问题,我会尝试使用UTextureRenderTarget2D
方法将UTexture2D
复制到TextureRenderTarget2D::ConstructTexture2D
。当您复制图像时(我希望它需要更快),您可以以15 FPS的比例将其推送到消费者线程。可以使用FRunnable
或Async引擎模块(使用所谓的任务图)创建线程。消费者线程可以使用纹理PlatformData->Mips[0]->BulkData
来访问克隆纹理。确保您正在BulkData->Lock(...)
和BulkData->Unlock()
来电之间阅读纹理数据。
我不确定,但它可以提供帮助,因为渲染线程不会使用复制的纹理,因此您可以锁定它的数据,而不会在渲染线程上进行任何阻塞。我只担心锁定和解锁操作的线程安全性(没有找到任何线索),除非有引擎RHI调用,否则应该是可能的。
Here是一些可能有用的相关代码。
答案 1 :(得分:1)
您可以将“保存到磁盘”操作从“游戏”线程移动到其他线程 请查看 Async.h 了解详细信息
线程的限制是你不能在其他线程中修改/添加/删除uobject / uactor。 Multi-thread for UE4
答案 2 :(得分:1)
我参加聚会很晚,但是如果有人仍然对此有疑问:我建立了一个受AirSim和UnrealCV插件代码启发的非阻塞版本,因为我对此有一些严重的编译和版本控制问题。 ,但需要一个平稳运行的相机来自己拍摄图像。这也是基于虚幻API的async.h类提供的,作为建议的可接受答案。 我为此设置了一个教程库: https://github.com/TimmHess/UnrealImageCapture。
希望它会有所帮助。
答案 3 :(得分:0)
也许您应该在TArray中存储截图,直到“游戏”完成,然后处理所有存储的数据?
或者看看从无人机相机拍摄截图而不是操纵像素数据?我假设左和中无人机相机图像是基本相机图像的材料修改版本。
保存图像时,是写入图像格式文件还是.uasset?
最后 - 这是一个彻头彻尾的黑暗 - 创建一个自定义UActorComponent,将其添加到无人机,并让它注意游戏中的一些指令。如果要编写图像,请将RenderTarget数据推送到自定义UActorComponent。由于actor组件可以在任何线程上打勾(在构造函数中使用BCanTickOnAnyThread = true,我认为这是变量名称),actor组件的tick可以监视传入的图像数据并在那里处理它。我从来没有这样做过,但是我已经在任何线程上制作了一个演员组件(我正在检查物理)。