OBS图形及渲染
+ -

obs_display结构体

2024-05-17 15 0

obs_display结构体用于存储需要将显卡渲染的图像输出到窗口显示的对象。

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_dipslay结构体时,使用结构体传递参数信息:

struct gs_window {
#if defined(_WIN32)
    void *hwnd;
#elif defined(__APPLE__)
    __unsafe_unretained id view;
#elif defined(__linux__) || defined(__FreeBSD__)
    /* I'm not sure how portable defining id to uint32_t is. */
    uint32_t id;
    void *display;
#endif
};

struct gs_init_data {
    struct gs_window window;
    uint32_t cx, cy;
    uint32_t num_backbuffers;
    enum gs_color_format format;
    enum gs_zstencil_format zsformat;
    uint32_t adapter;
};

函数为:

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中主窗口创建显示:

void OBSQTDisplay::CreateDisplay()
{
    if (display)
        return;

    if (destroying)
        return;

    if (!windowHandle()->isExposed())
        return;

    QSize size = GetPixelSize(this);

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

// win32下获取主窗口句柄hwnd
    if (!QTToGSWindow(windowHandle(), info.window))
        return;

    display = obs_display_create(&info, backgroundColor);

    emit DisplayCreated(this);
}

初始化display

初始化结构体display,参数为graphics_data

bool obs_display_init(struct obs_display *display,
              const struct gs_init_data *graphics_data)
{
    pthread_mutex_init_value(&display->draw_callbacks_mutex);
    pthread_mutex_init_value(&display->draw_info_mutex);

#if defined(_WIN32)
    /* Conservative test for NVIDIA flickering in multi-GPU setups */
    display->use_clear_workaround = gs_get_adapter_count() > 1 && !gs_can_adapter_fast_clear();
#elif defined(__APPLE__)
    /* Apple Silicon GL driver doesn't seem to track SRGB clears correctly */
    display->use_clear_workaround = true;
#else
    display->use_clear_workaround = false;
#endif

    if (graphics_data) {
        display->swap = gs_swapchain_create(graphics_data);
        if (!display->swap) {
            blog(LOG_ERROR, "obs_display_init: Failed to "
                    "create swap chain");
            return false;
        }

        const uint32_t cx = graphics_data->cx;
        const uint32_t cy = graphics_data->cy;
        display->cx = cx;
        display->cy = cy;
        display->next_cx = cx;
        display->next_cy = cy;
    }

    if (pthread_mutex_init(&display->draw_callbacks_mutex, NULL) != 0) {
        blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
        return false;
    }
    if (pthread_mutex_init(&display->draw_info_mutex, NULL) != 0) {
        blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
        return false;
    }

    display->enabled = true;
    return true;
}

在实始化时,会创建一个交换链,使用的函数为:gs_swapchain_create

gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data)
{
    struct gs_init_data new_data = *data;
    graphics_t *graphics = thread_graphics;

    if (!gs_valid_p("gs_swapchain_create", data))
        return NULL;

    if (new_data.num_backbuffers == 0)
        new_data.num_backbuffers = 1;

    return graphics->exports.device_swapchain_create(graphics->device,
                             &new_data);
}

其本质是d3d11导出的

gs_swapchain_t *device_swapchain_create(gs_device_t *device,
                    const struct gs_init_data *data)
{
    gs_swap_chain *swap = NULL;

    try {
        swap = new gs_swap_chain(device, data);
    } catch (const HRError &error) {
        blog(LOG_ERROR, "device_swapchain_create (D3D11): %s (%08lX)",
             error.str, error.hr);
        LogD3D11ErrorDetails(error, device);
    }

    return swap;
}

d3d11交换链

交换链是一个前台一个后台缓冲区,用于屏显防止闪屏。故有前后缓冲区。故称之为交换链。这里使用类结构体s_swap_chain的结构体来表示该对象。
交换链与窗口hwnd绑定,注意构造函数中对hwnd的初始化。

struct gs_swap_chain : gs_obj {
    HWND hwnd;
    gs_init_data initData;
    DXGI_SWAP_CHAIN_DESC swapDesc = {};
    gs_color_space space;

    gs_texture_2d target;
    gs_zstencil_buffer zs;
    ComPtr<IDXGISwapChain> swap;
    HANDLE hWaitable = NULL;

    void InitTarget(uint32_t cx, uint32_t cy);
    void InitZStencilBuffer(uint32_t cx, uint32_t cy);
    void Resize(uint32_t cx, uint32_t cy, gs_color_format format);
    void Init();

    void Rebuild(ID3D11Device *dev);

    inline void Release()
    {
        target.Release();
        zs.Release();
        if (hWaitable) {
            CloseHandle(hWaitable);
            hWaitable = NULL;
        }
        swap.Clear();
    }

    gs_swap_chain(gs_device *device, const gs_init_data *data);
    virtual ~gs_swap_chain();
};

交换链的主要函数实现在libos-d3d11.cpp中.

gs_swap_chain::gs_swap_chain(gs_device *device, const gs_init_data *data)
    : gs_obj(device, gs_type::gs_swap_chain),
      hwnd((HWND)data->window.hwnd),
      initData(*data),
      space(GS_CS_SRGB)
{
    DXGI_SWAP_EFFECT effect = DXGI_SWAP_EFFECT_DISCARD;
    UINT flags = 0;

    ComQIPtr<IDXGIFactory5> factory5 = device->factory;
    if (factory5) {
        initData.num_backbuffers = max(data->num_backbuffers, 2);

        effect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
    }

    space = make_swap_desc(device, swapDesc, &initData, effect, flags);
    HRESULT hr = device->factory->CreateSwapChain(device->device, &swapDesc,
                              swap.Assign());
    if (FAILED(hr))
        throw HRError("Failed to create swap chain", hr);

    /* Ignore Alt+Enter */
    device->factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);

    if (flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) {
        ComPtr<IDXGISwapChain2> swap2 = ComQIPtr<IDXGISwapChain2>(swap);
        hWaitable = swap2->GetFrameLatencyWaitableObject();
        if (hWaitable == NULL) {
            throw HRError("Failed to GetFrameLatencyWaitableObject",
                      hr);
        }
    }

    Init();
}

0 篇笔记 写笔记

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

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

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