1// 2// Copyright (c) 2020 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6// SyncMtl: 7// Defines the class interface for SyncMtl, implementing SyncImpl. 8// 9 10#include "libANGLE/renderer/metal/SyncMtl.h" 11 12#include <chrono> 13 14#include "common/debug.h" 15#include "libANGLE/Context.h" 16#include "libANGLE/Display.h" 17#include "libANGLE/renderer/metal/ContextMtl.h" 18#include "libANGLE/renderer/metal/DisplayMtl.h" 19 20namespace rx 21{ 22namespace mtl 23{ 24 25static uint64_t makeSignalValue(EGLAttrib highPart, EGLAttrib lowPart) 26{ 27 return (static_cast<uint64_t>(highPart & 0xFFFFFFFF) << 32) | 28 (static_cast<uint64_t>(lowPart & 0xFFFFFFFF)); 29} 30 31// SharedEvent is only available on iOS 12.0+ or mac 10.14+ 32#if ANGLE_MTL_EVENT_AVAILABLE 33Sync::Sync() {} 34Sync::~Sync() {} 35 36void Sync::onDestroy() 37{ 38 mMetalSharedEvent = nil; 39 mCv = nullptr; 40 mLock = nullptr; 41} 42 43angle::Result Sync::initialize(ContextMtl *contextMtl, 44 id<MTLSharedEvent> sharedEvent, 45 Optional<uint64_t> signalValue) 46{ 47 ANGLE_MTL_OBJC_SCOPE 48 { 49 if (sharedEvent) 50 { 51 mMetalSharedEvent.retainAssign(sharedEvent); 52 } 53 else 54 { 55 mMetalSharedEvent = contextMtl->getMetalDevice().newSharedEvent(); 56 } 57 } 58 59 auto signaledValue = mMetalSharedEvent.get().signaledValue; 60 mSignalValue = signalValue.valid() ? signalValue.value() : signaledValue + 1; 61 62 mCv.reset(new std::condition_variable()); 63 mLock.reset(new std::mutex()); 64 return angle::Result::Continue; 65} 66 67angle::Result Sync::set(ContextMtl *contextMtl, 68 GLenum condition, 69 GLbitfield flags, 70 id<MTLSharedEvent> sharedEvent, 71 Optional<uint64_t> signalValue) 72{ 73 if (!mMetalSharedEvent) 74 { 75 ANGLE_TRY(initialize(contextMtl, sharedEvent, signalValue)); 76 } 77 ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE || 78 condition == EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE); 79 ASSERT(flags == 0); 80 81 if (condition == GL_SYNC_GPU_COMMANDS_COMPLETE) 82 { 83 contextMtl->queueEventSignal(mMetalSharedEvent, mSignalValue); 84 } 85 86 return angle::Result::Continue; 87} 88 89angle::Result Sync::clientWait(ContextMtl *contextMtl, 90 bool flushCommands, 91 uint64_t timeout, 92 GLenum *outResult) 93{ 94 std::unique_lock<std::mutex> lg(*mLock); 95 if (mMetalSharedEvent.get().signaledValue >= mSignalValue) 96 { 97 *outResult = GL_ALREADY_SIGNALED; 98 return angle::Result::Continue; 99 } 100 if (flushCommands) 101 { 102 contextMtl->flushCommandBuffer(mtl::WaitUntilScheduled); 103 } 104 105 if (timeout == 0) 106 { 107 *outResult = GL_TIMEOUT_EXPIRED; 108 109 return angle::Result::Continue; 110 } 111 112 // Create references to mutex and condition variable since they might be released in 113 // onDestroy(), but the callback might still not be fired yet. 114 std::shared_ptr<std::condition_variable> cvRef = mCv; 115 std::shared_ptr<std::mutex> lockRef = mLock; 116 AutoObjCObj<MTLSharedEventListener> eventListener = 117 contextMtl->getDisplay()->getOrCreateSharedEventListener(); 118 [mMetalSharedEvent.get() notifyListener:eventListener 119 atValue:mSignalValue 120 block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) { 121 std::unique_lock<std::mutex> localLock(*lockRef); 122 cvRef->notify_one(); 123 }]; 124 125 if (!mCv->wait_for(lg, std::chrono::nanoseconds(timeout), 126 [this] { return mMetalSharedEvent.get().signaledValue >= mSignalValue; })) 127 { 128 *outResult = GL_TIMEOUT_EXPIRED; 129 return angle::Result::Incomplete; 130 } 131 132 ASSERT(mMetalSharedEvent.get().signaledValue >= mSignalValue); 133 *outResult = GL_CONDITION_SATISFIED; 134 135 return angle::Result::Continue; 136} 137void Sync::serverWait(ContextMtl *contextMtl) 138{ 139 contextMtl->serverWaitEvent(mMetalSharedEvent, mSignalValue); 140} 141angle::Result Sync::getStatus(bool *signaled) 142{ 143 *signaled = mMetalSharedEvent.get().signaledValue >= mSignalValue; 144 return angle::Result::Continue; 145} 146 147void *Sync::copySharedEvent() const 148{ 149 mtl::SharedEventRef copySharedEvent = mMetalSharedEvent; 150 return reinterpret_cast<void *>(copySharedEvent.leakObject()); 151} 152#endif // #if defined(__IPHONE_12_0) || defined(__MAC_10_14) 153} // namespace mtl 154 155// FenceNVMtl implementation 156FenceNVMtl::FenceNVMtl() : FenceNVImpl() {} 157 158FenceNVMtl::~FenceNVMtl() {} 159 160void FenceNVMtl::onDestroy(const gl::Context *context) 161{ 162 mSync.onDestroy(); 163} 164 165angle::Result FenceNVMtl::set(const gl::Context *context, GLenum condition) 166{ 167 ASSERT(condition == GL_ALL_COMPLETED_NV); 168 ContextMtl *contextMtl = mtl::GetImpl(context); 169 return mSync.set(contextMtl, GL_SYNC_GPU_COMMANDS_COMPLETE, 0, nil, Optional<uint64_t>{}); 170} 171 172angle::Result FenceNVMtl::test(const gl::Context *context, GLboolean *outFinished) 173{ 174 bool signaled = false; 175 ANGLE_TRY(mSync.getStatus(&signaled)); 176 177 *outFinished = signaled ? GL_TRUE : GL_FALSE; 178 return angle::Result::Continue; 179} 180 181angle::Result FenceNVMtl::finish(const gl::Context *context) 182{ 183 ContextMtl *contextMtl = mtl::GetImpl(context); 184 uint64_t timeout = 1000000000ul; 185 GLenum result; 186 do 187 { 188 ANGLE_TRY(mSync.clientWait(contextMtl, true, timeout, &result)); 189 } while (result == GL_TIMEOUT_EXPIRED); 190 191 if (result == GL_WAIT_FAILED) 192 { 193 UNREACHABLE(); 194 return angle::Result::Stop; 195 } 196 197 return angle::Result::Continue; 198} 199 200// SyncMtl implementation 201SyncMtl::SyncMtl() : SyncImpl() {} 202 203SyncMtl::~SyncMtl() {} 204 205void SyncMtl::onDestroy(const gl::Context *context) 206{ 207 mSync.onDestroy(); 208} 209 210angle::Result SyncMtl::set(const gl::Context *context, GLenum condition, GLbitfield flags) 211{ 212 ContextMtl *contextMtl = mtl::GetImpl(context); 213 return mSync.set(contextMtl, condition, flags, nil, Optional<uint64_t>{}); 214} 215 216angle::Result SyncMtl::clientWait(const gl::Context *context, 217 GLbitfield flags, 218 GLuint64 timeout, 219 GLenum *outResult) 220{ 221 ContextMtl *contextMtl = mtl::GetImpl(context); 222 223 ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0); 224 225 bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0; 226 227 return mSync.clientWait(contextMtl, flush, timeout, outResult); 228} 229 230angle::Result SyncMtl::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout) 231{ 232 ASSERT(flags == 0); 233 ASSERT(timeout == GL_TIMEOUT_IGNORED); 234 235 ContextMtl *contextMtl = mtl::GetImpl(context); 236 mSync.serverWait(contextMtl); 237 return angle::Result::Continue; 238} 239 240angle::Result SyncMtl::getStatus(const gl::Context *context, GLint *outResult) 241{ 242 bool signaled = false; 243 ANGLE_TRY(mSync.getStatus(&signaled)); 244 245 *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED; 246 return angle::Result::Continue; 247} 248 249// EGLSyncMtl implementation 250EGLSyncMtl::EGLSyncMtl(const egl::AttributeMap &attribs) : EGLSyncImpl() 251{ 252 mSharedEvent = (__bridge id<MTLSharedEvent>)reinterpret_cast<void *>( 253 attribs.get(EGL_SYNC_METAL_SHARED_EVENT_OBJECT_ANGLE, 0)); 254 255 if (attribs.contains(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE) || 256 attribs.contains(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE)) 257 { 258 mSignalValue = 259 mtl::makeSignalValue(attribs.get(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE, 0), 260 attribs.get(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE, 0)); 261 } 262 263 // Translate conditions that aren't EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE to 264 // GL_SYNC_GPU_COMMANDS_COMPLETE. This is to translate EGL_SYNC_PRIOR_COMMANDS_COMPLETE, from 265 // the EGLSync layer, to the GL equivalent used in mtl::Sync. 266 mCondition = 267 attribs.getAsInt(EGL_SYNC_CONDITION, 0) == EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE 268 ? EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE 269 : GL_SYNC_GPU_COMMANDS_COMPLETE; 270} 271 272EGLSyncMtl::~EGLSyncMtl() {} 273 274void EGLSyncMtl::onDestroy(const egl::Display *display) 275{ 276 mSync.onDestroy(); 277} 278 279egl::Error EGLSyncMtl::initialize(const egl::Display *display, 280 const gl::Context *context, 281 EGLenum type) 282{ 283 ASSERT(context != nullptr); 284 mType = type; 285 286 ContextMtl *contextMtl = mtl::GetImpl(context); 287 switch (type) 288 { 289 case EGL_SYNC_FENCE_KHR: 290 ASSERT(mSharedEvent == nil); 291 ASSERT(!mSignalValue.valid()); 292 break; 293 case EGL_SYNC_METAL_SHARED_EVENT_ANGLE: 294 break; 295 default: 296 UNREACHABLE(); 297 return egl::Error(EGL_BAD_ALLOC); 298 } 299 300 if (IsError(mSync.set(contextMtl, mCondition, 0, mSharedEvent, mSignalValue))) 301 { 302 return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object"); 303 } 304 305 return egl::NoError(); 306} 307 308egl::Error EGLSyncMtl::clientWait(const egl::Display *display, 309 const gl::Context *context, 310 EGLint flags, 311 EGLTime timeout, 312 EGLint *outResult) 313{ 314 ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0); 315 316 bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0; 317 GLenum result; 318 ContextMtl *contextMtl = mtl::GetImpl(context); 319 if (IsError(mSync.clientWait(contextMtl, flush, static_cast<uint64_t>(timeout), &result))) 320 { 321 return egl::Error(EGL_BAD_ALLOC); 322 } 323 324 switch (result) 325 { 326 case GL_ALREADY_SIGNALED: 327 // fall through. EGL doesn't differentiate between event being already set, or set 328 // before timeout. 329 case GL_CONDITION_SATISFIED: 330 *outResult = EGL_CONDITION_SATISFIED_KHR; 331 return egl::NoError(); 332 333 case GL_TIMEOUT_EXPIRED: 334 *outResult = EGL_TIMEOUT_EXPIRED_KHR; 335 return egl::NoError(); 336 337 default: 338 UNREACHABLE(); 339 *outResult = EGL_FALSE; 340 return egl::Error(EGL_BAD_ALLOC); 341 } 342} 343 344egl::Error EGLSyncMtl::serverWait(const egl::Display *display, 345 const gl::Context *context, 346 EGLint flags) 347{ 348 // Server wait requires a valid bound context. 349 ASSERT(context); 350 351 // No flags are currently implemented. 352 ASSERT(flags == 0); 353 354 ContextMtl *contextMtl = mtl::GetImpl(context); 355 mSync.serverWait(contextMtl); 356 return egl::NoError(); 357} 358 359egl::Error EGLSyncMtl::getStatus(const egl::Display *display, EGLint *outStatus) 360{ 361 bool signaled = false; 362 if (IsError(mSync.getStatus(&signaled))) 363 { 364 return egl::Error(EGL_BAD_ALLOC); 365 } 366 367 *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR; 368 return egl::NoError(); 369} 370 371egl::Error EGLSyncMtl::copyMetalSharedEventANGLE(const egl::Display *display, void **result) const 372{ 373 switch (mType) 374 { 375 case EGL_SYNC_METAL_SHARED_EVENT_ANGLE: 376 *result = mSync.copySharedEvent(); 377 return egl::NoError(); 378 379 default: 380 return egl::EglBadDisplay(); 381 } 382} 383 384egl::Error EGLSyncMtl::dupNativeFenceFD(const egl::Display *display, EGLint *result) const 385{ 386 UNREACHABLE(); 387 return egl::EglBadDisplay(); 388} 389 390} // namespace rx 391