obs-display.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "graphics/vec4.h"
  15. #include "obs.h"
  16. #include "obs-internal.h"
  17. bool obs_display_init(struct obs_display *display,
  18. const struct gs_init_data *graphics_data)
  19. {
  20. pthread_mutex_init_value(&display->draw_callbacks_mutex);
  21. pthread_mutex_init_value(&display->draw_info_mutex);
  22. #if defined(_WIN32)
  23. /* Conservative test for NVIDIA flickering in multi-GPU setups */
  24. display->use_clear_workaround = gs_get_adapter_count() > 1;
  25. #elif defined(__APPLE__)
  26. /* Apple Silicon GL driver doesn't seem to track SRGB clears correctly */
  27. display->use_clear_workaround = true;
  28. #else
  29. display->use_clear_workaround = false;
  30. #endif
  31. if (graphics_data) {
  32. display->swap = gs_swapchain_create(graphics_data);
  33. if (!display->swap) {
  34. blog(LOG_ERROR, "obs_display_init: Failed to "
  35. "create swap chain");
  36. return false;
  37. }
  38. const uint32_t cx = graphics_data->cx;
  39. const uint32_t cy = graphics_data->cy;
  40. display->cx = cx;
  41. display->cy = cy;
  42. display->next_cx = cx;
  43. display->next_cy = cy;
  44. }
  45. if (pthread_mutex_init(&display->draw_callbacks_mutex, NULL) != 0) {
  46. blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
  47. return false;
  48. }
  49. if (pthread_mutex_init(&display->draw_info_mutex, NULL) != 0) {
  50. blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
  51. return false;
  52. }
  53. display->enabled = true;
  54. return true;
  55. }
  56. obs_display_t *obs_display_create(const struct gs_init_data *graphics_data,
  57. uint32_t background_color)
  58. {
  59. struct obs_display *display = bzalloc(sizeof(struct obs_display));
  60. gs_enter_context(obs->video.graphics);
  61. display->background_color = background_color;
  62. if (!obs_display_init(display, graphics_data)) {
  63. obs_display_destroy(display);
  64. display = NULL;
  65. } else {
  66. pthread_mutex_lock(&obs->data.displays_mutex);
  67. display->prev_next = &obs->data.first_display;
  68. display->next = obs->data.first_display;
  69. obs->data.first_display = display;
  70. if (display->next)
  71. display->next->prev_next = &display->next;
  72. pthread_mutex_unlock(&obs->data.displays_mutex);
  73. }
  74. gs_leave_context();
  75. return display;
  76. }
  77. void obs_display_free(obs_display_t *display)
  78. {
  79. pthread_mutex_destroy(&display->draw_callbacks_mutex);
  80. pthread_mutex_destroy(&display->draw_info_mutex);
  81. da_free(display->draw_callbacks);
  82. if (display->swap) {
  83. gs_swapchain_destroy(display->swap);
  84. display->swap = NULL;
  85. }
  86. }
  87. void obs_display_destroy(obs_display_t *display)
  88. {
  89. if (display) {
  90. pthread_mutex_lock(&obs->data.displays_mutex);
  91. if (display->prev_next)
  92. *display->prev_next = display->next;
  93. if (display->next)
  94. display->next->prev_next = display->prev_next;
  95. pthread_mutex_unlock(&obs->data.displays_mutex);
  96. obs_enter_graphics();
  97. obs_display_free(display);
  98. obs_leave_graphics();
  99. bfree(display);
  100. }
  101. }
  102. void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy)
  103. {
  104. if (!display)
  105. return;
  106. pthread_mutex_lock(&display->draw_info_mutex);
  107. display->next_cx = cx;
  108. display->next_cy = cy;
  109. pthread_mutex_unlock(&display->draw_info_mutex);
  110. }
  111. void obs_display_update_color_space(obs_display_t *display)
  112. {
  113. if (!display)
  114. return;
  115. pthread_mutex_lock(&display->draw_info_mutex);
  116. display->update_color_space = true;
  117. pthread_mutex_unlock(&display->draw_info_mutex);
  118. }
  119. void obs_display_add_draw_callback(obs_display_t *display,
  120. void (*draw)(void *param, uint32_t cx,
  121. uint32_t cy),
  122. void *param)
  123. {
  124. if (!display)
  125. return;
  126. struct draw_callback data = {draw, param};
  127. pthread_mutex_lock(&display->draw_callbacks_mutex);
  128. da_push_back(display->draw_callbacks, &data);
  129. pthread_mutex_unlock(&display->draw_callbacks_mutex);
  130. }
  131. void obs_display_remove_draw_callback(obs_display_t *display,
  132. void (*draw)(void *param, uint32_t cx,
  133. uint32_t cy),
  134. void *param)
  135. {
  136. if (!display)
  137. return;
  138. struct draw_callback data = {draw, param};
  139. pthread_mutex_lock(&display->draw_callbacks_mutex);
  140. da_erase_item(display->draw_callbacks, &data);
  141. pthread_mutex_unlock(&display->draw_callbacks_mutex);
  142. }
  143. static inline bool render_display_begin(struct obs_display *display,
  144. uint32_t cx, uint32_t cy,
  145. bool update_color_space)
  146. {
  147. struct vec4 clear_color;
  148. gs_load_swapchain(display->swap);
  149. if ((display->cx != cx) || (display->cy != cy)) {
  150. gs_resize(cx, cy);
  151. display->cx = cx;
  152. display->cy = cy;
  153. } else if (update_color_space) {
  154. gs_update_color_space();
  155. }
  156. const bool success = gs_is_present_ready();
  157. if (success) {
  158. gs_begin_scene();
  159. if (gs_get_color_space() == GS_CS_SRGB)
  160. vec4_from_rgba(&clear_color, display->background_color);
  161. else
  162. vec4_from_rgba_srgb(&clear_color,
  163. display->background_color);
  164. clear_color.w = 1.0f;
  165. const bool use_clear_workaround = display->use_clear_workaround;
  166. uint32_t clear_flags = GS_CLEAR_DEPTH | GS_CLEAR_STENCIL;
  167. if (!use_clear_workaround)
  168. clear_flags |= GS_CLEAR_COLOR;
  169. gs_clear(clear_flags, &clear_color, 1.0f, 0);
  170. gs_enable_depth_test(false);
  171. /* gs_enable_blending(false); */
  172. gs_set_cull_mode(GS_NEITHER);
  173. gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
  174. gs_set_viewport(0, 0, cx, cy);
  175. if (use_clear_workaround) {
  176. gs_effect_t *const solid_effect =
  177. obs->video.solid_effect;
  178. gs_effect_set_vec4(gs_effect_get_param_by_name(
  179. solid_effect, "color"),
  180. &clear_color);
  181. while (gs_effect_loop(solid_effect, "Solid"))
  182. gs_draw_sprite(NULL, 0, cx, cy);
  183. }
  184. }
  185. return success;
  186. }
  187. static inline void render_display_end()
  188. {
  189. gs_end_scene();
  190. }
  191. void render_display(struct obs_display *display)
  192. {
  193. uint32_t cx, cy;
  194. bool update_color_space;
  195. if (!display || !display->enabled)
  196. return;
  197. /* -------------------------------------------- */
  198. pthread_mutex_lock(&display->draw_info_mutex);
  199. cx = display->next_cx;
  200. cy = display->next_cy;
  201. update_color_space = display->update_color_space;
  202. display->update_color_space = false;
  203. pthread_mutex_unlock(&display->draw_info_mutex);
  204. /* -------------------------------------------- */
  205. if (render_display_begin(display, cx, cy, update_color_space)) {
  206. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DISPLAY, "obs_display");
  207. pthread_mutex_lock(&display->draw_callbacks_mutex);
  208. for (size_t i = 0; i < display->draw_callbacks.num; i++) {
  209. struct draw_callback *callback;
  210. callback = display->draw_callbacks.array + i;
  211. callback->draw(callback->param, cx, cy);
  212. }
  213. pthread_mutex_unlock(&display->draw_callbacks_mutex);
  214. render_display_end();
  215. GS_DEBUG_MARKER_END();
  216. gs_present();
  217. }
  218. }
  219. void obs_display_set_enabled(obs_display_t *display, bool enable)
  220. {
  221. if (display)
  222. display->enabled = enable;
  223. }
  224. bool obs_display_enabled(obs_display_t *display)
  225. {
  226. return display ? display->enabled : false;
  227. }
  228. void obs_display_set_background_color(obs_display_t *display, uint32_t color)
  229. {
  230. if (display)
  231. display->background_color = color;
  232. }
  233. void obs_display_size(obs_display_t *display, uint32_t *width, uint32_t *height)
  234. {
  235. *width = 0;
  236. *height = 0;
  237. if (display) {
  238. pthread_mutex_lock(&display->draw_info_mutex);
  239. *width = display->cx;
  240. *height = display->cy;
  241. pthread_mutex_unlock(&display->draw_info_mutex);
  242. }
  243. }