1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2012 Collabora Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use, copy,
12 * modify, merge, publish, distribute, sublicense, and/or sell copies
13 * of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include "cogl-fence.h"
35 #include "cogl-fence-private.h"
36 #include "cogl-context-private.h"
37 #include "cogl-winsys-private.h"
38
39 #define FENCE_CHECK_TIMEOUT 5000 /* microseconds */
40
41 void *
cogl_fence_closure_get_user_data(CoglFenceClosure * closure)42 cogl_fence_closure_get_user_data (CoglFenceClosure *closure)
43 {
44 return closure->user_data;
45 }
46
47 static void
_cogl_fence_check(CoglFenceClosure * fence)48 _cogl_fence_check (CoglFenceClosure *fence)
49 {
50 CoglContext *context = fence->framebuffer->context;
51
52 if (fence->type == FENCE_TYPE_WINSYS)
53 {
54 const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
55 CoglBool ret;
56
57 ret = winsys->fence_is_complete (context, fence->fence_obj);
58 if (!ret)
59 return;
60 }
61 #ifdef GL_ARB_sync
62 else if (fence->type == FENCE_TYPE_GL_ARB)
63 {
64 GLenum arb;
65
66 arb = context->glClientWaitSync (fence->fence_obj,
67 GL_SYNC_FLUSH_COMMANDS_BIT,
68 0);
69 if (arb != GL_ALREADY_SIGNALED && arb != GL_CONDITION_SATISFIED)
70 return;
71 }
72 #endif
73
74 fence->callback (NULL, /* dummy CoglFence object */
75 fence->user_data);
76 cogl_framebuffer_cancel_fence_callback (fence->framebuffer, fence);
77 }
78
79 static void
_cogl_fence_poll_dispatch(void * source,int revents)80 _cogl_fence_poll_dispatch (void *source, int revents)
81 {
82 CoglContext *context = source;
83 CoglFenceClosure *fence, *tmp;
84
85 _cogl_list_for_each_safe (fence, tmp, &context->fences, link)
86 _cogl_fence_check (fence);
87 }
88
89 static int64_t
_cogl_fence_poll_prepare(void * source)90 _cogl_fence_poll_prepare (void *source)
91 {
92 CoglContext *context = source;
93 GList *l;
94
95 /* If there are any pending fences in any of the journals then we
96 * need to flush the journal otherwise the fence will never be
97 * hit and the main loop might block forever */
98 for (l = context->framebuffers; l; l = l->next)
99 {
100 CoglFramebuffer *fb = l->data;
101
102 if (!_cogl_list_empty (&fb->journal->pending_fences))
103 _cogl_framebuffer_flush_journal (fb);
104 }
105
106 if (!_cogl_list_empty (&context->fences))
107 return FENCE_CHECK_TIMEOUT;
108 else
109 return -1;
110 }
111
112 void
_cogl_fence_submit(CoglFenceClosure * fence)113 _cogl_fence_submit (CoglFenceClosure *fence)
114 {
115 CoglContext *context = fence->framebuffer->context;
116 const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
117
118 fence->type = FENCE_TYPE_ERROR;
119
120 if (winsys->fence_add)
121 {
122 fence->fence_obj = winsys->fence_add (context);
123 if (fence->fence_obj)
124 {
125 fence->type = FENCE_TYPE_WINSYS;
126 goto done;
127 }
128 }
129
130 #ifdef GL_ARB_sync
131 if (context->glFenceSync)
132 {
133 fence->fence_obj = context->glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE,
134 0);
135 if (fence->fence_obj)
136 {
137 fence->type = FENCE_TYPE_GL_ARB;
138 goto done;
139 }
140 }
141 #endif
142
143 done:
144 _cogl_list_insert (context->fences.prev, &fence->link);
145
146 if (!context->fences_poll_source)
147 {
148 context->fences_poll_source =
149 _cogl_poll_renderer_add_source (context->display->renderer,
150 _cogl_fence_poll_prepare,
151 _cogl_fence_poll_dispatch,
152 context);
153 }
154 }
155
156 CoglFenceClosure *
cogl_framebuffer_add_fence_callback(CoglFramebuffer * framebuffer,CoglFenceCallback callback,void * user_data)157 cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer,
158 CoglFenceCallback callback,
159 void *user_data)
160 {
161 CoglContext *context = framebuffer->context;
162 CoglJournal *journal = framebuffer->journal;
163 CoglFenceClosure *fence;
164
165 if (!COGL_FLAGS_GET (context->features, COGL_FEATURE_ID_FENCE))
166 return NULL;
167
168 fence = g_slice_new (CoglFenceClosure);
169 fence->framebuffer = framebuffer;
170 fence->callback = callback;
171 fence->user_data = user_data;
172 fence->fence_obj = NULL;
173
174 if (journal->entries->len)
175 {
176 _cogl_list_insert (journal->pending_fences.prev, &fence->link);
177 fence->type = FENCE_TYPE_PENDING;
178 }
179 else
180 _cogl_fence_submit (fence);
181
182 return fence;
183 }
184
185 void
cogl_framebuffer_cancel_fence_callback(CoglFramebuffer * framebuffer,CoglFenceClosure * fence)186 cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer,
187 CoglFenceClosure *fence)
188 {
189 CoglContext *context = framebuffer->context;
190
191 if (fence->type == FENCE_TYPE_PENDING)
192 {
193 _cogl_list_remove (&fence->link);
194 }
195 else
196 {
197 _cogl_list_remove (&fence->link);
198
199 if (fence->type == FENCE_TYPE_WINSYS)
200 {
201 const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
202
203 winsys->fence_destroy (context, fence->fence_obj);
204 }
205 #ifdef GL_ARB_sync
206 else if (fence->type == FENCE_TYPE_GL_ARB)
207 {
208 context->glDeleteSync (fence->fence_obj);
209 }
210 #endif
211 }
212
213 g_slice_free (CoglFenceClosure, fence);
214 }
215
216 void
_cogl_fence_cancel_fences_for_framebuffer(CoglFramebuffer * framebuffer)217 _cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer)
218 {
219 CoglJournal *journal = framebuffer->journal;
220 CoglContext *context = framebuffer->context;
221 CoglFenceClosure *fence, *tmp;
222
223 while (!_cogl_list_empty (&journal->pending_fences))
224 {
225 fence = _cogl_container_of (journal->pending_fences.next,
226 CoglFenceClosure,
227 link);
228 cogl_framebuffer_cancel_fence_callback (framebuffer, fence);
229 }
230
231 _cogl_list_for_each_safe (fence, tmp, &context->fences, link)
232 {
233 if (fence->framebuffer == framebuffer)
234 cogl_framebuffer_cancel_fence_callback (framebuffer, fence);
235 }
236 }
237