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