Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo 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 Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "common.h"
19 : : #include "formats.h"
20 : : #include "gpu.h"
21 : : #include "swapchain.h"
22 : : #include "utils.h"
23 : : #include "pl_thread.h"
24 : :
25 : : struct priv {
26 : : struct pl_sw_fns impl;
27 : :
28 : : struct pl_opengl_swapchain_params params;
29 : : pl_opengl gl;
30 : : pl_mutex lock;
31 : : bool has_sync;
32 : :
33 : : // current parameters
34 : : pl_tex fb;
35 : : bool frame_started;
36 : :
37 : : // vsync fences
38 : : int swapchain_depth;
39 : : PL_ARRAY(GLsync) vsync_fences;
40 : : };
41 : :
42 : : static const struct pl_sw_fns opengl_swapchain;
43 : :
44 : 4 : pl_swapchain pl_opengl_create_swapchain(pl_opengl pl_gl,
45 : : const struct pl_opengl_swapchain_params *params)
46 : : {
47 : 4 : pl_gpu gpu = pl_gl->gpu;
48 : :
49 [ - + ]: 4 : if (params->max_swapchain_depth < 0) {
50 : 0 : PL_ERR(gpu, "Tried specifying negative swapchain depth?");
51 : 0 : return NULL;
52 : : }
53 : :
54 [ - + ]: 4 : if (!gl_make_current(pl_gl))
55 : : return NULL;
56 : :
57 : 4 : struct pl_swapchain_t *sw = pl_zalloc_obj(NULL, sw, struct priv);
58 : 4 : sw->log = gpu->log;
59 : 4 : sw->gpu = gpu;
60 : :
61 : 4 : struct priv *p = PL_PRIV(sw);
62 [ - + ]: 4 : pl_mutex_init(&p->lock);
63 : 4 : p->impl = opengl_swapchain;
64 : 4 : p->params = *params;
65 : 4 : p->has_sync = pl_opengl_has_ext(pl_gl, "GL_ARB_sync");
66 : 4 : p->gl = pl_gl;
67 : :
68 : 4 : gl_release_current(pl_gl);
69 : 4 : return sw;
70 : : }
71 : :
72 : 4 : static void gl_sw_destroy(pl_swapchain sw)
73 : : {
74 : 4 : pl_gpu gpu = sw->gpu;
75 : 4 : struct priv *p = PL_PRIV(sw);
76 : :
77 : 4 : pl_gpu_flush(gpu);
78 : 4 : pl_tex_destroy(gpu, &p->fb);
79 : 4 : pl_mutex_destroy(&p->lock);
80 : 4 : pl_free((void *) sw);
81 : 4 : }
82 : :
83 : 0 : static int gl_sw_latency(pl_swapchain sw)
84 : : {
85 : 0 : struct priv *p = PL_PRIV(sw);
86 : 0 : return p->params.max_swapchain_depth;
87 : : }
88 : :
89 : 4 : static bool gl_sw_resize(pl_swapchain sw, int *width, int *height)
90 : : {
91 : 4 : struct priv *p = PL_PRIV(sw);
92 : 4 : const int w = *width, h = *height;
93 : :
94 : 4 : pl_mutex_lock(&p->lock);
95 [ - + - - : 4 : if (p->fb && w == p->fb->params.w && h == p->fb->params.h) {
- - ]
96 : 0 : pl_mutex_unlock(&p->lock);
97 : 0 : return true;
98 : : }
99 : :
100 [ - + - - ]: 4 : if (p->frame_started && (w || h)) {
101 : 0 : PL_ERR(sw, "Tried resizing the swapchain while a frame was in progress! "
102 : : "Please submit the current frame first.");
103 : 0 : pl_mutex_unlock(&p->lock);
104 : 0 : return false;
105 : : }
106 : :
107 [ + - ]: 4 : if (w && h) {
108 : 4 : pl_tex_destroy(sw->gpu, &p->fb);
109 : 4 : p->fb = pl_opengl_wrap(sw->gpu, pl_opengl_wrap_params(
110 : : .framebuffer = p->params.framebuffer.id,
111 : : .width = w,
112 : : .height = h,
113 : : ));
114 [ - + ]: 4 : if (!p->fb) {
115 : 0 : PL_ERR(sw, "Failed wrapping OpenGL framebuffer!");
116 : 0 : pl_mutex_unlock(&p->lock);
117 : 0 : return false;
118 : : }
119 : : }
120 : :
121 [ - + ]: 4 : if (!p->fb) {
122 : 0 : PL_ERR(sw, "Tried calling `pl_swapchain_resize` with unknown size! "
123 : : "This is forbidden for OpenGL. The first call to "
124 : : "`pl_swapchain_resize` must include the width and height of the "
125 : : "swapchain, because there's no way to figure this out from "
126 : : "within the API.");
127 : 0 : pl_mutex_unlock(&p->lock);
128 : 0 : return false;
129 : : }
130 : :
131 : 4 : *width = p->fb->params.w;
132 : 4 : *height = p->fb->params.h;
133 : 4 : pl_mutex_unlock(&p->lock);
134 : 4 : return true;
135 : : }
136 : :
137 : 0 : void pl_opengl_swapchain_update_fb(pl_swapchain sw,
138 : : const struct pl_opengl_framebuffer *fb)
139 : : {
140 : 0 : struct priv *p = PL_PRIV(sw);
141 : 0 : pl_mutex_lock(&p->lock);
142 [ # # ]: 0 : if (p->frame_started) {
143 : 0 : PL_ERR(sw,"Tried calling `pl_opengl_swapchain_update_fb` while a frame "
144 : : "was in progress! Please submit the current frame first.");
145 : 0 : pl_mutex_unlock(&p->lock);
146 : 0 : return;
147 : : }
148 : :
149 [ # # ]: 0 : if (p->params.framebuffer.id != fb->id)
150 : 0 : pl_tex_destroy(sw->gpu, &p->fb);
151 : :
152 : 0 : p->params.framebuffer = *fb;
153 : 0 : pl_mutex_unlock(&p->lock);
154 : : }
155 : :
156 : 40 : static bool gl_sw_start_frame(pl_swapchain sw,
157 : : struct pl_swapchain_frame *out_frame)
158 : : {
159 : 40 : struct priv *p = PL_PRIV(sw);
160 : 40 : pl_mutex_lock(&p->lock);
161 : : bool ok = false;
162 : :
163 [ - + ]: 40 : if (!p->fb) {
164 : 0 : PL_ERR(sw, "Unknown framebuffer size. Please call `pl_swapchain_resize` "
165 : : "before `pl_swapchain_start_frame` for OpenGL swapchains!");
166 : 0 : goto error;
167 : : }
168 : :
169 [ - + ]: 40 : if (p->frame_started) {
170 : 0 : PL_ERR(sw, "Attempted calling `pl_swapchain_start` while a frame was "
171 : : "already in progress! Call `pl_swapchain_submit_frame` first.");
172 : 0 : goto error;
173 : : }
174 : :
175 [ - + ]: 40 : if (!gl_make_current(p->gl))
176 : 0 : goto error;
177 : :
178 : 40 : *out_frame = (struct pl_swapchain_frame) {
179 : 40 : .fbo = p->fb,
180 : 40 : .flipped = !p->params.framebuffer.flipped,
181 : : .color_repr = {
182 : : .sys = PL_COLOR_SYSTEM_RGB,
183 : : .levels = PL_COLOR_LEVELS_FULL,
184 : 40 : .alpha = p->fb->params.format->num_components == 4
185 : : ? PL_ALPHA_PREMULTIPLIED
186 [ + - ]: 40 : : PL_ALPHA_NONE,
187 : : .bits = {
188 : : // Just use the red channel in the absence of anything more
189 : : // sane to do, because the red channel is both guaranteed to
190 : : // exist and also typically has the minimum number of bits
191 : : // (which is arguably what matters for dithering)
192 : 40 : .sample_depth = p->fb->params.format->component_depth[0],
193 : : .color_depth = p->fb->params.format->component_depth[0],
194 : : },
195 : : },
196 : : .color_space = pl_color_space_monitor,
197 : : };
198 : :
199 : 40 : p->frame_started = gl_check_err(sw->gpu, "gl_sw_start_frame");
200 [ - + ]: 40 : if (!p->frame_started)
201 : 0 : goto error;
202 : :
203 : : // keep p->lock held
204 : 40 : gl_release_current(p->gl);
205 : 40 : return true;
206 : :
207 : 0 : error:
208 : 0 : gl_release_current(p->gl);
209 : 0 : pl_mutex_unlock(&p->lock);
210 : 0 : return ok;
211 : : }
212 : :
213 : 40 : static bool gl_sw_submit_frame(pl_swapchain sw)
214 : : {
215 : 40 : struct priv *p = PL_PRIV(sw);
216 : 40 : struct gl_ctx *glctx = PL_PRIV(p->gl);
217 : : const gl_funcs *gl = &glctx->func;
218 [ - + ]: 40 : if (!gl_make_current(p->gl)) {
219 : 0 : p->frame_started = false;
220 : 0 : pl_mutex_unlock(&p->lock);
221 : 0 : return false;
222 : : }
223 : :
224 [ - + ]: 40 : pl_assert(p->frame_started);
225 [ + + - + ]: 40 : if (p->has_sync && p->params.max_swapchain_depth) {
226 : 0 : GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
227 [ # # ]: 0 : if (fence)
228 [ # # # # : 0 : PL_ARRAY_APPEND(sw, p->vsync_fences, fence);
# # ]
229 : : }
230 : :
231 : 40 : gl->Flush();
232 : 40 : p->frame_started = false;
233 : 40 : bool ok = gl_check_err(sw->gpu, "gl_sw_submit_frame");
234 : 40 : gl_release_current(p->gl);
235 : 40 : pl_mutex_unlock(&p->lock);
236 : :
237 : 40 : return ok;
238 : : }
239 : :
240 : 40 : static void gl_sw_swap_buffers(pl_swapchain sw)
241 : : {
242 : 40 : struct priv *p = PL_PRIV(sw);
243 : 40 : struct gl_ctx *glctx = PL_PRIV(p->gl);
244 : : const gl_funcs *gl = &glctx->func;
245 [ - + ]: 40 : if (!p->params.swap_buffers) {
246 : 0 : PL_ERR(sw, "`pl_swapchain_swap_buffers` called but no "
247 : : "`params.swap_buffers` callback set!");
248 : 0 : return;
249 : : }
250 : :
251 : 40 : pl_mutex_lock(&p->lock);
252 [ - + ]: 40 : if (!gl_make_current(p->gl)) {
253 : 0 : pl_mutex_unlock(&p->lock);
254 : 0 : return;
255 : : }
256 : :
257 : 40 : p->params.swap_buffers(p->params.priv);
258 : :
259 : 40 : const int max_depth = p->params.max_swapchain_depth;
260 [ - + - - ]: 40 : while (max_depth && p->vsync_fences.num >= max_depth) {
261 : 0 : gl->ClientWaitSync(p->vsync_fences.elem[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
262 : 0 : gl->DeleteSync(p->vsync_fences.elem[0]);
263 [ # # ]: 0 : PL_ARRAY_REMOVE_AT(p->vsync_fences, 0);
264 : : }
265 : :
266 : 40 : gl_check_err(sw->gpu, "gl_sw_swap_buffers");
267 : 40 : gl_release_current(p->gl);
268 : 40 : pl_mutex_unlock(&p->lock);
269 : : }
270 : :
271 : : static const struct pl_sw_fns opengl_swapchain = {
272 : : .destroy = gl_sw_destroy,
273 : : .latency = gl_sw_latency,
274 : : .resize = gl_sw_resize,
275 : : .start_frame = gl_sw_start_frame,
276 : : .submit_frame = gl_sw_submit_frame,
277 : : .swap_buffers = gl_sw_swap_buffers,
278 : : };
|