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 (at) 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 "main/imports.h" 42 43 #include "brw_context.h" 44 #include "intel_batchbuffer.h" 45 46 struct brw_fence { 47 struct brw_context *brw; 48 /** The fence waits for completion of this batch. */ 49 drm_intel_bo *batch_bo; 50 51 mtx_t mutex; 52 bool signalled; 53 }; 54 55 struct brw_gl_sync { 56 struct gl_sync_object gl; 57 struct brw_fence fence; 58 }; 59 60 static void 61 brw_fence_init(struct brw_context *brw, struct brw_fence *fence) 62 { 63 fence->brw = brw; 64 fence->batch_bo = NULL; 65 mtx_init(&fence->mutex, mtx_plain); 66 } 67 68 static void 69 brw_fence_finish(struct brw_fence *fence) 70 { 71 if (fence->batch_bo) 72 drm_intel_bo_unreference(fence->batch_bo); 73 74 mtx_destroy(&fence->mutex); 75 } 76 77 static void 78 brw_fence_insert(struct brw_context *brw, struct brw_fence *fence) 79 { 80 assert(!fence->batch_bo); 81 assert(!fence->signalled); 82 83 brw_emit_mi_flush(brw); 84 fence->batch_bo = brw->batch.bo; 85 drm_intel_bo_reference(fence->batch_bo); 86 intel_batchbuffer_flush(brw); 87 } 88 89 static bool 90 brw_fence_has_completed_locked(struct brw_fence *fence) 91 { 92 if (fence->signalled) 93 return true; 94 95 if (fence->batch_bo && !drm_intel_bo_busy(fence->batch_bo)) { 96 drm_intel_bo_unreference(fence->batch_bo); 97 fence->batch_bo = NULL; 98 fence->signalled = true; 99 return true; 100 } 101 102 return false; 103 } 104 105 static bool 106 brw_fence_has_completed(struct brw_fence *fence) 107 { 108 bool ret; 109 110 mtx_lock(&fence->mutex); 111 ret = brw_fence_has_completed_locked(fence); 112 mtx_unlock(&fence->mutex); 113 114 return ret; 115 } 116 117 static bool 118 brw_fence_client_wait_locked(struct brw_context *brw, struct brw_fence *fence, 119 uint64_t timeout) 120 { 121 if (fence->signalled) 122 return true; 123 124 assert(fence->batch_bo); 125 126 /* DRM_IOCTL_I915_GEM_WAIT uses a signed 64 bit timeout and returns 127 * immediately for timeouts <= 0. The best we can do is to clamp the 128 * timeout to INT64_MAX. This limits the maximum timeout from 584 years to 129 * 292 years - likely not a big deal. 130 */ 131 if (timeout > INT64_MAX) 132 timeout = INT64_MAX; 133 134 if (drm_intel_gem_bo_wait(fence->batch_bo, timeout) != 0) 135 return false; 136 137 fence->signalled = true; 138 drm_intel_bo_unreference(fence->batch_bo); 139 fence->batch_bo = NULL; 140 141 return true; 142 } 143 144 /** 145 * Return true if the function successfully signals or has already signalled. 146 * (This matches the behavior expected from __DRI2fence::client_wait_sync). 147 */ 148 static bool 149 brw_fence_client_wait(struct brw_context *brw, struct brw_fence *fence, 150 uint64_t timeout) 151 { 152 bool ret; 153 154 mtx_lock(&fence->mutex); 155 ret = brw_fence_client_wait_locked(brw, fence, timeout); 156 mtx_unlock(&fence->mutex); 157 158 return ret; 159 } 160 161 static void 162 brw_fence_server_wait(struct brw_context *brw, struct brw_fence *fence) 163 { 164 /* We have nothing to do for WaitSync. Our GL command stream is sequential, 165 * so given that the sync object has already flushed the batchbuffer, any 166 * batchbuffers coming after this waitsync will naturally not occur until 167 * the previous one is done. 168 */ 169 } 170 171 static struct gl_sync_object * 172 brw_gl_new_sync(struct gl_context *ctx, GLuint id) 173 { 174 struct brw_gl_sync *sync; 175 176 sync = calloc(1, sizeof(*sync)); 177 if (!sync) 178 return NULL; 179 180 return &sync->gl; 181 } 182 183 static void 184 brw_gl_delete_sync(struct gl_context *ctx, struct gl_sync_object *_sync) 185 { 186 struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync; 187 188 brw_fence_finish(&sync->fence); 189 free(sync); 190 } 191 192 static void 193 brw_gl_fence_sync(struct gl_context *ctx, struct gl_sync_object *_sync, 194 GLenum condition, GLbitfield flags) 195 { 196 struct brw_context *brw = brw_context(ctx); 197 struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync; 198 199 brw_fence_init(brw, &sync->fence); 200 brw_fence_insert(brw, &sync->fence); 201 } 202 203 static void 204 brw_gl_client_wait_sync(struct gl_context *ctx, struct gl_sync_object *_sync, 205 GLbitfield flags, GLuint64 timeout) 206 { 207 struct brw_context *brw = brw_context(ctx); 208 struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync; 209 210 if (brw_fence_client_wait(brw, &sync->fence, timeout)) 211 sync->gl.StatusFlag = 1; 212 } 213 214 static void 215 brw_gl_server_wait_sync(struct gl_context *ctx, struct gl_sync_object *_sync, 216 GLbitfield flags, GLuint64 timeout) 217 { 218 struct brw_context *brw = brw_context(ctx); 219 struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync; 220 221 brw_fence_server_wait(brw, &sync->fence); 222 } 223 224 static void 225 brw_gl_check_sync(struct gl_context *ctx, struct gl_sync_object *_sync) 226 { 227 struct brw_gl_sync *sync = (struct brw_gl_sync *) _sync; 228 229 if (brw_fence_has_completed(&sync->fence)) 230 sync->gl.StatusFlag = 1; 231 } 232 233 void 234 brw_init_syncobj_functions(struct dd_function_table *functions) 235 { 236 functions->NewSyncObject = brw_gl_new_sync; 237 functions->DeleteSyncObject = brw_gl_delete_sync; 238 functions->FenceSync = brw_gl_fence_sync; 239 functions->CheckSync = brw_gl_check_sync; 240 functions->ClientWaitSync = brw_gl_client_wait_sync; 241 functions->ServerWaitSync = brw_gl_server_wait_sync; 242 } 243 244 static void * 245 brw_dri_create_fence(__DRIcontext *ctx) 246 { 247 struct brw_context *brw = ctx->driverPrivate; 248 struct brw_fence *fence; 249 250 fence = calloc(1, sizeof(*fence)); 251 if (!fence) 252 return NULL; 253 254 brw_fence_init(brw, fence); 255 brw_fence_insert(brw, fence); 256 257 return fence; 258 } 259 260 static void 261 brw_dri_destroy_fence(__DRIscreen *dri_screen, void *_fence) 262 { 263 struct brw_fence *fence = _fence; 264 265 brw_fence_finish(fence); 266 free(fence); 267 } 268 269 static GLboolean 270 brw_dri_client_wait_sync(__DRIcontext *ctx, void *_fence, unsigned flags, 271 uint64_t timeout) 272 { 273 struct brw_fence *fence = _fence; 274 275 return brw_fence_client_wait(fence->brw, fence, timeout); 276 } 277 278 static void 279 brw_dri_server_wait_sync(__DRIcontext *ctx, void *_fence, unsigned flags) 280 { 281 struct brw_fence *fence = _fence; 282 283 /* We might be called here with a NULL fence as a result of WaitSyncKHR 284 * on a EGL_KHR_reusable_sync fence. Nothing to do here in such case. 285 */ 286 if (!fence) 287 return; 288 289 brw_fence_server_wait(fence->brw, fence); 290 } 291 292 const __DRI2fenceExtension intelFenceExtension = { 293 .base = { __DRI2_FENCE, 1 }, 294 295 .create_fence = brw_dri_create_fence, 296 .destroy_fence = brw_dri_destroy_fence, 297 .client_wait_sync = brw_dri_client_wait_sync, 298 .server_wait_sync = brw_dri_server_wait_sync, 299 .get_fence_from_cl_event = NULL, 300 }; 301