obs_display及render_displays
一个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
- obs_graphics_thread_loop
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保存的,是由其子项渲染后的最终结果。