1/*
2 * Copyright © 2008 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 *    Eric Anholt <eric@anholt.net>
25 *
26 */
27
28/**
29 * \file
30 * \brief Support for GL_ARB_sync and EGL_KHR_fence_sync.
31 *
32 * GL_ARB_sync is implemented by flushing the current batchbuffer and keeping a
33 * reference on it.  We can then check for completion or wait for completion
34 * using the normal buffer object mechanisms.  This does mean that if an
35 * application is using many sync objects, it will emit small batchbuffers
36 * which may end up being a significant overhead.  In other tests of removing
37 * gratuitous batchbuffer syncs in Mesa, it hasn't appeared to be a significant
38 * performance bottleneck, though.
39 */
40
41#include <libsync.h> /* Requires Android or libdrm-2.4.72 */
42
43#include "main/imports.h"
44
45#include "brw_context.h"
46#include "intel_batchbuffer.h"
47
48struct brw_fence {
49   struct brw_context *brw;
50
51   enum brw_fence_type {
52      /** The fence waits for completion of brw_fence::batch_bo. */
53      BRW_FENCE_TYPE_BO_WAIT,
54
55      /** The fence waits for brw_fence::sync_fd to signal. */
56      BRW_FENCE_TYPE_SYNC_FD,
57   } type;
58
59   union {
60      struct brw_bo *batch_bo;
61
62      /* This struct owns the fd. */
63      int sync_fd;
64   };
65
66   mtx_t mutex;
67   bool signalled;
68};
69
70struct brw_gl_sync {
71   struct gl_sync_object gl;
72   struct brw_fence fence;
73};
74
75static void
76brw_fence_init(struct brw_context *brw, struct brw_fence *fence,
77               enum brw_fence_type type)
78{
79   fence->brw = brw;
80   fence->type = type;
81   mtx_init(&fence->mutex, mtx_plain);
82
83   switch (type) {
84   case BRW_FENCE_TYPE_BO_WAIT:
85      fence->batch_bo = NULL;
86      break;
87    case BRW_FENCE_TYPE_SYNC_FD:
88      fence->sync_fd = -1;
89      break;
90   }
91}
92
93static void
94brw_fence_finish(struct brw_fence *fence)
95{
96   switch (fence->type) {
97   case BRW_FENCE_TYPE_BO_WAIT:
98      if (fence->batch_bo)
99         brw_bo_unreference(fence->batch_bo);
100      break;
101   case BRW_FENCE_TYPE_SYNC_FD:
102      if (fence->sync_fd != -1)
103         close(fence->sync_fd);
104      break;
105   }
106
107   mtx_destroy(&fence->mutex);
108}
109
110static bool MUST_CHECK
111brw_fence_insert_locked(struct brw_context *brw, struct brw_fence *fence)
112{
113   __DRIcontext *driContext = brw->driContext;
114   __DRIdrawable *driDrawable = driContext->driDrawablePriv;
115
116   /*
117    * From KHR_fence_sync:
118    *
119    *   When the condition of the sync object is satisfied by the fence
120    *   command, the sync is signaled by the associated client API context,
121    *   causing any eglClientWaitSyncKHR commands (see below) blocking on
122    *   <sync> to unblock. The only condition currently supported is
123    *   EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR, which is satisfied by
124    *   completion of the fence command corresponding to the sync object,
125    *   and all preceding commands in the associated client API context's
126    *   command stream. The sync object will not be signaled until all
127    *   effects from these commands on the client API's internal and
128    *   framebuffer state are fully realized. No other state is affected by
129    *   execution of the fence command.
130    *
131    * Note the emphasis there on ensuring that the framebuffer is fully
132    * realised before the fence is signaled. We cannot just flush the batch,
133    * but must also resolve the drawable first. The importance of this is,
134    * for example, in creating a fence for a frame to be passed to a
135    * remote compositor. Without us flushing the drawable explicitly, the
136    * resolve will be in a following batch (when the client finally calls
137    * SwapBuffers, or triggers a resolve via some other path) and so the
138    * compositor may read the incomplete framebuffer instead.
139    */
140   if (driDrawable)
141      intel_resolve_for_dri2_flush(brw, driDrawable);
142   brw_emit_mi_flush(brw);
143
144   switch (fence->type) {
145   case BRW_FENCE_TYPE_BO_WAIT:
146      assert(!fence->batch_bo);
147      assert(!fence->signalled);
148
149      fence->batch_bo = brw->batch.batch.bo;
150      brw_bo_reference(fence->batch_bo);
151
152      if (intel_batchbuffer_flush(brw) < 0) {
153         brw_bo_unreference(fence->batch_bo);
154         fence->batch_bo = NULL;
155         return false;
156      }
157      break;
158   case BRW_FENCE_TYPE_SYNC_FD:
159      assert(!fence->signalled);
160
161      if (fence->sync_fd == -1) {
162         /* Create an out-fence that signals after all pending commands
163          * complete.
164          */
165         if (intel_batchbuffer_flush_fence(brw, -1, &fence->sync_fd) < 0)
166            return false;
167         assert(fence->sync_fd != -1);
168      } else {
169         /* Wait on the in-fence before executing any subsequently submitted
170          * commands.
171          */
172         if (intel_batchbuffer_flush(brw) < 0)
173            return false;
174
175         /* Emit a dummy batch just for the fence. */
176         brw_emit_mi_flush(brw);
177         if (intel_batchbuffer_flush_fence(brw, fence->sync_fd, NULL) < 0)
178            return false;
179      }
180      break;
181   }
182
183   return true;
184}
185
186static bool MUST_CHECK
187brw_fence_insert(struct brw_context *brw, struct brw_fence *fence)
188{
189   bool ret;
190
191   mtx_lock(&fence->mutex);
192   ret = brw_fence_insert_locked(brw, fence);
193   mtx_unlock(&fence->mutex);
194
195   return ret;
196}
197
198static bool
199brw_fence_has_completed_locked(struct brw_fence *fence)
200{
201   if (fence->signalled)
202      return true;
203
204   switch (fence->type) {
205   case BRW_FENCE_TYPE_BO_WAIT:
206      if (!fence->batch_bo) {
207         /* There may be no batch if intel_batchbuffer_flush() failed. */
208         return false;
209      }
210
211      if (brw_bo_busy(fence->batch_bo))
212         return false;
213
214      brw_bo_unreference(fence->batch_bo);
215      fence->batch_bo = NULL;
216      fence->signalled = true;
217
218      return true;
219
220   case BRW_FENCE_TYPE_SYNC_FD:
221      assert(fence->sync_fd != -1);
222
223      if (sync_wait(fence->sync_fd, 0) == -1)
224         return false;
225
226      fence->signalled = true;
227
228      return true;
229   }
230
231   return false;
232}
233
234static bool
235brw_fence_has_completed(struct brw_fence *fence)
236{
237   bool ret;
238
239   mtx_lock(&fence->mutex);
240   ret = brw_fence_has_completed_locked(fence);
241   mtx_unlock(&fence->mutex);
242
243   return ret;
244}
245
246static bool
247brw_fence_client_wait_locked(struct brw_context *brw, struct brw_fence *fence,
248                             uint64_t timeout)
249{
250   int32_t timeout_i32;
251
252   if (fence->signalled)
253      return true;
254
255   switch (fence->type) {
256   case BRW_FENCE_TYPE_BO_WAIT:
257      if (!fence->batch_bo) {
258         /* There may be no batch if intel_batchbuffer_flush() failed. */
259         return false;
260      }
261
262      /* DRM_IOCTL_I915_GEM_WAIT uses a signed 64 bit timeout and returns
263       * immediately for timeouts <= 0.  The best we can do is to clamp the
264       * timeout to INT64_MAX.  This limits the maximum timeout from 584 years to
265       * 292 years - likely not a big deal.
266       */
267      if (timeout > INT64_MAX)
268         timeout = INT64_MAX;
269
270      if (brw_bo_wait(fence->batch_bo, timeout) != 0)
271         return false;
272
273      fence->signalled = true;
274      brw_bo_unreference(fence->batch_bo);
275      fence->batch_bo = NULL;
276
277      return true;
278   case BRW_FENCE_TYPE_SYNC_FD:
279      if (fence->sync_fd == -1)
280         return false;
281
282      if (timeout > INT32_MAX)
283         timeout_i32 = -1;
284      else
285         timeout_i32 = timeout;
286
287      if (sync_wait(fence->sync_fd, timeout_i32) == -1)
288         return false;
289
290      fence->signalled = true;
291      return true;
292   }
293
294   assert(!"bad enum brw_fence_type");
295   return false;
296}
297
298/**
299 * Return true if the function successfully signals or has already signalled.
300 * (This matches the behavior expected from __DRI2fence::client_wait_sync).
301 */
302static bool
303brw_fence_client_wait(struct brw_context *brw, struct brw_fence *fence,
304                      uint64_t timeout)
305{
306   bool ret;
307
308   mtx_lock(&fence->mutex);
309   ret = brw_fence_client_wait_locked(brw, fence, timeout);
310   mtx_unlock(&fence->mutex);
311
312   return ret;
313}
314
315static void
316brw_fence_server_wait(struct brw_context *brw, struct brw_fence *fence)
317{
318   switch (fence->type) {
319   case BRW_FENCE_TYPE_BO_WAIT:
320      /* We have nothing to do for WaitSync.  Our GL command stream is sequential,
321       * so given that the sync object has already flushed the batchbuffer, any
322       * batchbuffers coming after this waitsync will naturally not occur until
323       * the previous one is done.
324       */
325      break;
326   case BRW_FENCE_TYPE_SYNC_FD:
327      assert(fence->sync_fd != -1);
328
329      /* The user wants explicit synchronization, so give them what they want. */
330      if (!brw_fence_insert(brw, fence)) {
331         /* FIXME: There exists no way yet to report an error here. If an error
332          * occurs, continue silently and hope for the best.
333          */
334      }
335      break;
336   }
337}
338
339static struct gl_sync_object *
340brw_gl_new_sync(struct gl_context *ctx)
341{
342   struct brw_gl_sync *sync;
343
344   sync = calloc(1, sizeof(*sync));
345   if (!sync)
346      return NULL;
347
348   return &sync->gl;
349}
350
351static void
352brw_gl_delete_sync(struct gl_context *ctx, struct gl_sync_object *_sync)
353{
354   struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync;
355
356   brw_fence_finish(&sync->fence);
357   free(sync);
358}
359
360static void
361brw_gl_fence_sync(struct gl_context *ctx, struct gl_sync_object *_sync,
362                  GLenum condition, GLbitfield flags)
363{
364   struct brw_context *brw = brw_context(ctx);
365   struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync;
366
367   /* brw_fence_insert_locked() assumes it must do a complete flush */
368   assert(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
369
370   brw_fence_init(brw, &sync->fence, BRW_FENCE_TYPE_BO_WAIT);
371
372   if (!brw_fence_insert_locked(brw, &sync->fence)) {
373      /* FIXME: There exists no way to report a GL error here. If an error
374       * occurs, continue silently and hope for the best.
375       */
376   }
377}
378
379static void
380brw_gl_client_wait_sync(struct gl_context *ctx, struct gl_sync_object *_sync,
381                        GLbitfield flags, GLuint64 timeout)
382{
383   struct brw_context *brw = brw_context(ctx);
384   struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync;
385
386   if (brw_fence_client_wait(brw, &sync->fence, timeout))
387      sync->gl.StatusFlag = 1;
388}
389
390static void
391brw_gl_server_wait_sync(struct gl_context *ctx, struct gl_sync_object *_sync,
392                          GLbitfield flags, GLuint64 timeout)
393{
394   struct brw_context *brw = brw_context(ctx);
395   struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync;
396
397   brw_fence_server_wait(brw, &sync->fence);
398}
399
400static void
401brw_gl_check_sync(struct gl_context *ctx, struct gl_sync_object *_sync)
402{
403   struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync;
404
405   if (brw_fence_has_completed(&sync->fence))
406      sync->gl.StatusFlag = 1;
407}
408
409void
410brw_init_syncobj_functions(struct dd_function_table *functions)
411{
412   functions->NewSyncObject = brw_gl_new_sync;
413   functions->DeleteSyncObject = brw_gl_delete_sync;
414   functions->FenceSync = brw_gl_fence_sync;
415   functions->CheckSync = brw_gl_check_sync;
416   functions->ClientWaitSync = brw_gl_client_wait_sync;
417   functions->ServerWaitSync = brw_gl_server_wait_sync;
418}
419
420static void *
421brw_dri_create_fence(__DRIcontext *ctx)
422{
423   struct brw_context *brw = ctx->driverPrivate;
424   struct brw_fence *fence;
425
426   fence = calloc(1, sizeof(*fence));
427   if (!fence)
428      return NULL;
429
430   brw_fence_init(brw, fence, BRW_FENCE_TYPE_BO_WAIT);
431
432   if (!brw_fence_insert_locked(brw, fence)) {
433      brw_fence_finish(fence);
434      free(fence);
435      return NULL;
436   }
437
438   return fence;
439}
440
441static void
442brw_dri_destroy_fence(__DRIscreen *dri_screen, void *_fence)
443{
444   struct brw_fence *fence = _fence;
445
446   brw_fence_finish(fence);
447   free(fence);
448}
449
450static GLboolean
451brw_dri_client_wait_sync(__DRIcontext *ctx, void *_fence, unsigned flags,
452                         uint64_t timeout)
453{
454   struct brw_fence *fence = _fence;
455
456   return brw_fence_client_wait(fence->brw, fence, timeout);
457}
458
459static void
460brw_dri_server_wait_sync(__DRIcontext *ctx, void *_fence, unsigned flags)
461{
462   struct brw_fence *fence = _fence;
463
464   /* We might be called here with a NULL fence as a result of WaitSyncKHR
465    * on a EGL_KHR_reusable_sync fence. Nothing to do here in such case.
466    */
467   if (!fence)
468      return;
469
470   brw_fence_server_wait(fence->brw, fence);
471}
472
473static unsigned
474brw_dri_get_capabilities(__DRIscreen *dri_screen)
475{
476   struct intel_screen *screen = dri_screen->driverPrivate;
477   unsigned caps = 0;
478
479   if (screen->has_exec_fence)
480      caps |=  __DRI_FENCE_CAP_NATIVE_FD;
481
482   return caps;
483}
484
485static void *
486brw_dri_create_fence_fd(__DRIcontext *dri_ctx, int fd)
487{
488   struct brw_context *brw = dri_ctx->driverPrivate;
489   struct brw_fence *fence;
490
491   assert(brw->screen->has_exec_fence);
492
493   fence = calloc(1, sizeof(*fence));
494   if (!fence)
495      return NULL;
496
497   brw_fence_init(brw, fence, BRW_FENCE_TYPE_SYNC_FD);
498
499   if (fd == -1) {
500      /* Create an out-fence fd */
501      if (!brw_fence_insert_locked(brw, fence))
502         goto fail;
503   } else {
504      /* Import the sync fd as an in-fence. */
505      fence->sync_fd = dup(fd);
506   }
507
508   assert(fence->sync_fd != -1);
509
510   return fence;
511
512fail:
513   brw_fence_finish(fence);
514   free(fence);
515   return NULL;
516}
517
518static int
519brw_dri_get_fence_fd_locked(struct brw_fence *fence)
520{
521   assert(fence->type == BRW_FENCE_TYPE_SYNC_FD);
522   return dup(fence->sync_fd);
523}
524
525static int
526brw_dri_get_fence_fd(__DRIscreen *dri_screen, void *_fence)
527{
528   struct brw_fence *fence = _fence;
529   int fd;
530
531   mtx_lock(&fence->mutex);
532   fd = brw_dri_get_fence_fd_locked(fence);
533   mtx_unlock(&fence->mutex);
534
535   return fd;
536}
537
538const __DRI2fenceExtension intelFenceExtension = {
539   .base = { __DRI2_FENCE, 2 },
540
541   .create_fence = brw_dri_create_fence,
542   .destroy_fence = brw_dri_destroy_fence,
543   .client_wait_sync = brw_dri_client_wait_sync,
544   .server_wait_sync = brw_dri_server_wait_sync,
545   .get_fence_from_cl_event = NULL,
546   .get_capabilities = brw_dri_get_capabilities,
547   .create_fence_fd = brw_dri_create_fence_fd,
548   .get_fence_fd = brw_dri_get_fence_fd,
549};
550