2 * Copyright © 2008 Intel Corporation
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:
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
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
24 * Eric Anholt <eric@anholt.net>
30 * \brief Support for GL_ARB_sync and EGL_KHR_fence_sync.
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.
41 #include <libsync.h> /* Requires Android or libdrm-2.4.72 */
43 #include "brw_context.h"
44 #include "intel_batchbuffer.h"
47 struct brw_context
*brw
;
50 /** The fence waits for completion of brw_fence::batch_bo. */
51 BRW_FENCE_TYPE_BO_WAIT
,
53 /** The fence waits for brw_fence::sync_fd to signal. */
54 BRW_FENCE_TYPE_SYNC_FD
,
58 struct brw_bo
*batch_bo
;
60 /* This struct owns the fd. */
69 struct gl_sync_object gl
;
70 struct brw_fence fence
;
74 brw_fence_init(struct brw_context
*brw
, struct brw_fence
*fence
,
75 enum brw_fence_type type
)
79 mtx_init(&fence
->mutex
, mtx_plain
);
82 case BRW_FENCE_TYPE_BO_WAIT
:
83 fence
->batch_bo
= NULL
;
85 case BRW_FENCE_TYPE_SYNC_FD
:
92 brw_fence_finish(struct brw_fence
*fence
)
94 switch (fence
->type
) {
95 case BRW_FENCE_TYPE_BO_WAIT
:
97 brw_bo_unreference(fence
->batch_bo
);
99 case BRW_FENCE_TYPE_SYNC_FD
:
100 if (fence
->sync_fd
!= -1)
101 close(fence
->sync_fd
);
105 mtx_destroy(&fence
->mutex
);
108 static bool MUST_CHECK
109 brw_fence_insert_locked(struct brw_context
*brw
, struct brw_fence
*fence
)
111 __DRIcontext
*driContext
= brw
->driContext
;
112 __DRIdrawable
*driDrawable
= driContext
->driDrawablePriv
;
115 * From KHR_fence_sync:
117 * When the condition of the sync object is satisfied by the fence
118 * command, the sync is signaled by the associated client API context,
119 * causing any eglClientWaitSyncKHR commands (see below) blocking on
120 * <sync> to unblock. The only condition currently supported is
121 * EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR, which is satisfied by
122 * completion of the fence command corresponding to the sync object,
123 * and all preceding commands in the associated client API context's
124 * command stream. The sync object will not be signaled until all
125 * effects from these commands on the client API's internal and
126 * framebuffer state are fully realized. No other state is affected by
127 * execution of the fence command.
129 * Note the emphasis there on ensuring that the framebuffer is fully
130 * realised before the fence is signaled. We cannot just flush the batch,
131 * but must also resolve the drawable first. The importance of this is,
132 * for example, in creating a fence for a frame to be passed to a
133 * remote compositor. Without us flushing the drawable explicitly, the
134 * resolve will be in a following batch (when the client finally calls
135 * SwapBuffers, or triggers a resolve via some other path) and so the
136 * compositor may read the incomplete framebuffer instead.
139 intel_resolve_for_dri2_flush(brw
, driDrawable
);
140 brw_emit_mi_flush(brw
);
142 switch (fence
->type
) {
143 case BRW_FENCE_TYPE_BO_WAIT
:
144 assert(!fence
->batch_bo
);
145 assert(!fence
->signalled
);
147 fence
->batch_bo
= brw
->batch
.batch
.bo
;
148 brw_bo_reference(fence
->batch_bo
);
150 if (intel_batchbuffer_flush(brw
) < 0) {
151 brw_bo_unreference(fence
->batch_bo
);
152 fence
->batch_bo
= NULL
;
156 case BRW_FENCE_TYPE_SYNC_FD
:
157 assert(!fence
->signalled
);
159 if (fence
->sync_fd
== -1) {
160 /* Create an out-fence that signals after all pending commands
163 if (intel_batchbuffer_flush_fence(brw
, -1, &fence
->sync_fd
) < 0)
165 assert(fence
->sync_fd
!= -1);
167 /* Wait on the in-fence before executing any subsequently submitted
170 if (intel_batchbuffer_flush(brw
) < 0)
173 /* Emit a dummy batch just for the fence. */
174 brw_emit_mi_flush(brw
);
175 if (intel_batchbuffer_flush_fence(brw
, fence
->sync_fd
, NULL
) < 0)
184 static bool MUST_CHECK
185 brw_fence_insert(struct brw_context
*brw
, struct brw_fence
*fence
)
189 mtx_lock(&fence
->mutex
);
190 ret
= brw_fence_insert_locked(brw
, fence
);
191 mtx_unlock(&fence
->mutex
);
197 brw_fence_has_completed_locked(struct brw_fence
*fence
)
199 if (fence
->signalled
)
202 switch (fence
->type
) {
203 case BRW_FENCE_TYPE_BO_WAIT
:
204 if (!fence
->batch_bo
) {
205 /* There may be no batch if intel_batchbuffer_flush() failed. */
209 if (brw_bo_busy(fence
->batch_bo
))
212 brw_bo_unreference(fence
->batch_bo
);
213 fence
->batch_bo
= NULL
;
214 fence
->signalled
= true;
218 case BRW_FENCE_TYPE_SYNC_FD
:
219 assert(fence
->sync_fd
!= -1);
221 if (sync_wait(fence
->sync_fd
, 0) == -1)
224 fence
->signalled
= true;
233 brw_fence_has_completed(struct brw_fence
*fence
)
237 mtx_lock(&fence
->mutex
);
238 ret
= brw_fence_has_completed_locked(fence
);
239 mtx_unlock(&fence
->mutex
);
245 brw_fence_client_wait_locked(struct brw_context
*brw
, struct brw_fence
*fence
,
250 if (fence
->signalled
)
253 switch (fence
->type
) {
254 case BRW_FENCE_TYPE_BO_WAIT
:
255 if (!fence
->batch_bo
) {
256 /* There may be no batch if intel_batchbuffer_flush() failed. */
260 /* DRM_IOCTL_I915_GEM_WAIT uses a signed 64 bit timeout and returns
261 * immediately for timeouts <= 0. The best we can do is to clamp the
262 * timeout to INT64_MAX. This limits the maximum timeout from 584 years to
263 * 292 years - likely not a big deal.
265 if (timeout
> INT64_MAX
)
268 if (brw_bo_wait(fence
->batch_bo
, timeout
) != 0)
271 fence
->signalled
= true;
272 brw_bo_unreference(fence
->batch_bo
);
273 fence
->batch_bo
= NULL
;
276 case BRW_FENCE_TYPE_SYNC_FD
:
277 if (fence
->sync_fd
== -1)
280 if (timeout
> INT32_MAX
)
283 timeout_i32
= timeout
;
285 if (sync_wait(fence
->sync_fd
, timeout_i32
) == -1)
288 fence
->signalled
= true;
292 assert(!"bad enum brw_fence_type");
297 * Return true if the function successfully signals or has already signalled.
298 * (This matches the behavior expected from __DRI2fence::client_wait_sync).
301 brw_fence_client_wait(struct brw_context
*brw
, struct brw_fence
*fence
,
306 mtx_lock(&fence
->mutex
);
307 ret
= brw_fence_client_wait_locked(brw
, fence
, timeout
);
308 mtx_unlock(&fence
->mutex
);
314 brw_fence_server_wait(struct brw_context
*brw
, struct brw_fence
*fence
)
316 switch (fence
->type
) {
317 case BRW_FENCE_TYPE_BO_WAIT
:
318 /* We have nothing to do for WaitSync. Our GL command stream is sequential,
319 * so given that the sync object has already flushed the batchbuffer, any
320 * batchbuffers coming after this waitsync will naturally not occur until
321 * the previous one is done.
324 case BRW_FENCE_TYPE_SYNC_FD
:
325 assert(fence
->sync_fd
!= -1);
327 /* The user wants explicit synchronization, so give them what they want. */
328 if (!brw_fence_insert(brw
, fence
)) {
329 /* FIXME: There exists no way yet to report an error here. If an error
330 * occurs, continue silently and hope for the best.
337 static struct gl_sync_object
*
338 brw_gl_new_sync(struct gl_context
*ctx
)
340 struct brw_gl_sync
*sync
;
342 sync
= calloc(1, sizeof(*sync
));
350 brw_gl_delete_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
)
352 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
354 brw_fence_finish(&sync
->fence
);
355 free(sync
->gl
.Label
);
360 brw_gl_fence_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
,
361 GLenum condition
, GLbitfield flags
)
363 struct brw_context
*brw
= brw_context(ctx
);
364 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
366 /* brw_fence_insert_locked() assumes it must do a complete flush */
367 assert(condition
== GL_SYNC_GPU_COMMANDS_COMPLETE
);
369 brw_fence_init(brw
, &sync
->fence
, BRW_FENCE_TYPE_BO_WAIT
);
371 if (!brw_fence_insert_locked(brw
, &sync
->fence
)) {
372 /* FIXME: There exists no way to report a GL error here. If an error
373 * occurs, continue silently and hope for the best.
379 brw_gl_client_wait_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
,
380 GLbitfield flags
, GLuint64 timeout
)
382 struct brw_context
*brw
= brw_context(ctx
);
383 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
385 if (brw_fence_client_wait(brw
, &sync
->fence
, timeout
))
386 sync
->gl
.StatusFlag
= 1;
390 brw_gl_server_wait_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
,
391 GLbitfield flags
, GLuint64 timeout
)
393 struct brw_context
*brw
= brw_context(ctx
);
394 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
396 brw_fence_server_wait(brw
, &sync
->fence
);
400 brw_gl_check_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
)
402 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
404 if (brw_fence_has_completed(&sync
->fence
))
405 sync
->gl
.StatusFlag
= 1;
409 brw_init_syncobj_functions(struct dd_function_table
*functions
)
411 functions
->NewSyncObject
= brw_gl_new_sync
;
412 functions
->DeleteSyncObject
= brw_gl_delete_sync
;
413 functions
->FenceSync
= brw_gl_fence_sync
;
414 functions
->CheckSync
= brw_gl_check_sync
;
415 functions
->ClientWaitSync
= brw_gl_client_wait_sync
;
416 functions
->ServerWaitSync
= brw_gl_server_wait_sync
;
420 brw_dri_create_fence(__DRIcontext
*ctx
)
422 struct brw_context
*brw
= ctx
->driverPrivate
;
423 struct brw_fence
*fence
;
425 fence
= calloc(1, sizeof(*fence
));
429 brw_fence_init(brw
, fence
, BRW_FENCE_TYPE_BO_WAIT
);
431 if (!brw_fence_insert_locked(brw
, fence
)) {
432 brw_fence_finish(fence
);
441 brw_dri_destroy_fence(__DRIscreen
*dri_screen
, void *_fence
)
443 struct brw_fence
*fence
= _fence
;
445 brw_fence_finish(fence
);
450 brw_dri_client_wait_sync(__DRIcontext
*ctx
, void *_fence
, unsigned flags
,
453 struct brw_fence
*fence
= _fence
;
455 return brw_fence_client_wait(fence
->brw
, fence
, timeout
);
459 brw_dri_server_wait_sync(__DRIcontext
*ctx
, void *_fence
, unsigned flags
)
461 struct brw_fence
*fence
= _fence
;
463 /* We might be called here with a NULL fence as a result of WaitSyncKHR
464 * on a EGL_KHR_reusable_sync fence. Nothing to do here in such case.
469 brw_fence_server_wait(fence
->brw
, fence
);
473 brw_dri_get_capabilities(__DRIscreen
*dri_screen
)
475 struct intel_screen
*screen
= dri_screen
->driverPrivate
;
478 if (screen
->has_exec_fence
)
479 caps
|= __DRI_FENCE_CAP_NATIVE_FD
;
485 brw_dri_create_fence_fd(__DRIcontext
*dri_ctx
, int fd
)
487 struct brw_context
*brw
= dri_ctx
->driverPrivate
;
488 struct brw_fence
*fence
;
490 assert(brw
->screen
->has_exec_fence
);
492 fence
= calloc(1, sizeof(*fence
));
496 brw_fence_init(brw
, fence
, BRW_FENCE_TYPE_SYNC_FD
);
499 /* Create an out-fence fd */
500 if (!brw_fence_insert_locked(brw
, fence
))
503 /* Import the sync fd as an in-fence. */
504 fence
->sync_fd
= dup(fd
);
507 assert(fence
->sync_fd
!= -1);
512 brw_fence_finish(fence
);
518 brw_dri_get_fence_fd_locked(struct brw_fence
*fence
)
520 assert(fence
->type
== BRW_FENCE_TYPE_SYNC_FD
);
521 return dup(fence
->sync_fd
);
525 brw_dri_get_fence_fd(__DRIscreen
*dri_screen
, void *_fence
)
527 struct brw_fence
*fence
= _fence
;
530 mtx_lock(&fence
->mutex
);
531 fd
= brw_dri_get_fence_fd_locked(fence
);
532 mtx_unlock(&fence
->mutex
);
537 const __DRI2fenceExtension intelFenceExtension
= {
538 .base
= { __DRI2_FENCE
, 2 },
540 .create_fence
= brw_dri_create_fence
,
541 .destroy_fence
= brw_dri_destroy_fence
,
542 .client_wait_sync
= brw_dri_client_wait_sync
,
543 .server_wait_sync
= brw_dri_server_wait_sync
,
544 .get_fence_from_cl_event
= NULL
,
545 .get_capabilities
= brw_dri_get_capabilities
,
546 .create_fence_fd
= brw_dri_create_fence_fd
,
547 .get_fence_fd
= brw_dri_get_fence_fd
,