渲染流程及纹理关系render_video
2024-10-17
56
0

- 渲染输出:场景合成后,画面存入 render_texture(RGB格式)。
- 色彩转换:通过GPU着色器将 render_texture 转换为YUV格式,输出到 convert_textures。
- 异步复制:copy_surfaces 介入。OBS不会直接让CPU去显存里读数据(这样会卡住总线),而是发起一个异步的GPU拷贝命令,将 convert_textures 的内容复制到 copy_surfaces。这个复制操作不会阻塞渲染管线。
- CPU读取:当拷贝完成后,copy_surfaces 指向的内存中就有了YUV数据。视频编码器(如libx264)可以直接读取这块内存进行压缩。
避免映射开销:如果每次编码时都把GPU显存映射到CPU地址空间,这个过程会产生较大的延迟和PCIe总线同步开销。copy_surfaces 作为固定内存池,减少了这种动态映射。
支持不同硬件路径:
- 在使用NVENC(NVIDIA编码器)等硬件编码器时,数据甚至可以不经过CPU内存,直接从 convert_textures 传给编码器硬件,此时 copy_surfaces 的角色会弱化或改变。ovi->gpu_conversion为true时才支持
- 但在使用软件编码或需要回采样显示时,copy_surfaces 是数据离开GPU进入CPU的必经之路。
RGB输出
最终实际渲染输出为video->render_texture
如果输出大小和图像大小不一致video->render_texture放缩放输出至output_texture
RGB转yuv(ovi->gpu_conversion为true)
实际输出的是YUV格式,使用的是conver_textures[]来存储,只不过这个数据在GPU中,还需要借用copy_surfaces将数据从GPU中拷贝至CPU中
video->gpu_conversion由高级中的视频格式决定,如果是RGBA则为false,为YUV为true
如果ovi->gpu_conversion为false,则还是RGBA格式
if (video->gpu_conversion)
{
//YUV转换
if (!obs_init_gpu_copy_surfaces(ovi, i))
return false;
}
else
{
//RGBA
video->copy_surfaces[i][0] =gs_stagesurface_create(ovi->output_width,ovi->output_height, GS_RGBA);
if (!video->copy_surfaces[i][0])
return false;
}
渲染主函数为render_video
static inline void render_video(struct obs_core_video *video, bool raw_active,
const bool gpu_active, int cur_texture)
{
gs_begin_scene();
gs_enable_depth_test(false);
gs_set_cull_mode(GS_NEITHER);
render_main_texture(video);
if (raw_active || gpu_active) {
gs_texture_t *texture = render_output_texture(video);
#ifdef _WIN32
if (gpu_active)
gs_flush();
#endif
if (video->gpu_conversion)
render_convert_texture(video, texture);
#ifdef _WIN32
if (gpu_active) {
gs_flush();
output_gpu_encoders(video, raw_active);
}
#endif
if (raw_active)
stage_output_texture(video, cur_texture);
}
gs_set_render_target(NULL, NULL);
gs_enable_blending(true);
gs_end_scene();
}
render_main_texture将各个加入的视频图片渲染到video->render_texture上
static const char *render_main_texture_name = "render_main_texture";
static inline void render_main_texture(struct obs_core_video *video)
{
profile_start(render_main_texture_name);
GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_MAIN_TEXTURE,render_main_texture_name);
struct vec4 clear_color;
vec4_set(&clear_color, 0.0f, 0.0f, 0.0f, 0.0f);
gs_set_render_target(video->render_texture, NULL);
gs_clear(GS_CLEAR_COLOR, &clear_color, 1.0f, 0);
set_render_size(video->base_width, video->base_height);
pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
for (size_t i = obs->data.draw_callbacks.num; i > 0; i--) {
struct draw_callback *callback;
callback = obs->data.draw_callbacks.array + (i - 1);
callback->draw(callback->param, video->base_width,video->base_height);
}
pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
obs_view_render(&obs->data.main_view);
video->texture_rendered = true;
GS_DEBUG_MARKER_END();
profile_end(render_main_texture_name);
}
如果画布和输出大小不致,则需要将video->render_texture按比例缩放到video->output_texture上,如果相等,就直接用video->render_texture。
static inline gs_texture_t *render_output_texture(struct obs_core_video *video)
{
gs_texture_t *texture = video->render_texture;
gs_texture_t *target = video->output_texture;
uint32_t width = gs_texture_get_width(target);
uint32_t height = gs_texture_get_height(target);
gs_effect_t *effect = get_scale_effect(video, width, height);
gs_technique_t *tech;
if (video->ovi.output_format == VIDEO_FORMAT_RGBA)
{
tech = gs_effect_get_technique(effect, "DrawAlphaDivide");
}
else
{
if ((effect == video->default_effect) && (width == video->base_width) && (height == video->base_height))
{
return texture;
}
tech = gs_effect_get_technique(effect, "Draw");
}
profile_start(render_output_texture_name);
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
gs_eparam_t *bres = gs_effect_get_param_by_name(effect, "base_dimension");
gs_eparam_t *bres_i = gs_effect_get_param_by_name(effect, "base_dimension_i");
size_t passes, i;
gs_set_render_target(target, NULL);
set_render_size(width, height);
if (bres)
{
struct vec2 base;
vec2_set(&base, (float)video->base_width, (float)video->base_height);
gs_effect_set_vec2(bres, &base);
}
if (bres_i)
{
struct vec2 base_i;
vec2_set(&base_i, 1.0f / (float)video->base_width, 1.0f / (float)video->base_height);
gs_effect_set_vec2(bres_i, &base_i);
}
gs_effect_set_texture_srgb(image, texture);
gs_enable_framebuffer_srgb(true);
gs_enable_blending(false);
passes = gs_technique_begin(tech);
for (i = 0; i < passes; i++) {
gs_technique_begin_pass(tech, i);
gs_draw_sprite(texture, 0, width, height);
gs_technique_end_pass(tech);
}
gs_technique_end(tech);
gs_enable_blending(true);
gs_enable_framebuffer_srgb(false);
profile_end(render_output_texture_name);
return target;
}
- 如果输出为RGB格式,使用technique DrawAlphaDivide
- 如果为YUV,则使用Draw
OBS图形及渲染





