7.9 KiB
7.9 KiB
素材读入强制转换为 RGBAF32 并内部全链路使用 F32 处理 — 实施计划
1. 现状分析
1.1 视频读入位置
视频/图像素材在以下位置被读入并解码为 GPU Texture:
| 层级 | 文件 | 职责 |
|---|---|---|
| 解码接口 | app/codec/decoder.h / .cpp |
基类 Decoder,定义 RetrieveVideo(RetrieveVideoParams) 公共接口 |
| FFmpeg 解码 | app/codec/ffmpeg/ffmpegdecoder.cpp |
FFmpegDecoder::RetrieveVideoInternal() —— 核心视频解码路径 |
| OIIO 解码 | app/codec/oiio/oiiodecoder.cpp |
OIIODecoder::RetrieveVideoInternal() —— 静态图片解码路径 |
| 渲染触发 | app/render/renderprocessor.cpp |
ProcessVideoFootage() —— 在节点图遍历中触发解码,并做颜色管理转换 |
| 遍历调度 | app/node/traverser.cpp |
ResolveJobs() —— 将 FootageJob 分发给 ProcessVideoFootage() |
数据流:
文件 → FFmpegDecoder::RetrieveVideoInternal()
→ RetrieveFrame() 解码出 AVFrame
→ PreProcessFrame() CPU 缩放/格式转换 (sws_scale_frame)
→ ProcessFrameIntoTexture() 上传为 GPU Texture
→ YUV 格式:上传为 3 个 plane texture + YUV→RGB shader
→ RGBA/RGBA64LE:直接 glTexSubImage2D 上传
→ RenderProcessor::ProcessVideoFootage()
→ BlitColorManaged() OCIO 颜色空间转换 shader
→ 进入节点图后续处理
1.2 像素格式体系
- 核心枚举:
ext/core/include/olive/core/render/pixelformat.h定义PixelFormat::U8 / U16 / F16 / F32 - GPU 格式映射:
app/render/opengl/openglrenderer.cpp已将F32 + 4ch映射到GL_RGBA32F / GL_FLOAT - 内部工作格式:
NodeTraverser::GetCacheVideoParams().format()决定节点图内部缓存格式 - 项目默认配置:
app/config/config.cpp中OnlinePixelFormat = F32,OfflinePixelFormat = F16,说明设计意图就是在线编辑使用 F32
1.3 当前 F32 支持的关键缺失
-
FFmpegDecoder::GetNativePixelFormat()不识别 F32 FFmpeg 格式- 仅映射
RGBA → U8、RGBA64 → U16 AV_PIX_FMT_RGBAF32、AV_PIX_FMT_RGBF32等落入default: INVALID
- 仅映射
-
IsPixelFormatGLSLCompatible()未将 RGBAF32 列为 GLSL 兼容- 这会导致即使解码器输出 RGBAF32,也会强制走
sws_scale_frameCPU 转换路径
- 这会导致即使解码器输出 RGBAF32,也会强制走
-
ProcessFrameIntoTexture()直接上传路径缺少 RGBAF32 分支- 当前只有
YUV...和RGBA / RGBA64LE两个直接上传分支,没有RGBAF32等直接上传路径
- 当前只有
-
PreProcessFrame()的sws_scale_frame目标格式选择需验证 F32 支持FFmpegUtils::GetCompatiblePixelFormat(..., maximum=F32)理论上应返回AV_PIX_FMT_RGBAF32,但需实测验证
-
OIIO 解码器已原生支持 F32(FLOAT → F32),无需修改
2. 目标
- 读入时转换: 无论源素材格式(YUV、U8、U16、F16 等),在解码器层面统一转换为 RGBAF32 后上传 GPU
- 内部全链路 F32: 节点图遍历、效果处理、合成、缓存等内部环节全部使用
PixelFormat::F32(4 通道) - 导出保持灵活: 导出/编码时从 F32 转换为目标格式,保持现有编码逻辑
3. 实施方案:全局强制 F32
思路: 将 F32 作为唯一的内部工作格式,在解码器出口强制转换。
改动点:
-
解码器层强制 F32 输出
FFmpegDecoder::RetrieveVideoInternal():- 修改
RetrieveVideoParams或内部逻辑,令maximum_format固定为F32 - 在
PreProcessFrame()中,若源格式非 RGBAF32,通过sws_scale_frame转换到AV_PIX_FMT_RGBAF32 - 在
ProcessFrameIntoTexture()中增加AV_PIX_FMT_RGBAF32直接上传分支(GL_RGBA32F / GL_FLOAT)
- 修改
OIIODecoder::RetrieveVideoInternal():- OIIO 读入后,若格式非 F32,通过
Frame::convert(PixelFormat::F32)转换,再上传
- OIIO 读入后,若格式非 F32,通过
-
修复 F32 格式映射
FFmpegDecoder::GetNativePixelFormat()增加AV_PIX_FMT_RGBAF32 → PixelFormat::F32、AV_PIX_FMT_RGBF32 → PixelFormat::F32FFmpegDecoder::GetNativeChannelCount()增加对应分支IsPixelFormatGLSLCompatible()增加AV_PIX_FMT_RGBAF32(可选,因为强制转换后解码器输出就是 RGBAF32)
-
内部工作格式锁定 F32
- 在
NodeTraverser初始化或RenderProcessor创建时,SetCacheVideoParams()强制format = PixelFormat::F32 - 移除用户层对工作格式的可选配置(或保留配置但忽略/默认 F32)
traverser.cpp中FootageJob、GenerateJob、ColorTransformJob的格式设置已经使用GetCacheVideoParams().format(),因此只需确保基类参数是 F32 即可
- 在
-
导出层适配
FFmpegEncoder的输入当前通过avfilter图做格式转换,源为 F32 时:FFmpegUtils::GetFFmpegPixelFormat(F32, 4)已返回AV_PIX_FMT_RGBAF32- 验证 filter graph 的
buffersource 和formatfilter 能否正确处理RGBAF32
RenderProcessor::GenerateFrame()下载 GPU texture 到FramePtr时,DownloadFromTexture()已支持GL_FLOAT,直接得到 F32 CPU buffer
4. 关键文件与修改清单
| 文件 | 修改内容 |
|---|---|
app/codec/ffmpeg/ffmpegdecoder.cpp |
① GetNativePixelFormat() 增加 RGBAF32/RGBF32 → F32 映射② GetNativeChannelCount() 增加对应分支③ IsPixelFormatGLSLCompatible() 增加 RGBAF32④ ProcessFrameIntoTexture() 增加 RGBAF32 直接上传分支⑤ PreProcessFrame() 确保 divider=1 且格式为 RGBAF32 时跳过 CPU 转换 |
app/codec/oiio/oiiodecoder.cpp |
RetrieveVideoInternal() 上传前若 frame.format() != F32 则调用 convert(F32) |
app/node/traverser.cpp 或 app/render/renderprocessor.cpp |
初始化时强制 SetCacheVideoParams().format = F32 |
app/codec/ffmpeg/ffmpegencoder.cpp |
验证 filter graph 对 RGBAF32 source 的处理,必要时调整 |
app/render/opengl/openglrenderer.cpp |
确认 GL_RGBA32F / GL_FLOAT 路径完整,补充必要错误检查 |
app/codec/ffmpeg/ffmpegutils.cpp |
验证 GetCompatiblePixelFormat(maximum=F32) 的行为 |
5. 风险评估
| 风险 | 说明 | 缓解措施 |
|---|---|---|
| 内存带宽 ×4 | F32 是 U8 的 4 倍、U16/F16 的 2 倍,显存和内存占用显著增加 | 这是预期代价;OfflinePixelFormat 机制可继续用于代理预览,降低分辨率同时用 F16 减少带宽 |
| FFmpeg swscale 对 RGBAF32 支持 | sws_scale_frame 是否能正确处理 AV_PIX_FMT_RGBAF32 作为目标格式需验证 |
先写单元测试验证;若不支持,可用 OIIO Frame::convert() 作为 fallback,或在 GPU 上通过 shader 做格式转换 |
| OFX 插件兼容性 | 大部分 OFX 插件支持 kOfxBitDepthFloat,但仍有少数可能只支持 U8/U16 |
PluginRenderer 已有格式转换路径,F32 的支持比 F16 更成熟 |
| 性能回归 | YUV→RGB 原来在 GPU 走 shader,若强制先转 RGBAF32 再上传,可能需要调整流程 | YUV 素材仍保留 GPU shader 转换路径,只是 shader 输出目标 texture 格式改为 F32(OpenGL 已支持 GL_RGBA32F 作为 render target) |
| 缓存文件体积翻倍 | 帧缓存从 U8/U16 改为 F32 后,磁盘缓存体积增大 | 可接受;必要时调整缓存策略或压缩 |
6. 建议的实施顺序
- 第一阶段: 修复
FFmpegDecoderF32 映射 + 增加 RGBAF32 直接上传分支,编写解码器单元测试 - 第二阶段: 在
OIIODecoder添加强制 F32 转换 - 第三阶段: 锁定内部工作格式为 F32,验证节点图全链路
- 第四阶段: 验证导出编码路径,确认 filter graph 对 RGBAF32 的处理
- 第五阶段: 性能测试与回归测试
7. 决策点
- 是否保留
OfflinePixelFormat = F16的代理降级机制?还是连 proxy 也强制 F32? - 若保留代理降级,是否需要在解码器层根据 online/offline 模式选择输出格式?