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 "gpu.h"
19 : : #include "common.h"
20 : : #include "formats.h"
21 : : #include "utils.h"
22 : :
23 : : #ifdef PL_HAVE_UNIX
24 : : #include <unistd.h>
25 : : #endif
26 : :
27 : : #ifdef PL_HAVE_WIN32
28 : : #include <windows.h>
29 : : #include <sysinfoapi.h>
30 : : #endif
31 : :
32 : : static const struct pl_gpu_fns pl_fns_gl;
33 : :
34 : 4 : static void gl_gpu_destroy(pl_gpu gpu)
35 : : {
36 : 4 : struct pl_gl *p = PL_PRIV(gpu);
37 : :
38 : 4 : pl_gpu_finish(gpu);
39 [ - + ]: 4 : while (p->callbacks.num > 0)
40 : 0 : gl_poll_callbacks(gpu);
41 : :
42 : 4 : pl_free((void *) gpu);
43 : 4 : }
44 : :
45 : 0 : pl_opengl pl_opengl_get(pl_gpu gpu)
46 : : {
47 : 0 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
48 [ # # ]: 0 : if (impl->destroy == gl_gpu_destroy) {
49 : : struct pl_gl *p = (struct pl_gl *) impl;
50 : 0 : return p->gl;
51 : : }
52 : :
53 : : return NULL;
54 : : }
55 : :
56 : 8 : static pl_handle_caps tex_handle_caps(pl_gpu gpu, bool import)
57 : : {
58 : : pl_handle_caps caps = 0;
59 : 8 : struct pl_gl *p = PL_PRIV(gpu);
60 : :
61 [ + - - + : 8 : if (!p->egl_dpy || (!p->has_egl_storage && !p->has_egl_import))
- - ]
62 : : return 0;
63 : :
64 [ + + ]: 8 : if (import) {
65 [ + - ]: 4 : if (pl_opengl_has_ext(p->gl, "EGL_EXT_image_dma_buf_import"))
66 : : caps |= PL_HANDLE_DMA_BUF;
67 [ + - ]: 4 : } else if (!import && p->egl_ctx) {
68 [ + - ]: 4 : if (pl_opengl_has_ext(p->gl, "EGL_MESA_image_dma_buf_export"))
69 : : caps |= PL_HANDLE_DMA_BUF;
70 : : }
71 : :
72 : : return caps;
73 : : }
74 : :
75 : : static inline size_t get_page_size(void)
76 : : {
77 : :
78 : : #ifdef PL_HAVE_UNIX
79 : 0 : return sysconf(_SC_PAGESIZE);
80 : : #endif
81 : :
82 : : #ifdef PL_HAVE_WIN32
83 : : SYSTEM_INFO sysInfo;
84 : : GetSystemInfo(&sysInfo);
85 : : return sysInfo.dwAllocationGranularity;
86 : : #endif
87 : :
88 : : pl_assert(!"Unsupported platform!");
89 : : }
90 : :
91 : : #define get(pname, field) \
92 : : do { \
93 : : GLint tmp = 0; \
94 : : gl->GetIntegerv((pname), &tmp); \
95 : : *(field) = tmp; \
96 : : } while (0)
97 : :
98 : : #define geti(pname, i, field) \
99 : : do { \
100 : : GLint tmp = 0; \
101 : : gl->GetIntegeri_v((pname), i, &tmp);\
102 : : *(field) = tmp; \
103 : : } while (0)
104 : :
105 : 4 : pl_gpu pl_gpu_create_gl(pl_log log, pl_opengl pl_gl, const struct pl_opengl_params *params)
106 : : {
107 : 4 : struct pl_gpu_t *gpu = pl_zalloc_obj(NULL, gpu, struct pl_gl);
108 : 4 : gpu->log = log;
109 : :
110 : 4 : struct pl_gl *p = PL_PRIV(gpu);
111 : 4 : p->impl = pl_fns_gl;
112 : 4 : p->gl = pl_gl;
113 : :
114 : : const gl_funcs *gl = gl_funcs_get(gpu);
115 : : struct pl_glsl_version *glsl = &gpu->glsl;
116 : 4 : glsl->gles = gl_is_gles(pl_gl);
117 : 4 : int ver = pl_gl->major * 10 + pl_gl->minor;
118 [ + + ]: 4 : p->gl_ver = glsl->gles ? 0 : ver;
119 [ + + ]: 4 : p->gles_ver = glsl->gles ? ver : 0;
120 : :
121 : : // If possible, query the GLSL version from the implementation
122 : 4 : const char *glslver_p = (char *) gl->GetString(GL_SHADING_LANGUAGE_VERSION);
123 : 4 : pl_str glslver = pl_str0(glslver_p);
124 [ + - ]: 4 : if (glslver.len) {
125 [ + - ]: 8 : PL_INFO(gpu, " GL_SHADING_LANGUAGE_VERSION: %.*s", PL_STR_FMT(glslver));
126 : 4 : pl_str_eatstart0(&glslver, "OpenGL ES GLSL ES ");
127 : 4 : int major = 0, minor = 0;
128 [ + - ]: 4 : if (pl_str_sscanf(glslver, "%d.%d", &major, &minor) == 2)
129 : 4 : glsl->version = major * 100 + minor;
130 : : }
131 : :
132 [ - + ]: 4 : if (!glsl->version) {
133 : : // Otherwise, use the fixed magic versions 100 and 300 for GLES.
134 [ # # ]: 0 : if (p->gles_ver >= 30) {
135 : 0 : glsl->version = 300;
136 [ # # ]: 0 : } else if (p->gles_ver >= 20) {
137 : 0 : glsl->version = 100;
138 : : } else {
139 : 0 : goto error;
140 : : }
141 : : }
142 : :
143 : : static const int glsl_ver_req = 130;
144 [ - + ]: 4 : if (glsl->version < glsl_ver_req) {
145 : 0 : PL_FATAL(gpu, "GLSL version too old (%d < %d), please use a newer "
146 : : "OpenGL implementation or downgrade libplacebo!",
147 : : glsl->version, glsl_ver_req);
148 : 0 : goto error;
149 : : }
150 : :
151 [ + - ]: 4 : if (params->max_glsl_version && params->max_glsl_version >= glsl_ver_req) {
152 : 4 : glsl->version = PL_MIN(glsl->version, params->max_glsl_version);
153 : 4 : PL_INFO(gpu, "Restricting GLSL version to %d... new version is %d",
154 : : params->max_glsl_version, glsl->version);
155 : : }
156 : :
157 [ + + + + ]: 4 : if (gl_test_ext(gpu, "GL_ARB_compute_shader", 43, 0) && glsl->version >= 420) {
158 : 1 : glsl->compute = !params->no_compute;
159 : 1 : get(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, &glsl->max_shmem_size);
160 : 1 : get(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &glsl->max_group_threads);
161 [ + + ]: 4 : for (int i = 0; i < 3; i++)
162 : 3 : geti(GL_MAX_COMPUTE_WORK_GROUP_SIZE, i, &glsl->max_group_size[i]);
163 : : }
164 : :
165 [ + - + + ]: 8 : if (gl_test_ext(gpu, "GL_ARB_texture_gather", 40, 31) &&
166 [ + + ]: 4 : glsl->version >= (p->gles_ver ? 310 : 400)) {
167 [ - + ]: 1 : if (p->gles_ver)
168 : 0 : p->gather_comps = 4;
169 : : else
170 : 1 : get(GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB, &p->gather_comps);
171 : 1 : get(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB, &glsl->min_gather_offset);
172 : 1 : get(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB, &glsl->max_gather_offset);
173 : : }
174 : :
175 : : // Query all device limits
176 : : struct pl_gpu_limits *limits = &gpu->limits;
177 : 4 : limits->thread_safe = params->make_current;
178 : 4 : limits->callbacks = gl_test_ext(gpu, "GL_ARB_sync", 32, 30);
179 : 4 : limits->align_vertex_stride = 1;
180 [ + + ]: 4 : if (gl_test_ext(gpu, "GL_ARB_pixel_buffer_object", 31, 0)) {
181 : 3 : limits->max_buf_size = SIZE_MAX; // no restriction imposed by GL
182 [ + - ]: 3 : if (gl_test_ext(gpu, "GL_ARB_uniform_buffer_object", 31, 0))
183 : 3 : get(GL_MAX_UNIFORM_BLOCK_SIZE, &limits->max_ubo_size);
184 [ + - ]: 3 : if (gl_test_ext(gpu, "GL_ARB_shader_storage_buffer_object", 43, 0) &&
185 [ + + ]: 3 : gpu->glsl.version >= 140)
186 : : {
187 : 2 : get(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &limits->max_ssbo_size);
188 : : }
189 : 3 : limits->max_vbo_size = limits->max_buf_size; // No additional restrictions
190 [ + - ]: 3 : if (gl_test_ext(gpu, "GL_ARB_buffer_storage", 44, 0)) {
191 : 3 : const char *vendor = (char *) gl->GetString(GL_VENDOR);
192 : 3 : limits->max_mapped_size = limits->max_buf_size;
193 : 3 : limits->max_mapped_vram = limits->max_buf_size;
194 [ + - ]: 6 : limits->host_cached = strcmp(vendor, "AMD") == 0 ||
195 [ - + ]: 3 : strcmp(vendor, "NVIDIA Corporation") == 0;
196 : : }
197 : : }
198 : :
199 : 4 : get(GL_MAX_TEXTURE_SIZE, &limits->max_tex_2d_dim);
200 [ + - ]: 4 : if (gl_test_ext(gpu, "GL_EXT_texture3D", 21, 30))
201 : 4 : get(GL_MAX_3D_TEXTURE_SIZE, &limits->max_tex_3d_dim);
202 : : // There's no equivalent limit for 1D textures for whatever reason, so
203 : : // just set it to the same as the 2D limit
204 [ + + ]: 4 : if (p->gl_ver >= 21)
205 : 3 : limits->max_tex_1d_dim = limits->max_tex_2d_dim;
206 : 4 : limits->buf_transfer = true;
207 : :
208 [ + + + - ]: 4 : if (p->gl_ver || p->gles_ver >= 30) {
209 : 4 : get(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &limits->max_variable_comps);
210 : : } else {
211 : : // fallback for GLES 2.0, which doesn't have max_comps
212 : 0 : get(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &limits->max_variable_comps);
213 : 0 : limits->max_variable_comps *= 4;
214 : : }
215 : :
216 [ + + ]: 4 : if (glsl->compute) {
217 [ + + ]: 4 : for (int i = 0; i < 3; i++)
218 : 3 : geti(GL_MAX_COMPUTE_WORK_GROUP_COUNT, i, &limits->max_dispatch[i]);
219 : : }
220 : :
221 : : // Query import/export support
222 : 4 : p->egl_dpy = params->egl_display;
223 : 4 : p->egl_ctx = params->egl_context;
224 : 4 : p->has_egl_storage = pl_opengl_has_ext(p->gl, "GL_EXT_EGL_image_storage");
225 : 4 : p->has_egl_import = pl_opengl_has_ext(p->gl, "GL_OES_EGL_image_external");
226 : 4 : gpu->export_caps.tex = tex_handle_caps(gpu, false);
227 : 4 : gpu->import_caps.tex = tex_handle_caps(gpu, true);
228 : :
229 [ + - ]: 4 : if (p->egl_dpy) {
230 : 4 : p->has_modifiers = pl_opengl_has_ext(p->gl,
231 : : "EGL_EXT_image_dma_buf_import_modifiers");
232 : : }
233 : :
234 [ - + ]: 4 : if (pl_opengl_has_ext(pl_gl, "GL_AMD_pinned_memory")) {
235 : 0 : gpu->import_caps.buf |= PL_HANDLE_HOST_PTR;
236 : 0 : gpu->limits.align_host_ptr = get_page_size();
237 : : }
238 : :
239 : : // Cache some internal capability checks
240 : 4 : p->has_vao = gl_test_ext(gpu, "GL_ARB_vertex_array_object", 30, 30);
241 : 4 : p->has_invalidate_fb = gl_test_ext(gpu, "GL_ARB_invalidate_subdata", 43, 30);
242 : 4 : p->has_invalidate_tex = gl_test_ext(gpu, "GL_ARB_invalidate_subdata", 43, 0);
243 : 4 : p->has_queries = gl_test_ext(gpu, "GL_ARB_timer_query", 30, 0);
244 : 4 : p->has_storage = gl_test_ext(gpu, "GL_ARB_shader_image_load_store", 42, 31);
245 : 4 : p->has_readback = true;
246 : :
247 [ + + ]: 4 : if (p->has_readback && p->gles_ver) {
248 : 1 : GLuint fbo = 0, tex = 0;
249 : 1 : GLint read_type = 0, read_fmt = 0;
250 [ - + ]: 1 : const GLenum target = p->gles_ver >= 30 ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER;
251 : 1 : gl->GenTextures(1, &tex);
252 : 1 : gl->BindTexture(GL_TEXTURE_2D, tex);
253 : 1 : gl->GenFramebuffers(1, &fbo);
254 : 1 : gl->TexImage2D(GL_TEXTURE_2D, 0, GL_R8, 64, 64, 0, GL_RED,
255 : : GL_UNSIGNED_BYTE, NULL);
256 : 1 : gl->BindFramebuffer(target, fbo);
257 : 1 : gl->FramebufferTexture2D(target, GL_COLOR_ATTACHMENT0,
258 : : GL_TEXTURE_2D, tex, 0);
259 : 1 : gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &read_type);
260 : 1 : gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &read_fmt);
261 [ + - - + ]: 1 : if (read_type != GL_UNSIGNED_BYTE || read_fmt != GL_RED) {
262 : 0 : PL_INFO(gpu, "GPU does not seem to support lossless texture "
263 : : "readback, restricting readback capabilities! This is a "
264 : : "GLES/driver limitation, there is little we can do to "
265 : : "work around it.");
266 : 0 : p->has_readback = false;
267 : : }
268 : 1 : gl->BindFramebuffer(target, 0);
269 : 1 : gl->BindTexture(GL_TEXTURE_2D, 0);
270 : 1 : gl->DeleteFramebuffers(1, &fbo);
271 : 1 : gl->DeleteTextures(1, &tex);
272 : : }
273 : :
274 : : // We simply don't know, so make up some values
275 : 4 : limits->align_tex_xfer_offset = 32;
276 : 4 : limits->align_tex_xfer_pitch = 4;
277 : 4 : limits->fragment_queues = 1;
278 : 4 : limits->compute_queues = glsl->compute ? 1 : 0;
279 : :
280 [ - + ]: 4 : if (!gl_check_err(gpu, "pl_gpu_create_gl")) {
281 : 0 : PL_WARN(gpu, "Encountered errors while detecting GPU capabilities... "
282 : : "ignoring, but expect limitations/issues");
283 : 0 : p->failed = false;
284 : : }
285 : :
286 : : // Filter out error messages during format probing
287 : 4 : pl_log_level_cap(gpu->log, PL_LOG_INFO);
288 : 4 : bool formats_ok = gl_setup_formats(gpu);
289 : 4 : pl_log_level_cap(gpu->log, PL_LOG_NONE);
290 [ - + ]: 4 : if (!formats_ok)
291 : 0 : goto error;
292 : :
293 : 4 : return pl_gpu_finalize(gpu);
294 : :
295 : 0 : error:
296 : 0 : gl_gpu_destroy(gpu);
297 : 0 : return NULL;
298 : : }
299 : :
300 : 181 : void gl_buf_destroy(pl_gpu gpu, pl_buf buf)
301 : : {
302 : : const gl_funcs *gl = gl_funcs_get(gpu);
303 : : if (!MAKE_CURRENT()) {
304 : 0 : PL_ERR(gpu, "Failed uninitializing buffer, leaking resources!");
305 : 0 : return;
306 : : }
307 : :
308 : 181 : struct pl_buf_gl *buf_gl = PL_PRIV(buf);
309 [ - + ]: 181 : if (buf_gl->fence)
310 : 0 : gl->DeleteSync(buf_gl->fence);
311 : :
312 [ + + ]: 181 : if (buf_gl->mapped) {
313 : 3 : gl->BindBuffer(GL_COPY_WRITE_BUFFER, buf_gl->buffer);
314 : 3 : gl->UnmapBuffer(GL_COPY_WRITE_BUFFER);
315 : 3 : gl->BindBuffer(GL_COPY_WRITE_BUFFER, 0);
316 : : }
317 : :
318 : 181 : gl->DeleteBuffers(1, &buf_gl->buffer);
319 : 181 : gl_check_err(gpu, "gl_buf_destroy");
320 : : RELEASE_CURRENT();
321 : 181 : pl_free((void *) buf);
322 : : }
323 : :
324 : 181 : pl_buf gl_buf_create(pl_gpu gpu, const struct pl_buf_params *params)
325 : : {
326 : : const gl_funcs *gl = gl_funcs_get(gpu);
327 : : if (!MAKE_CURRENT())
328 : 0 : return NULL;
329 : :
330 : 181 : struct pl_buf_t *buf = pl_zalloc_obj(NULL, buf, struct pl_buf_gl);
331 : 181 : buf->params = *params;
332 : 181 : buf->params.initial_data = NULL;
333 : :
334 : : struct pl_gl *p = PL_PRIV(gpu);
335 : 181 : struct pl_buf_gl *buf_gl = PL_PRIV(buf);
336 : 181 : buf_gl->id = ++p->buf_id;
337 : :
338 : : // Just use this since the generic GL_BUFFER doesn't work
339 : : GLenum target = GL_ARRAY_BUFFER;
340 : 181 : const void *data = params->initial_data;
341 : 181 : size_t total_size = params->size;
342 : : bool import = false;
343 : :
344 [ - + ]: 181 : if (params->import_handle == PL_HANDLE_HOST_PTR) {
345 : : const struct pl_shared_mem *shmem = ¶ms->shared_mem;
346 : : target = GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD;
347 : :
348 : 0 : data = shmem->handle.ptr;
349 : 0 : buf_gl->offset = shmem->offset;
350 : 0 : total_size = shmem->size;
351 : : import = true;
352 : :
353 [ # # ]: 0 : if (params->host_mapped)
354 : 0 : buf->data = (uint8_t *) data + buf_gl->offset;
355 : :
356 [ # # # # ]: 0 : if (buf_gl->offset > 0 && params->drawable) {
357 : 0 : PL_ERR(gpu, "Cannot combine non-aligned host pointer imports with "
358 : : "drawable (vertex) buffers! This is a design limitation, "
359 : : "open an issue if you absolutely need this.");
360 : 0 : goto error;
361 : : }
362 : : }
363 : :
364 : 181 : gl->GenBuffers(1, &buf_gl->buffer);
365 : 181 : gl->BindBuffer(target, buf_gl->buffer);
366 : :
367 [ + - + - ]: 181 : if (gl_test_ext(gpu, "GL_ARB_buffer_storage", 44, 0) && !import) {
368 : :
369 : : GLbitfield mapflags = 0, storflags = 0;
370 [ + + ]: 181 : if (params->host_writable)
371 : : storflags |= GL_DYNAMIC_STORAGE_BIT;
372 [ + + ]: 181 : if (params->host_mapped) {
373 : : mapflags |= GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
374 : : GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
375 : : }
376 [ - + ]: 181 : if (params->memory_type == PL_BUF_MEM_HOST)
377 : 0 : storflags |= GL_CLIENT_STORAGE_BIT; // hopefully this works
378 : :
379 : 181 : gl->BufferStorage(target, total_size, data, storflags | mapflags);
380 : :
381 [ + + ]: 181 : if (params->host_mapped) {
382 : 3 : buf_gl->mapped = true;
383 : 3 : buf->data = gl->MapBufferRange(target, buf_gl->offset, params->size,
384 : : mapflags);
385 [ - + ]: 3 : if (!buf->data) {
386 : 0 : gl->BindBuffer(target, 0);
387 [ # # ]: 0 : if (!gl_check_err(gpu, "gl_buf_create: map"))
388 : 0 : PL_ERR(gpu, "Failed mapping buffer: unknown reason");
389 : 0 : goto error;
390 : : }
391 : : }
392 : :
393 : : } else {
394 : :
395 : : // Make a random guess based on arbitrary criteria we can't know
396 : : GLenum hint = GL_STREAM_DRAW;
397 [ # # # # : 0 : if (params->initial_data && !params->host_writable && !params->host_mapped)
# # ]
398 : : hint = GL_STATIC_DRAW;
399 [ # # # # : 0 : if (params->host_readable && !params->host_writable && !params->host_mapped)
# # ]
400 : : hint = GL_STREAM_READ;
401 [ # # ]: 0 : if (params->storable)
402 : : hint = GL_DYNAMIC_COPY;
403 : :
404 : 0 : gl->BufferData(target, total_size, data, hint);
405 : :
406 [ # # # # ]: 0 : if (import && gl->GetError() == GL_INVALID_OPERATION) {
407 : 0 : PL_ERR(gpu, "Failed importing host pointer!");
408 : 0 : goto error;
409 : : }
410 : :
411 : : }
412 : :
413 : 181 : gl->BindBuffer(target, 0);
414 [ - + ]: 181 : if (!gl_check_err(gpu, "gl_buf_create"))
415 : 0 : goto error;
416 : :
417 [ + + ]: 181 : if (params->storable) {
418 : 10 : buf_gl->barrier = GL_BUFFER_UPDATE_BARRIER_BIT | // for buf_copy etc.
419 : : GL_PIXEL_BUFFER_BARRIER_BIT | // for tex_upload
420 : : GL_SHADER_STORAGE_BARRIER_BIT;
421 : :
422 [ - + ]: 10 : if (params->host_mapped)
423 : 0 : buf_gl->barrier |= GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
424 [ - + ]: 10 : if (params->uniform)
425 : 0 : buf_gl->barrier |= GL_UNIFORM_BARRIER_BIT;
426 [ - + ]: 10 : if (params->drawable)
427 : 0 : buf_gl->barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
428 : : }
429 : :
430 : : RELEASE_CURRENT();
431 : 181 : return buf;
432 : :
433 : 0 : error:
434 : 0 : gl_buf_destroy(gpu, buf);
435 : : RELEASE_CURRENT();
436 : 0 : return NULL;
437 : : }
438 : :
439 [ + + ]: 4 : bool gl_buf_poll(pl_gpu gpu, pl_buf buf, uint64_t timeout)
440 : : {
441 : : const gl_funcs *gl = gl_funcs_get(gpu);
442 : :
443 : : // Non-persistently mapped buffers are always implicitly reusable in OpenGL,
444 : : // the implementation will create more buffers under the hood if needed.
445 [ + + ]: 4 : if (!buf->data)
446 : : return false;
447 : :
448 : : if (!MAKE_CURRENT())
449 : 0 : return true; // conservative guess
450 : :
451 : 3 : struct pl_buf_gl *buf_gl = PL_PRIV(buf);
452 [ - + ]: 3 : if (buf_gl->fence) {
453 : 0 : GLenum res = gl->ClientWaitSync(buf_gl->fence,
454 : : timeout ? GL_SYNC_FLUSH_COMMANDS_BIT : 0,
455 : : timeout);
456 [ # # ]: 0 : if (res == GL_ALREADY_SIGNALED || res == GL_CONDITION_SATISFIED) {
457 : 0 : gl->DeleteSync(buf_gl->fence);
458 : 0 : buf_gl->fence = NULL;
459 : : }
460 : : }
461 : :
462 : 3 : gl_poll_callbacks(gpu);
463 : : RELEASE_CURRENT();
464 : 3 : return !!buf_gl->fence;
465 : : }
466 : :
467 : 347 : void gl_buf_write(pl_gpu gpu, pl_buf buf, size_t offset,
468 : : const void *data, size_t size)
469 : : {
470 : : const gl_funcs *gl = gl_funcs_get(gpu);
471 : : if (!MAKE_CURRENT())
472 : 0 : return;
473 : :
474 : 347 : struct pl_buf_gl *buf_gl = PL_PRIV(buf);
475 : 347 : gl->BindBuffer(GL_ARRAY_BUFFER, buf_gl->buffer);
476 : 347 : gl->BufferSubData(GL_ARRAY_BUFFER, buf_gl->offset + offset, size, data);
477 : 347 : gl->BindBuffer(GL_ARRAY_BUFFER, 0);
478 : 347 : gl_check_err(gpu, "gl_buf_write");
479 : : RELEASE_CURRENT();
480 : : }
481 : :
482 : 46 : bool gl_buf_read(pl_gpu gpu, pl_buf buf, size_t offset,
483 : : void *dest, size_t size)
484 : : {
485 : : const gl_funcs *gl = gl_funcs_get(gpu);
486 : : if (!MAKE_CURRENT())
487 : 0 : return false;
488 : :
489 : 46 : struct pl_buf_gl *buf_gl = PL_PRIV(buf);
490 : 46 : gl->BindBuffer(GL_ARRAY_BUFFER, buf_gl->buffer);
491 : 46 : gl->GetBufferSubData(GL_ARRAY_BUFFER, buf_gl->offset + offset, size, dest);
492 : 46 : gl->BindBuffer(GL_ARRAY_BUFFER, 0);
493 : 46 : bool ok = gl_check_err(gpu, "gl_buf_read");
494 : : RELEASE_CURRENT();
495 : 46 : return ok;
496 : : }
497 : :
498 : 3 : void gl_buf_copy(pl_gpu gpu, pl_buf dst, size_t dst_offset,
499 : : pl_buf src, size_t src_offset, size_t size)
500 : : {
501 : : const gl_funcs *gl = gl_funcs_get(gpu);
502 : : if (!MAKE_CURRENT())
503 : 0 : return;
504 : :
505 : 3 : struct pl_buf_gl *src_gl = PL_PRIV(src);
506 : 3 : struct pl_buf_gl *dst_gl = PL_PRIV(dst);
507 : 3 : gl->BindBuffer(GL_COPY_READ_BUFFER, src_gl->buffer);
508 : 3 : gl->BindBuffer(GL_COPY_WRITE_BUFFER, dst_gl->buffer);
509 : 3 : gl->CopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
510 : 3 : src_gl->offset + src_offset,
511 : 3 : dst_gl->offset + dst_offset, size);
512 : 3 : gl_check_err(gpu, "gl_buf_copy");
513 : : RELEASE_CURRENT();
514 : : }
515 : :
516 : : #define QUERY_OBJECT_NUM 8
517 : :
518 : : struct pl_timer_t {
519 : : GLuint query[QUERY_OBJECT_NUM];
520 : : int index_write; // next index to write to
521 : : int index_read; // next index to read from
522 : : };
523 : :
524 [ + + ]: 884 : static pl_timer gl_timer_create(pl_gpu gpu)
525 : : {
526 : : const gl_funcs *gl = gl_funcs_get(gpu);
527 : : struct pl_gl *p = PL_PRIV(gpu);
528 [ + + ]: 884 : if (!p->has_queries || !MAKE_CURRENT())
529 : 35 : return NULL;
530 : :
531 : 849 : pl_timer timer = pl_zalloc_ptr(NULL, timer);
532 : 849 : gl->GenQueries(QUERY_OBJECT_NUM, timer->query);
533 : : RELEASE_CURRENT();
534 : 849 : return timer;
535 : : }
536 : :
537 : 849 : static void gl_timer_destroy(pl_gpu gpu, pl_timer timer)
538 : : {
539 : : const gl_funcs *gl = gl_funcs_get(gpu);
540 : : if (!MAKE_CURRENT()) {
541 : 0 : PL_ERR(gpu, "Failed uninitializing timer, leaking resources!");
542 : 0 : return;
543 : : }
544 : :
545 : 849 : gl->DeleteQueries(QUERY_OBJECT_NUM, timer->query);
546 : 849 : gl_check_err(gpu, "gl_timer_destroy");
547 : : RELEASE_CURRENT();
548 : 849 : pl_free(timer);
549 : : }
550 : :
551 : 2987 : static uint64_t gl_timer_query(pl_gpu gpu, pl_timer timer)
552 : : {
553 [ + + ]: 2987 : if (timer->index_read == timer->index_write)
554 : : return 0; // no more unprocessed results
555 : :
556 : 2982 : struct pl_gl *p = PL_PRIV(gpu);
557 : :
558 : : const gl_funcs *gl = gl_funcs_get(gpu);
559 : : if (!MAKE_CURRENT())
560 : 0 : return 0;
561 : :
562 : 2982 : uint64_t res = 0;
563 : 2982 : GLuint query = timer->query[timer->index_read];
564 : 2982 : GLuint avail = 0;
565 : 2982 : gl->GetQueryObjectuiv(query, GL_QUERY_RESULT_AVAILABLE, &avail);
566 [ + + ]: 2982 : if (!avail)
567 : 1402 : goto done;
568 [ + - - + ]: 1580 : if (p->gles_ver || p->gl_ver < 33) {
569 : 0 : GLuint tmp = 0;
570 : 0 : gl->GetQueryObjectuiv(query, GL_QUERY_RESULT, &tmp);
571 : 0 : res = tmp;
572 : : } else {
573 : 1580 : gl->GetQueryObjectui64v(query, GL_QUERY_RESULT, &res);
574 : : }
575 : :
576 : 1580 : timer->index_read = (timer->index_read + 1) % QUERY_OBJECT_NUM;
577 : : // fall through
578 : :
579 : 2982 : done:
580 : : RELEASE_CURRENT();
581 : 2982 : return res;
582 : : }
583 : :
584 : 2053 : void gl_timer_begin(pl_gpu gpu, pl_timer timer)
585 : : {
586 [ + + ]: 2053 : if (!timer)
587 : : return;
588 : :
589 : : const gl_funcs *gl = gl_funcs_get(gpu);
590 : 1930 : gl->BeginQuery(GL_TIME_ELAPSED, timer->query[timer->index_write]);
591 : : }
592 : :
593 : 2053 : void gl_timer_end(pl_gpu gpu, pl_timer timer)
594 : : {
595 [ + + ]: 2053 : if (!timer)
596 : : return;
597 : :
598 : : const gl_funcs *gl = gl_funcs_get(gpu);
599 : 1930 : gl->EndQuery(GL_TIME_ELAPSED);
600 : :
601 : 1930 : timer->index_write = (timer->index_write + 1) % QUERY_OBJECT_NUM;
602 [ - + ]: 1930 : if (timer->index_write == timer->index_read) {
603 : : // forcibly drop the least recent result to make space
604 : 0 : timer->index_read = (timer->index_read + 1) % QUERY_OBJECT_NUM;
605 : : }
606 : : }
607 : :
608 : 272 : static void gl_gpu_flush(pl_gpu gpu)
609 : : {
610 : : const gl_funcs *gl = gl_funcs_get(gpu);
611 : : if (!MAKE_CURRENT())
612 : 0 : return;
613 : :
614 : 272 : gl->Flush();
615 : 272 : gl_check_err(gpu, "gl_gpu_flush");
616 : : RELEASE_CURRENT();
617 : : }
618 : :
619 : 281 : static void gl_gpu_finish(pl_gpu gpu)
620 : : {
621 : : const gl_funcs *gl = gl_funcs_get(gpu);
622 : : if (!MAKE_CURRENT())
623 : 0 : return;
624 : :
625 : 281 : gl->Finish();
626 : 281 : gl_check_err(gpu, "gl_gpu_finish");
627 : : RELEASE_CURRENT();
628 : : }
629 : :
630 : 8 : static bool gl_gpu_is_failed(pl_gpu gpu)
631 : : {
632 : 8 : struct pl_gl *gl = PL_PRIV(gpu);
633 : 8 : return gl->failed;
634 : : }
635 : :
636 : : static const struct pl_gpu_fns pl_fns_gl = {
637 : : .destroy = gl_gpu_destroy,
638 : : .tex_create = gl_tex_create,
639 : : .tex_destroy = gl_tex_destroy,
640 : : .tex_invalidate = gl_tex_invalidate,
641 : : .tex_clear_ex = gl_tex_clear_ex,
642 : : .tex_blit = gl_tex_blit,
643 : : .tex_upload = gl_tex_upload,
644 : : .tex_download = gl_tex_download,
645 : : .buf_create = gl_buf_create,
646 : : .buf_destroy = gl_buf_destroy,
647 : : .buf_write = gl_buf_write,
648 : : .buf_read = gl_buf_read,
649 : : .buf_copy = gl_buf_copy,
650 : : .buf_poll = gl_buf_poll,
651 : : .desc_namespace = gl_desc_namespace,
652 : : .pass_create = gl_pass_create,
653 : : .pass_destroy = gl_pass_destroy,
654 : : .pass_run = gl_pass_run,
655 : : .timer_create = gl_timer_create,
656 : : .timer_destroy = gl_timer_destroy,
657 : : .timer_query = gl_timer_query,
658 : : .gpu_flush = gl_gpu_flush,
659 : : .gpu_finish = gl_gpu_finish,
660 : : .gpu_is_failed = gl_gpu_is_failed,
661 : : };
|