Files
XCEngine/docs/api/XCEngine/RHI/D3D12/D3D12Screenshot/Capture.md

4.4 KiB
Raw Blame History

D3D12Screenshot::Capture

bool Capture(RHIDevice* device, RHISwapChain* swapChain, const char* filename) override;
static bool Capture(ID3D12Device* device,
                    ID3D12CommandQueue* commandQueue,
                    ID3D12Resource* renderTarget,
                    const char* filename,
                    uint32_t width,
                    uint32_t height);
static bool Capture(D3D12Device& device,
                    D3D12CommandQueue& commandQueue,
                    D3D12Texture& texture,
                    const char* filename);

作用

从 D3D12 渲染目标抓取一帧图像,并把结果保存为 PPM 文件。

这三个公开重载只是适配不同调用层级,最终都会进入同一套 copy-to-readback 再写盘的实现。

核心流程

底层实现 CopyToReadbackAndSave(...) 的关键步骤如下:

  • 校验原生 devicecommandQueuerenderTarget
  • 查询资源描述和 copy footprint
  • 创建一次性 command allocator、command list、readback buffer
  • 插入 RENDER_TARGET -> COPY_SOURCE -> RENDER_TARGET 状态切换
  • 执行 CopyTextureRegion(...)
  • 提交队列并通过 fence 阻塞等待
  • Map() 读回数据,按 P6 PPM 写出 RGB 像素

重载 1: RHI 入口

bool Capture(RHIDevice* device, RHISwapChain* swapChain, const char* filename) override;

当前实现行为

  • 直接把 device 转成 D3D12Device*
  • 直接把 swapChain 转成 D3D12SwapChain*
  • 通过 swapChain->GetCurrentBackBuffer() 取得当前 back buffer
  • 通过 d3d12SwapChain->GetNativeCommandQueue() 取得原生命令队列
  • 再转发到内部 copy 逻辑

注意事项

  • 没有运行时类型检查,所以它只能在 D3D12 后端上下文中正确工作
  • 也没有对 device / swapChain 做空指针保护
  • 如果 D3D12SwapChain 当前没有缓存原生命令队列,底层会在空队列检查时报错并返回 false

重载 2: 原生 D3D12 入口

static bool Capture(ID3D12Device* device,
                    ID3D12CommandQueue* commandQueue,
                    ID3D12Resource* renderTarget,
                    const char* filename,
                    uint32_t width,
                    uint32_t height);

当前实现行为

  • 这是最直接的后端入口
  • 只做一层转发,真正逻辑全部在 CopyToReadbackAndSave(...)
  • 会记录 devicecommandQueuerenderTarget 为空的错误日志

关键前提

  • renderTarget 当前状态被假定为 D3D12_RESOURCE_STATE_RENDER_TARGET
  • 目标资源需要能被 CopyTextureRegion(...) 读取
  • 当前实现默认按“每像素 4 字节,取前 3 字节写 RGB”处理输出

宽高参数说明

这里的 width / height 不是通过 renderTarget->GetDesc() 自动推导出来的输出尺寸,而是调用者显式传入的写盘尺寸。

当前实现里:

  • copy 区域使用的是资源描述中的 Width / Height
  • 文件写出循环使用的是函数参数里的 width / height

因此如果两者不一致,输出内容可能越界、截断或产生错误图像。常规做法是传入与资源真实尺寸一致的值。

重载 3: D3D12 包装对象入口

static bool Capture(D3D12Device& device,
                    D3D12CommandQueue& commandQueue,
                    D3D12Texture& texture,
                    const char* filename);

当前实现行为

  • D3D12DeviceGetDevice()
  • D3D12CommandQueueGetCommandQueue()
  • D3D12TextureGetResource()GetWidth()GetHeight()
  • 再调用原生 D3D12 重载

适用场景

这通常是测试和集成样例里最方便的入口,因为调用方已经处在 D3D12 封装层,不需要再自己拆出所有原生句柄。

输出格式

  • 文件格式固定为 P6 PPM
  • 只写 RGB不写 alpha
  • 不做压缩
  • 不做色彩空间转换或 HDR 编码

实践建议

  • 优先用于测试截图、调试抓帧和最小验证程序
  • 如果未来要支持商业级截图能力,通常应把这条同步路径升级成“异步 readback + 后台编码”体系
  • 如果 render target 不是标准 4 字节颜色格式,应先评估当前实现是否适配,而不是直接假设结果正确

相关文档