OBS图形及渲染
+ -

渲染流程及纹理关系render_video

2024-10-17 56 0

160512815918

  • 渲染输出:场景合成后,画面存入 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

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!