在 DirectX 11 中高效捕获帧

Efficiently Capture Frames in DirectX 11

我正在尝试捕捉我正在玩的游戏的每一帧。市面上有很多优秀的屏幕捕捉软件,Windows 10.

中内置了其中一款

但是,出于不同的原因,我需要一种自定义方法。我目前正在使用 DirectX 工具包 SaveWICTextureToFile() 方法来保存由 Present() 生成的每一帧。

https://github.com/microsoft/DirectXTK

对于捕获的每一帧,我想用它的适用编号标记文件名的末尾。 ScreenShot_0ScreenShot_n.

方法SaveWICTextureToFile()为您保存截图:

DirectX::SaveWICTextureToFile(context, backbufferTex, GUID_ContainerFormatJpeg, L"C:/Users/User Name/Desktop/Images/ScreenShot.JPG");

这不允许您按顺序捕获帧。它只是为每一帧覆盖同一个文件。但是,性能非常流畅。在游戏过程中没有任何滞后。

为了尝试为每一帧编写一个文件,我执行了以下操作:

#include <sstream>
int Frame_Number;

//For each Call to Present() do the following:

//Get Device
ID3D11Device* device;
HRESULT gd = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
assert(gd == S_OK);

//Get context
ID3D11DeviceContext* context;
device->GetImmediateContext(&context);

//get back buffer
ID3D11Texture2D* backbufferTex;
HRESULT gb = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2d), (LPVOID*)&backbufferTex);
assert(gb == S_OK);

//Set-up Directory
std::wstringstream Image_Directory;
Image_Directory << L"C:/Users/User Name/Desktop/Images/ScreenShot_" << Frame_Number << L".JPG";

//Capture Frame
REFGUID GUID_ContainerFormatJpeg{ 0x19e4a5aa, 0x5662, 0x4fc5, 0xa0, 0xc0, 0x17, 0x58, 0x2, 0x8e, 0x10, 0x57 };
HRESULT hr = DirectX::SaveWICTextureToFile(context, backbufferTex, GUID_ContainerFormatJpeg, Image_Directory.str().c_str());
assert(hr == S_OK);
Frame_Number = Frame_Number + 1;

这有效,但性能不稳定。与以前的方法相比,我不再获得流畅的游戏体验。有人能推荐一种更有效的方法吗?

如果这是在调试版本中完成的,我怀疑字符串流有导致问题的逻辑,我建议改用字符数组和 swprintf。更好的是,如果你跟踪目录的结束位置,那么只需要写出文件名部分(你可以更进一步,这样你只需要格式化数字和扩展名)。

It simply writes over the same file for each frame. The performance however is very smooth.

如果其他条件相同,则减速有几种可能性:

  • 该文件仅写入内存,从未提交到磁盘,因为它一直被覆盖。

  • 每秒打开很多文件从来都不是一个好主意,特别是在 Windows 上,与例如相比,这在这方面特别慢。 Linux.

  • 将许多文件写入同一个文件夹是另一个坏主意,因为许多文件系统不能很好地处理这种情况。

找出这三个中的哪一个是罪魁祸首,然后从那里迭代。


并让我们知道!听到现在不同用例的 IO 有多快总是很有趣:-)