obs-display.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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. #if defined(_WIN32)
  18. #ifndef WIN32_LEAN_AND_MEAN
  19. #define WIN32_LEAN_AND_MEAN 1
  20. #endif
  21. #include <Windows.h>
  22. #endif
  23. bool obs_display_init(struct obs_display *display,
  24. const struct gs_init_data *graphics_data)
  25. {
  26. pthread_mutex_init_value(&display->draw_callbacks_mutex);
  27. pthread_mutex_init_value(&display->draw_info_mutex);
  28. #if defined(_WIN32)
  29. /* Conservative test for NVIDIA flickering on Intel display */
  30. SYSTEM_POWER_STATUS status;
  31. display->use_clear_workaround =
  32. !GetSystemPowerStatus(&status) ||
  33. (status.BatteryFlag != 128 && gs_get_adapter_count() != 1);
  34. #elif defined(__APPLE__)
  35. /* Apple Silicon GL driver doesn't seem to track SRGB clears correctly */
  36. display->use_clear_workaround = true;
  37. #else
  38. display->use_clear_workaround = false;
  39. #endif
  40. if (graphics_data) {
  41. display->swap = gs_swapchain_create(graphics_data);
  42. if (!display->swap) {
  43. blog(LOG_ERROR, "obs_display_init: Failed to "
  44. "create swap chain");
  45. return false;
  46. }
  47. const uint32_t cx = graphics_data->cx;
  48. const uint32_t cy = graphics_data->cy;
  49. display->cx = cx;
  50. display->cy = cy;
  51. display->next_cx = cx;
  52. display->next_cy = cy;
  53. }
  54. if (pthread_mutex_init(&display->draw_callbacks_mutex, NULL) != 0) {
  55. blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
  56. return false;
  57. }
  58. if (pthread_mutex_init(&display->draw_info_mutex, NULL) != 0) {
  59. blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
  60. return false;
  61. }
  62. display->enabled = true;
  63. return true;
  64. }
  65. obs_display_t *obs_display_create(const struct gs_init_data *graphics_data,
  66. uint32_t background_color)
  67. {
  68. struct obs_display *display = bzalloc(sizeof(struct obs_display));
  69. gs_enter_context(obs->video.graphics);
  70. display->background_color = background_color;
  71. if (!obs_display_init(display, graphics_data)) {
  72. obs_display_destroy(display);
  73. display = NULL;
  74. } else {
  75. pthread_mutex_lock(&obs->data.displays_mutex);
  76. display->prev_next = &obs->data.first_display;
  77. display->next = obs->data.first_display;
  78. obs->data.first_display = display;
  79. if (display->next)
  80. display->next->prev_next = &display->next;
  81. pthread_mutex_unlock(&obs->data.displays_mutex);
  82. }
  83. gs_leave_context();
  84. return display;
  85. }
  86. void obs_display_free(obs_display_t *display)
  87. {
  88. pthread_mutex_destroy(&display->draw_callbacks_mutex);
  89. pthread_mutex_destroy(&display->draw_info_mutex);
  90. da_free(display->draw_callbacks);
  91. if (display->swap) {
  92. gs_swapchain_destroy(display->swap);
  93. display->swap = NULL;
  94. }
  95. }
  96. void obs_display_destroy(obs_display_t *display)
  97. {
  98. if (display) {
  99. pthread_mutex_lock(&obs->data.displays_mutex);
  100. if (display->prev_next)
  101. *display->prev_next = display->next;
  102. if (display->next)
  103. display->next->prev_next = display->prev_next;
  104. pthread_mutex_unlock(&obs->data.displays_mutex);
  105. obs_enter_graphics();
  106. obs_display_free(display);
  107. obs_leave_graphics();
  108. bfree(display);
  109. }
  110. }
  111. void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy)
  112. {
  113. if (!display)
  114. return;
  115. pthread_mutex_lock(&display->draw_info_mutex);
  116. display->next_cx = cx;
  117. display->next_cy = cy;
  118. pthread_mutex_unlock(&display->draw_info_mutex);
  119. }
  120. void obs_display_update_color_space(obs_display_t *display)
  121. {
  122. if (!display)
  123. return;
  124. pthread_mutex_lock(&display->draw_info_mutex);
  125. display->update_color_space = true;
  126. pthread_mutex_unlock(&display->draw_info_mutex);
  127. }
  128. void obs_display_add_draw_callback(obs_display_t *display,
  129. void (*draw)(void *param, uint32_t cx,
  130. uint32_t cy),
  131. void *param)
  132. {
  133. if (!display)
  134. return;
  135. struct draw_callback data = {draw, param};
  136. pthread_mutex_lock(&display->draw_callbacks_mutex);
  137. da_push_back(display->draw_callbacks, &data);
  138. pthread_mutex_unlock(&display->draw_callbacks_mutex);
  139. }
  140. void obs_display_remove_draw_callback(obs_display_t *display,
  141. void (*draw)(void *param, uint32_t cx,
  142. uint32_t cy),
  143. void *param)
  144. {
  145. if (!display)
  146. return;
  147. struct draw_callback data = {draw, param};
  148. pthread_mutex_lock(&display->draw_callbacks_mutex);
  149. da_erase_item(display->draw_callbacks, &data);
  150. pthread_mutex_unlock(&display->draw_callbacks_mutex);
  151. }
  152. static inline bool render_display_begin(struct obs_display *display,
  153. uint32_t cx, uint32_t cy,
  154. bool update_color_space)
  155. {
  156. struct vec4 clear_color;
  157. gs_load_swapchain(display->swap);
  158. if ((display->cx != cx) || (display->cy != cy)) {
  159. gs_resize(cx, cy);
  160. display->cx = cx;
  161. display->cy = cy;
  162. } else if (update_color_space) {
  163. gs_update_color_space();
  164. }
  165. const bool success = gs_is_present_ready();
  166. if (success) {
  167. gs_begin_scene();
  168. if (gs_get_color_space() == GS_CS_SRGB)
  169. vec4_from_rgba(&clear_color, display->background_color);
  170. else
  171. vec4_from_rgba_srgb(&clear_color,
  172. display->background_color);
  173. clear_color.w = 1.0f;
  174. const bool use_clear_workaround = display->use_clear_workaround;
  175. uint32_t clear_flags = GS_CLEAR_DEPTH | GS_CLEAR_STENCIL;
  176. if (!use_clear_workaround)
  177. clear_flags |= GS_CLEAR_COLOR;
  178. gs_clear(clear_flags, &clear_color, 1.0f, 0);
  179. gs_enable_depth_test(false);
  180. /* gs_enable_blending(false); */
  181. gs_set_cull_mode(GS_NEITHER);
  182. gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
  183. gs_set_viewport(0, 0, cx, cy);
  184. if (use_clear_workaround) {
  185. gs_effect_t *const solid_effect =
  186. obs->video.solid_effect;
  187. gs_effect_set_vec4(gs_effect_get_param_by_name(
  188. solid_effect, "color"),
  189. &clear_color);
  190. while (gs_effect_loop(solid_effect, "Solid"))
  191. gs_draw_sprite(NULL, 0, cx, cy);
  192. }
  193. }
  194. return success;
  195. }
  196. static inline void render_display_end()
  197. {
  198. gs_end_scene();
  199. }
  200. void render_display(struct obs_display *display)
  201. {
  202. uint32_t cx, cy;
  203. bool update_color_space;
  204. if (!display || !display->enabled)
  205. return;
  206. /* -------------------------------------------- */
  207. pthread_mutex_lock(&display->draw_info_mutex);
  208. cx = display->next_cx;
  209. cy = display->next_cy;
  210. update_color_space = display->update_color_space;
  211. display->update_color_space = false;
  212. pthread_mutex_unlock(&display->draw_info_mutex);
  213. /* -------------------------------------------- */
  214. if (render_display_begin(display, cx, cy, update_color_space)) {
  215. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DISPLAY, "obs_display");
  216. pthread_mutex_lock(&display->draw_callbacks_mutex);
  217. for (size_t i = 0; i < display->draw_callbacks.num; i++) {
  218. struct draw_callback *callback;
  219. callback = display->draw_callbacks.array + i;
  220. callback->draw(callback->param, cx, cy);
  221. }
  222. pthread_mutex_unlock(&display->draw_callbacks_mutex);
  223. render_display_end();
  224. GS_DEBUG_MARKER_END();
  225. gs_present();
  226. }
  227. }
  228. void obs_display_set_enabled(obs_display_t *display, bool enable)
  229. {
  230. if (display)
  231. display->enabled = enable;
  232. }
  233. bool obs_display_enabled(obs_display_t *display)
  234. {
  235. return display ? display->enabled : false;
  236. }
  237. void obs_display_set_background_color(obs_display_t *display, uint32_t color)
  238. {
  239. if (display)
  240. display->background_color = color;
  241. }
  242. void obs_display_size(obs_display_t *display, uint32_t *width, uint32_t *height)
  243. {
  244. *width = 0;
  245. *height = 0;
  246. if (display) {
  247. pthread_mutex_lock(&display->draw_info_mutex);
  248. *width = display->cx;
  249. *height = display->cy;
  250. pthread_mutex_unlock(&display->draw_info_mutex);
  251. }
  252. }