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