OBS图形及渲染
+ -

obs_display及render_displays

2024-06-03 23 0

一个obs_display_t是一个渲染组,其与窗口HWND绑定,也可以认为其代表一个交换链。

render_displays是显示渲染的结果,可以有多个,与窗口HWND绑定

obs_display_t创建

在OBS中其实就是输出主窗口。其结绝体如下:

/* displays */

struct obs_display {
    bool update_color_space;
    bool enabled;
    uint32_t cx, cy;
    uint32_t next_cx, next_cy;
    uint32_t background_color;

    gs_swapchain_t *swap; //交换链

    pthread_mutex_t draw_callbacks_mutex;
    pthread_mutex_t draw_info_mutex;
    DARRAY(struct draw_callback) draw_callbacks;
    bool use_clear_workaround;

    struct obs_display *next;
    struct obs_display **prev_next;
};

其创建一个obs_display_t时,需要的参数如下:

struct gs_init_data {
    struct gs_window window; //窗口句柄
    uint32_t cx, cy;  //长宽
    uint32_t num_backbuffers;
    enum gs_color_format format;//GS_BGRA
    enum gs_zstencil_format zsformat;/GS_ZS_NONE
    uint32_t adapter;  //适配器索引,应该与窗口句柄互斥
};

所以其创建时:

    gs_init_data info = {};
    info.cx = size.width();
    info.cy = size.height();
    info.format = GS_BGRA;
    info.zsformat = GS_ZS_NONE;

    if (!QTToGSWindow(windowHandle(), info.window))
        return;

    display = obs_display_create(&info, backgroundColor);

新创建的obs_display_t其实是以链表互连,其头指针为obs->data.first_display。新增加一个时,也仅是互链指针而已

obs_display_t *obs_display_create(const struct gs_init_data *graphics_data,
                  uint32_t background_color)
{
    struct obs_display *display = bzalloc(sizeof(struct obs_display));

    gs_enter_context(obs->video.graphics);

    display->background_color = background_color;

//初始化交换链
    if (!obs_display_init(display, graphics_data))
    {
        obs_display_destroy(display);
        display = NULL;
    }
    else
    {
        //通过链表相连
        pthread_mutex_lock(&obs->data.displays_mutex);
        display->prev_next = &obs->data.first_display;
        display->next = obs->data.first_display;
        obs->data.first_display = display;
        if (display->next)
            display->next->prev_next = &display->next;
        pthread_mutex_unlock(&obs->data.displays_mutex);
    }

    gs_leave_context();
    return display;
}

obs_display_t渲染调用

在视频渲染线程中:

  • obs_graphics_thread
    • obs_graphics_thread_loop
      • render_displays

render_displays函数其实就是从链表中找到第一个obs_display,并调用render_display

static inline void render_displays(void)
{
    /* render extra displays/swaps */
    pthread_mutex_lock(&obs->data.displays_mutex);

    display = obs->data.first_display;
    while (display)
    {
        render_display(display);
        display = display->next;
    }

    pthread_mutex_unlock(&obs->data.displays_mutex);
}

对于render_display函数,对于其draw_callbacks进行调用绘图,最终调用gs_present进行上屏


void render_display(struct obs_display *display)
{
    uint32_t cx, cy;
    bool update_color_space;

    if (!display || !display->enabled)
        return;

    /* -------------------------------------------- */
    pthread_mutex_lock(&display->draw_info_mutex);

    cx = display->next_cx;
    cy = display->next_cy;
    update_color_space = display->update_color_space;
    display->update_color_space = false;

    pthread_mutex_unlock(&display->draw_info_mutex);
    /* -------------------------------------------- */


    if (render_display_begin(display, cx, cy, update_color_space)) 
    {
        GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DISPLAY, "obs_display");

        pthread_mutex_lock(&display->draw_callbacks_mutex);
        for (size_t i = 0; i < display->draw_callbacks.num; i++)
        {
            struct draw_callback *callback;
            callback = display->draw_callbacks.array + i;
            callback->draw(callback->param, cx, cy);
        }
        pthread_mutex_unlock(&display->draw_callbacks_mutex);

        render_display_end();
        GS_DEBUG_MARKER_END();

        //上屏
        gs_present();
    }
}

render_display_begin

render_display_begin用于初始化上屏渲染,其中内部很重要的一个调用是gs_load_swapchain

static inline bool render_display_begin(struct obs_display *display,
                    uint32_t cx, uint32_t cy,
                    bool update_color_space)
{
...
    gs_load_swapchain(display->swap);
...
}

gs_load_swapchain对于d3d11实质为device_load_swapchain

void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swapchain)
{
    gs_texture_t *target = device->curRenderTarget;
    gs_zstencil_t *zs = device->curZStencilBuffer;
    bool is_cube =    device->curRenderTarget? (device->curRenderTarget->type == GS_TEXTURE_CUBE): false;

    if (device->curSwapChain) {
        if (target == &device->curSwapChain->target)
            target = NULL;
        if (zs == &device->curSwapChain->zs)
            zs = NULL;
    }

    //设置swapchain为当前交换链
    device->curSwapChain = swapchain;

    if (is_cube) {
        device_set_cube_render_target(device, target,device->curRenderSide, zs);
    } else {
        const enum gs_color_space space = swapchain ? swapchain->space GS_CS_SRGB;
        device_set_render_target_internal(device, target, zs, space);
    }
}

首选设置为当前的交换链,然后
由于是非cube,则调用device_set_render_target_internal,则根据当前交换链的target为渲染目标。

draw_callbacks

可以看到,分别使用draw_callbacks来进行绘制。怎么绘制以及怎么图像融合,需要看draw_callbacks的相关信息。
对于每一个draw_callback,其是一个回调函数以及回调函数对应的参数。

struct draw_callback {
    void (*draw)(void *param, uint32_t cx, uint32_t cy);
    void *param;
};

其通过函数obs_display_add_draw_callback增加的。

void obs_display_add_draw_callback(obs_display_t *display,
                   void (*draw)(void *param, uint32_t cx,uint32_t cy),
                   void *param)
{
    if (!display)
        return;

    struct draw_callback data = {draw, param};

    pthread_mutex_lock(&display->draw_callbacks_mutex);
    da_push_back(display->draw_callbacks, &data);
    pthread_mutex_unlock(&display->draw_callbacks_mutex);
}

使用此函数的类有:

  • OBSBasicFilters::OBSBasicFilters 滤镜的
  • OBSBasicInteraction::OBSBasicInteraction
  • OBSBasic::CreateProgramDisplay
  • OBSBasic::OBSInit
    • OBSBasicProperties::OBSBasicProperties /OBSBasicProperties::DrawPreview
    • OBSBasicProperties::OBSBasicProperties/OBSBasicProperties::DrawTransitionPreview
  • OBSProjector::OBSProjector

这里只关注OBSBasic::RenderMain

void OBSBasic::RenderMain(void *data, uint32_t, uint32_t)
{
    GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "RenderMain");

    OBSBasic *window = static_cast<OBSBasic *>(data);
    obs_video_info ovi;
    obs_get_video_info(&ovi);

    window->previewCX = int(window->previewScale * float(ovi.base_width));
    window->previewCY = int(window->previewScale * float(ovi.base_height));

    gs_viewport_push();
    gs_projection_push();

    obs_display_t *display = window->ui->preview->GetDisplay();
    uint32_t width, height;
    obs_display_size(display, &width, &height);
    float right = float(width) - window->previewX;
    float bottom = float(height) - window->previewY;

    gs_ortho(-window->previewX, right, -window->previewY, bottom, -100.0f, 100.0f);

    window->ui->preview->DrawOverflow();

    /* --------------------------------------- */

    gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height), -100.0f, 100.0f);
    gs_set_viewport(window->previewX, window->previewY, window->previewCX,    window->previewCY);

    if (window->IsPreviewProgramMode())
    {
        window->DrawBackdrop(float(ovi.base_width),float(ovi.base_height));

        OBSScene scene = window->GetCurrentScene();
        obs_source_t *source = obs_scene_get_source(scene);
        if (source)
            obs_source_video_render(source);
    }
    else
    {
        obs_render_main_texture_src_color_only();
    }
    gs_load_vertexbuffer(nullptr);

    /* --------------------------------------- */

    gs_ortho(-window->previewX, right, -window->previewY, bottom, -100.0f, 100.0f);
    gs_reset_viewport();

    uint32_t targetCX = window->previewCX;
    uint32_t targetCY = window->previewCY;

    if (window->drawSafeAreas) {
        RenderSafeAreas(window->actionSafeMargin, targetCX, targetCY);
        RenderSafeAreas(window->graphicsSafeMargin, targetCX, targetCY);
        RenderSafeAreas(window->fourByThreeSafeMargin, targetCX,targetCY);
        RenderSafeAreas(window->leftLine, targetCX, targetCY);
        RenderSafeAreas(window->topLine, targetCX, targetCY);
        RenderSafeAreas(window->rightLine, targetCX, targetCY);
    }

    window->ui->preview->DrawSceneEditing();

    if (window->drawSpacingHelpers)
        window->ui->preview->DrawSpacingHelpers();

    /* --------------------------------------- */

    gs_projection_pop();
    gs_viewport_pop();

    GS_DEBUG_MARKER_END();
}

这其实是获取当前scene,然后调用obs_source_video_render进行渲染。这其实应是将该scene中渲染的最终结果到渲染到交换链上进行显示。
而scene中渲染的结果是由render_target保存的,是由其子项渲染后的最终结果。

0 篇笔记 写笔记

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

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

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