5.7 KiB
RHIScreenshot
命名空间: XCEngine::RHI
类型: class (abstract)
头文件: XCEngine/RHI/RHIScreenshot.h
描述: 截图辅助对象,用于把当前 swap chain back buffer 读回到 CPU 并保存为文件,主要面向调试、测试和工具场景。
角色概述
RHIScreenshot 不是核心渲染管线对象,而是一条诊断与工具链路径。
它解决的问题很直接:
- 当前画面到底渲染出了什么
- swap chain 在 present 前后是否正确
- 集成测试能否把输出结果落盘
从定位上看,它更接近“引擎内置的轻量截图抓取辅助”,而不是面向最终玩家的完整截图系统。当前实现明显偏 debug / test 风格,而不是面向高性能运行时捕获。
创建方式
当前统一入口是:
RHIScreenshot::Create(RHIType) 会按后端返回不同实现:
D3D12当前始终可创建OpenGL取决于XCENGINE_SUPPORT_OPENGLVulkan取决于XCENGINE_SUPPORT_VULKAN- 其他未实现后端返回
nullptr
这意味着截图能力和 RHI 工厂一样,也受编译开关与后端建设状态影响。
当前主工作流
对上层来说,最典型的使用顺序是:
- 拿到有效的 RHIDevice
- 拿到有效的 RHISwapChain
- 调用 Capture
- 用完后 Shutdown 并
delete
tests/RHI/unit/test_screenshot.cpp 也主要围绕这条路径进行验证:
- 可以创建截图对象
- 可以对当前 swap chain 进行基本截图
Present(0, 0)之后依然可以截图
当前输出行为
这是最需要提前告诉使用者的实现事实。
当前三套后端的截图实现都会把数据写成二进制 PPM,也就是 P6 格式:
- 文件头写入
P6 - 紧接着写宽高与
255 - 再写 RGB 像素数据
这意味着:
- 即使你把文件名写成
.png、.jpg或其他扩展名,当前写出的内容依然是 PPM 数据 - 当前抽象层没有提供输出格式选择、压缩、alpha 保留或 HDR 导出能力
这是同步、阻塞的调试路径
当前各后端实现都会显式等待 GPU / 驱动完成读回:
- OpenGL 调用
glFinish() - D3D12 提交临时命令列表并等待 fence
- Vulkan 提交复制命令并
vkQueueWaitIdle()
所以 RHIScreenshot 现在应当被视为:
- 调试工具路径
- 自动化测试辅助路径
- Editor / 开发期排障路径
而不是适合每帧调用的实时功能。
后端差异
D3D12
当前 D3D12 会:
- 创建 readback buffer
- 临时创建 command allocator / command list
- 把 back buffer 从 render target 转成 copy source
- 执行
CopyTextureRegion - 再切回 render target
- 用 fence 等待 GPU 完成
- Map readback buffer 并写出 PPM
这条路径的关键现实约束是:
- 当前实现假定被捕获资源能从
D3D12_RESOURCE_STATE_RENDER_TARGET转到 copy source 再切回去 - 它更偏向当前 swap chain back buffer 这种调试用途
- 它是一次完整的同步读回流程,代价较高
OpenGL
OpenGL 路径会:
- 显式
MakeContextCurrent() glFinish()- 根据 swap chain 当前 back buffer 状态,选择直接从
GL_BACK读,或者临时创建 read FBO 从 back buffer texture 读 - 调用
glReadPixels() - 逐行倒序写出文件
“逐行倒序写出”这一点很重要,它说明 OpenGL 路径当前会主动做一次垂直翻转,以适配 OpenGL 常见的左下角原点读取习惯。
Vulkan
Vulkan 路径会:
- 创建 staging buffer 与 host visible 内存
- 创建临时 command pool / command buffer
- 把 swap chain image 从
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR转成VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL - 执行
vkCmdCopyImageToBuffer - 再切回 present layout
- 提交命令并等待 graphics queue idle
- Map staging 内存并写出 PPM
当前 Vulkan 写文件时是按内存中的行顺序直接写出,没有像 OpenGL 那样做显式垂直翻转。
因此当前跨后端截图结果在“图像朝向”上不应被假定完全一致。OpenGL 路径显式翻转,而 D3D12 / Vulkan 路径当前没有这一层统一处理。
与商业引擎工作流的关系
商业级引擎里通常会有多层截图路径:
- 轻量级 back buffer dump
- 离屏 render target 抓取
- 异步 readback
- 带格式转换、压缩、UI 叠加或平台分享的最终用户截图
当前 RHIScreenshot 只覆盖了其中最基础的一层,也就是“把当前 swap chain back buffer 读回并存盘”。这对于底层 bring-up 和图形测试已经很有价值,但还远不是完整截图系统。
生命周期
当前三套后端的 Shutdown 基本都是 no-op,但仍然建议统一按接口风格调用:
- Shutdown
delete
这样后续即使截图对象持有更多缓存资源,也不需要改调用习惯。
线程语义
当前接口没有声明线程安全保证。从源码行为看,更稳妥的工程规则是:
- 在渲染线程或 RHI 线程使用
- 调用时确保 device / swap chain 处于有效状态
- 不要并发对同一个 swap chain 做截图和重建
当前实现限制
- 当前抽象接口只能抓 swap chain,不支持直接抓任意
RHITexture - 只有同步读回,没有异步 capture 队列
- 只有 PPM 输出,没有 PNG/JPG/HDR 等格式支持
- 没有颜色空间、gamma、alpha、UI 合成等高级控制
- 输出目录若不存在,后端会直接失败,不会自动创建目录