• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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