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 "main/imports.h"
45 #include "brw_context.h"
46 #include "intel_batchbuffer.h"
49 struct brw_context
*brw
;
52 /** The fence waits for completion of brw_fence::batch_bo. */
53 BRW_FENCE_TYPE_BO_WAIT
,
55 /** The fence waits for brw_fence::sync_fd to signal. */
56 BRW_FENCE_TYPE_SYNC_FD
,
60 struct brw_bo
*batch_bo
;
62 /* This struct owns the fd. */
71 struct gl_sync_object gl
;
72 struct brw_fence fence
;
76 brw_fence_init(struct brw_context
*brw
, struct brw_fence
*fence
,
77 enum brw_fence_type type
)
81 mtx_init(&fence
->mutex
, mtx_plain
);
84 case BRW_FENCE_TYPE_BO_WAIT
:
85 fence
->batch_bo
= NULL
;
87 case BRW_FENCE_TYPE_SYNC_FD
:
94 brw_fence_finish(struct brw_fence
*fence
)
96 switch (fence
->type
) {
97 case BRW_FENCE_TYPE_BO_WAIT
:
99 brw_bo_unreference(fence
->batch_bo
);
101 case BRW_FENCE_TYPE_SYNC_FD
:
102 if (fence
->sync_fd
!= -1)
103 close(fence
->sync_fd
);
107 mtx_destroy(&fence
->mutex
);
110 static bool MUST_CHECK
111 brw_fence_insert_locked(struct brw_context
*brw
, struct brw_fence
*fence
)
113 __DRIcontext
*driContext
= brw
->driContext
;
114 __DRIdrawable
*driDrawable
= driContext
->driDrawablePriv
;
117 * From KHR_fence_sync:
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.
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.
141 intel_resolve_for_dri2_flush(brw
, driDrawable
);
142 brw_emit_mi_flush(brw
);
144 switch (fence
->type
) {
145 case BRW_FENCE_TYPE_BO_WAIT
:
146 assert(!fence
->batch_bo
);
147 assert(!fence
->signalled
);
149 fence
->batch_bo
= brw
->batch
.bo
;
150 brw_bo_reference(fence
->batch_bo
);
152 if (intel_batchbuffer_flush(brw
) < 0) {
153 brw_bo_unreference(fence
->batch_bo
);
154 fence
->batch_bo
= NULL
;
158 case BRW_FENCE_TYPE_SYNC_FD
:
159 assert(!fence
->signalled
);
161 if (fence
->sync_fd
== -1) {
162 /* Create an out-fence that signals after all pending commands
165 if (intel_batchbuffer_flush_fence(brw
, -1, &fence
->sync_fd
) < 0)
167 assert(fence
->sync_fd
!= -1);
169 /* Wait on the in-fence before executing any subsequently submitted
172 if (intel_batchbuffer_flush(brw
) < 0)
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)
186 static bool MUST_CHECK
187 brw_fence_insert(struct brw_context
*brw
, struct brw_fence
*fence
)
191 mtx_lock(&fence
->mutex
);
192 ret
= brw_fence_insert_locked(brw
, fence
);
193 mtx_unlock(&fence
->mutex
);
199 brw_fence_has_completed_locked(struct brw_fence
*fence
)
201 if (fence
->signalled
)
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. */
211 if (brw_bo_busy(fence
->batch_bo
))
214 brw_bo_unreference(fence
->batch_bo
);
215 fence
->batch_bo
= NULL
;
216 fence
->signalled
= true;
220 case BRW_FENCE_TYPE_SYNC_FD
:
221 assert(fence
->sync_fd
!= -1);
223 if (sync_wait(fence
->sync_fd
, 0) == -1)
226 fence
->signalled
= true;
235 brw_fence_has_completed(struct brw_fence
*fence
)
239 mtx_lock(&fence
->mutex
);
240 ret
= brw_fence_has_completed_locked(fence
);
241 mtx_unlock(&fence
->mutex
);
247 brw_fence_client_wait_locked(struct brw_context
*brw
, struct brw_fence
*fence
,
252 if (fence
->signalled
)
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. */
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.
267 if (timeout
> INT64_MAX
)
270 if (brw_bo_wait(fence
->batch_bo
, timeout
) != 0)
273 fence
->signalled
= true;
274 brw_bo_unreference(fence
->batch_bo
);
275 fence
->batch_bo
= NULL
;
278 case BRW_FENCE_TYPE_SYNC_FD
:
279 if (fence
->sync_fd
== -1)
282 if (timeout
> INT32_MAX
)
285 timeout_i32
= timeout
;
287 if (sync_wait(fence
->sync_fd
, timeout_i32
) == -1)
290 fence
->signalled
= true;
294 assert(!"bad enum brw_fence_type");
299 * Return true if the function successfully signals or has already signalled.
300 * (This matches the behavior expected from __DRI2fence::client_wait_sync).
303 brw_fence_client_wait(struct brw_context
*brw
, struct brw_fence
*fence
,
308 mtx_lock(&fence
->mutex
);
309 ret
= brw_fence_client_wait_locked(brw
, fence
, timeout
);
310 mtx_unlock(&fence
->mutex
);
316 brw_fence_server_wait(struct brw_context
*brw
, struct brw_fence
*fence
)
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.
326 case BRW_FENCE_TYPE_SYNC_FD
:
327 assert(fence
->sync_fd
!= -1);
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.
339 static struct gl_sync_object
*
340 brw_gl_new_sync(struct gl_context
*ctx
)
342 struct brw_gl_sync
*sync
;
344 sync
= calloc(1, sizeof(*sync
));
352 brw_gl_delete_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
)
354 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
356 brw_fence_finish(&sync
->fence
);
361 brw_gl_fence_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
,
362 GLenum condition
, GLbitfield flags
)
364 struct brw_context
*brw
= brw_context(ctx
);
365 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
367 /* brw_fence_insert_locked() assumes it must do a complete flush */
368 assert(condition
== GL_SYNC_GPU_COMMANDS_COMPLETE
);
370 brw_fence_init(brw
, &sync
->fence
, BRW_FENCE_TYPE_BO_WAIT
);
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.
380 brw_gl_client_wait_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
,
381 GLbitfield flags
, GLuint64 timeout
)
383 struct brw_context
*brw
= brw_context(ctx
);
384 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
386 if (brw_fence_client_wait(brw
, &sync
->fence
, timeout
))
387 sync
->gl
.StatusFlag
= 1;
391 brw_gl_server_wait_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
,
392 GLbitfield flags
, GLuint64 timeout
)
394 struct brw_context
*brw
= brw_context(ctx
);
395 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
397 brw_fence_server_wait(brw
, &sync
->fence
);
401 brw_gl_check_sync(struct gl_context
*ctx
, struct gl_sync_object
*_sync
)
403 struct brw_gl_sync
*sync
= (struct brw_gl_sync
*) _sync
;
405 if (brw_fence_has_completed(&sync
->fence
))
406 sync
->gl
.StatusFlag
= 1;
410 brw_init_syncobj_functions(struct dd_function_table
*functions
)
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
;
421 brw_dri_create_fence(__DRIcontext
*ctx
)
423 struct brw_context
*brw
= ctx
->driverPrivate
;
424 struct brw_fence
*fence
;
426 fence
= calloc(1, sizeof(*fence
));
430 brw_fence_init(brw
, fence
, BRW_FENCE_TYPE_BO_WAIT
);
432 if (!brw_fence_insert_locked(brw
, fence
)) {
433 brw_fence_finish(fence
);
442 brw_dri_destroy_fence(__DRIscreen
*dri_screen
, void *_fence
)
444 struct brw_fence
*fence
= _fence
;
446 brw_fence_finish(fence
);
451 brw_dri_client_wait_sync(__DRIcontext
*ctx
, void *_fence
, unsigned flags
,
454 struct brw_fence
*fence
= _fence
;
456 return brw_fence_client_wait(fence
->brw
, fence
, timeout
);
460 brw_dri_server_wait_sync(__DRIcontext
*ctx
, void *_fence
, unsigned flags
)
462 struct brw_fence
*fence
= _fence
;
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.
470 brw_fence_server_wait(fence
->brw
, fence
);
474 brw_dri_get_capabilities(__DRIscreen
*dri_screen
)
476 struct intel_screen
*screen
= dri_screen
->driverPrivate
;
479 if (screen
->has_exec_fence
)
480 caps
|= __DRI_FENCE_CAP_NATIVE_FD
;
486 brw_dri_create_fence_fd(__DRIcontext
*dri_ctx
, int fd
)
488 struct brw_context
*brw
= dri_ctx
->driverPrivate
;
489 struct brw_fence
*fence
;
491 assert(brw
->screen
->has_exec_fence
);
493 fence
= calloc(1, sizeof(*fence
));
497 brw_fence_init(brw
, fence
, BRW_FENCE_TYPE_SYNC_FD
);
500 /* Create an out-fence fd */
501 if (!brw_fence_insert_locked(brw
, fence
))
504 /* Import the sync fd as an in-fence. */
505 fence
->sync_fd
= dup(fd
);
508 assert(fence
->sync_fd
!= -1);
513 brw_fence_finish(fence
);
519 brw_dri_get_fence_fd_locked(struct brw_fence
*fence
)
521 assert(fence
->type
== BRW_FENCE_TYPE_SYNC_FD
);
522 return dup(fence
->sync_fd
);
526 brw_dri_get_fence_fd(__DRIscreen
*dri_screen
, void *_fence
)
528 struct brw_fence
*fence
= _fence
;
531 mtx_lock(&fence
->mutex
);
532 fd
= brw_dri_get_fence_fd_locked(fence
);
533 mtx_unlock(&fence
->mutex
);
538 const __DRI2fenceExtension intelFenceExtension
= {
539 .base
= { __DRI2_FENCE
, 2 },
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
,