mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
f88300a613
Laptops that render OBS displays on NVIDIA, and display through Intel have been seen flickering, and we think the driver is not properly handling swap chain buffers that have been cleared but not drawn to. To work around this, we use a draw to simulate a render target clear. This is not as efficient, so hopefully NVIDIA will fix this soon.
304 lines
7.4 KiB
C
304 lines
7.4 KiB
C
/******************************************************************************
|
|
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
#include "graphics/vec4.h"
|
|
#include "obs.h"
|
|
#include "obs-internal.h"
|
|
|
|
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 (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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void obs_display_free(obs_display_t *display)
|
|
{
|
|
pthread_mutex_destroy(&display->draw_callbacks_mutex);
|
|
pthread_mutex_destroy(&display->draw_info_mutex);
|
|
da_free(display->draw_callbacks);
|
|
|
|
if (display->swap) {
|
|
gs_swapchain_destroy(display->swap);
|
|
display->swap = NULL;
|
|
}
|
|
}
|
|
|
|
void obs_display_destroy(obs_display_t *display)
|
|
{
|
|
if (display) {
|
|
pthread_mutex_lock(&obs->data.displays_mutex);
|
|
if (display->prev_next)
|
|
*display->prev_next = display->next;
|
|
if (display->next)
|
|
display->next->prev_next = display->prev_next;
|
|
pthread_mutex_unlock(&obs->data.displays_mutex);
|
|
|
|
obs_enter_graphics();
|
|
obs_display_free(display);
|
|
obs_leave_graphics();
|
|
|
|
bfree(display);
|
|
}
|
|
}
|
|
|
|
void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy)
|
|
{
|
|
if (!display)
|
|
return;
|
|
|
|
pthread_mutex_lock(&display->draw_info_mutex);
|
|
|
|
display->next_cx = cx;
|
|
display->next_cy = cy;
|
|
|
|
pthread_mutex_unlock(&display->draw_info_mutex);
|
|
}
|
|
|
|
void obs_display_update_color_space(obs_display_t *display)
|
|
{
|
|
if (!display)
|
|
return;
|
|
|
|
pthread_mutex_lock(&display->draw_info_mutex);
|
|
|
|
display->update_color_space = true;
|
|
|
|
pthread_mutex_unlock(&display->draw_info_mutex);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void obs_display_remove_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_erase_item(display->draw_callbacks, &data);
|
|
pthread_mutex_unlock(&display->draw_callbacks_mutex);
|
|
}
|
|
|
|
/* NVIDIA clear can sometimes cause flickering */
|
|
#define NVIDIA_BROKEN_CLEAR 1
|
|
|
|
static inline bool render_display_begin(struct obs_display *display,
|
|
uint32_t cx, uint32_t cy,
|
|
bool update_color_space)
|
|
{
|
|
struct vec4 clear_color;
|
|
|
|
gs_load_swapchain(display->swap);
|
|
|
|
if ((display->cx != cx) || (display->cy != cy)) {
|
|
gs_resize(cx, cy);
|
|
display->cx = cx;
|
|
display->cy = cy;
|
|
} else if (update_color_space) {
|
|
gs_update_color_space();
|
|
}
|
|
|
|
const bool success = gs_is_present_ready();
|
|
if (success) {
|
|
gs_begin_scene();
|
|
|
|
if (gs_get_color_space() == GS_CS_SRGB)
|
|
vec4_from_rgba(&clear_color, display->background_color);
|
|
else
|
|
vec4_from_rgba_srgb(&clear_color,
|
|
display->background_color);
|
|
clear_color.w = 1.0f;
|
|
|
|
#if !NVIDIA_BROKEN_CLEAR
|
|
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH | GS_CLEAR_STENCIL,
|
|
&clear_color, 1.0f, 0);
|
|
#endif
|
|
|
|
gs_enable_depth_test(false);
|
|
/* gs_enable_blending(false); */
|
|
gs_set_cull_mode(GS_NEITHER);
|
|
|
|
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
|
gs_set_viewport(0, 0, cx, cy);
|
|
|
|
#if NVIDIA_BROKEN_CLEAR
|
|
gs_effect_t *const solid_effect = obs->video.solid_effect;
|
|
gs_effect_set_vec4(gs_effect_get_param_by_name(solid_effect,
|
|
"color"),
|
|
&clear_color);
|
|
while (gs_effect_loop(solid_effect, "Solid"))
|
|
gs_draw_sprite(NULL, 0, cx, cy);
|
|
#endif
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static inline void render_display_end()
|
|
{
|
|
gs_end_scene();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
void obs_display_set_enabled(obs_display_t *display, bool enable)
|
|
{
|
|
if (display)
|
|
display->enabled = enable;
|
|
}
|
|
|
|
bool obs_display_enabled(obs_display_t *display)
|
|
{
|
|
return display ? display->enabled : false;
|
|
}
|
|
|
|
void obs_display_set_background_color(obs_display_t *display, uint32_t color)
|
|
{
|
|
if (display)
|
|
display->background_color = color;
|
|
}
|
|
|
|
void obs_display_size(obs_display_t *display, uint32_t *width, uint32_t *height)
|
|
{
|
|
*width = 0;
|
|
*height = 0;
|
|
|
|
if (display) {
|
|
pthread_mutex_lock(&display->draw_info_mutex);
|
|
|
|
*width = display->cx;
|
|
*height = display->cy;
|
|
|
|
pthread_mutex_unlock(&display->draw_info_mutex);
|
|
}
|
|
}
|