| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/gl/gl_display.h" |
| |
| #import <Metal/Metal.h> |
| |
| #include "base/apple/scoped_nsobject.h" |
| #include "ui/gl/gl_bindings.h" |
| |
| // From ANGLE's egl/eglext_angle.h. |
| #ifndef EGL_ANGLE_metal_shared_event_sync |
| #define EGL_ANGLE_metal_hared_event_sync 1 |
| #define EGL_SYNC_METAL_SHARED_EVENT_ANGLE 0x34D8 |
| #define EGL_SYNC_METAL_SHARED_EVENT_OBJECT_ANGLE 0x34D9 |
| #define EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE 0x34DA |
| #define EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE 0x34DB |
| #define EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE 0x34DC |
| #endif |
| |
| namespace gl { |
| |
| struct GLDisplayEGL::ObjCStorage { |
| base::apple::scoped_nsprotocol<id<MTLSharedEvent>> metal_shared_event; |
| uint64_t metal_signaled_value = 0; |
| }; |
| |
| // Because on Apple platforms there is a member variable of a type (ObjCStorage) |
| // that is defined in this file, the constructor/destructor also have to be |
| // here. If making changes to this copy, be sure to adjust the other copy in |
| // gl_display.cc. |
| GLDisplayEGL::GLDisplayEGL(uint64_t system_device_id, DisplayKey display_key) |
| : GLDisplay(system_device_id, display_key, EGL) { |
| ext = std::make_unique<DisplayExtensionsEGL>(); |
| |
| // -- BEGIN difference from copy in gl_display.cc -- |
| objc_storage_ = std::make_unique<ObjCStorage>(); |
| // -- END difference from copy in gl_display.cc -- |
| } |
| |
| GLDisplayEGL::~GLDisplayEGL() = default; |
| |
| bool GLDisplayEGL::IsANGLEMetalSharedEventSyncSupported() { |
| return this->ext->b_EGL_ANGLE_metal_shared_event_sync; |
| } |
| |
| bool GLDisplayEGL::CreateMetalSharedEvent(id<MTLSharedEvent>* shared_event_out, |
| uint64_t* signal_value_out) { |
| DCHECK(g_driver_egl.fn.eglCreateSyncFn); |
| DCHECK(g_driver_egl.fn.eglGetSyncAttribFn); |
| if (!objc_storage_->metal_shared_event) { |
| std::vector<EGLAttrib> attribs; |
| attribs.push_back(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE); |
| attribs.push_back(0); |
| attribs.push_back(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE); |
| attribs.push_back(0); |
| attribs.push_back(EGL_NONE); |
| EGLSync sync = |
| eglCreateSync(display_, EGL_SYNC_METAL_SHARED_EVENT_ANGLE, &attribs[0]); |
| if (!sync) |
| return false; |
| |
| // eglCopyMetalSharedEventANGLE returns an MTLSharedEvent object that has a |
| // retain count of 1. |
| objc_storage_->metal_shared_event.reset(static_cast<id<MTLSharedEvent>>( |
| eglCopyMetalSharedEventANGLE(display_, sync))); |
| |
| // The sync object is already enqueued for signaling in ANGLE's command |
| // stream. Since the MTLSharedEvent is already retained, it's safe to delete |
| // the sync object immediately. |
| eglDestroySync(display_, sync); |
| } |
| |
| // Create another sync object, perhaps redundantly the first time, |
| // but with our specified signal value. |
| ++objc_storage_->metal_signaled_value; |
| id<MTLSharedEvent> shared_event = objc_storage_->metal_shared_event.get(); |
| std::vector<EGLAttrib> attribs; |
| attribs.push_back(EGL_SYNC_METAL_SHARED_EVENT_OBJECT_ANGLE); |
| attribs.push_back( |
| static_cast<EGLAttrib>(reinterpret_cast<uintptr_t>(shared_event))); |
| attribs.push_back(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE); |
| attribs.push_back(objc_storage_->metal_signaled_value & 0xFFFFFFFF); |
| attribs.push_back(EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE); |
| attribs.push_back((objc_storage_->metal_signaled_value >> 32) & 0xFFFFFFFF); |
| attribs.push_back(EGL_NONE); |
| |
| EGLSync sync = |
| eglCreateSync(display_, EGL_SYNC_METAL_SHARED_EVENT_ANGLE, &attribs[0]); |
| if (!sync) |
| return false; |
| |
| // The sync object is already enqueued for signaling in ANGLE's command |
| // stream. Since the MTLSharedEvent is already retained, it's safe to delete |
| // the sync object immediately. |
| eglDestroySync(display_, sync); |
| |
| *shared_event_out = objc_storage_->metal_shared_event.get(); |
| *signal_value_out = objc_storage_->metal_signaled_value; |
| return true; |
| } |
| |
| void GLDisplayEGL::WaitForMetalSharedEvent(id<MTLSharedEvent> shared_event, |
| uint64_t signal_value) { |
| EGLAttrib attribs[] = { |
| // Pass the Metal shared event as an EGLAttrib. |
| EGL_SYNC_METAL_SHARED_EVENT_OBJECT_ANGLE, |
| static_cast<EGLAttrib>(reinterpret_cast<uintptr_t>(shared_event)), |
| // EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE is important as it requests |
| // ANGLE to create an EGL sync object from the Metal shared event, but NOT |
| // signal it to the specified value. The shared event is imported with |
| // that signal value. The next call to eglWaitSync enqueue's a GPU wait to |
| // wait for that value to be signaled by another command buffer. |
| EGL_SYNC_CONDITION, |
| EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE, |
| // Encode the signaled value in two EGLAttribs. |
| EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE, |
| EGLAttrib(signal_value & 0xFFFFFFFF), |
| EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE, |
| EGLAttrib((signal_value >> 32) & 0xFFFFFFFF), |
| EGL_NONE, |
| }; |
| |
| DCHECK(g_driver_egl.fn.eglCreateSyncFn); |
| EGLSync sync = |
| eglCreateSync(display_, EGL_SYNC_METAL_SHARED_EVENT_ANGLE, attribs); |
| EGLBoolean res = eglWaitSync(display_, sync, 0); |
| DCHECK(res == EGL_TRUE); |
| // The wait on the sync object has been enqueued already, so it's safe to |
| // destroy it now. |
| eglDestroySync(display_, sync); |
| } |
| |
| void GLDisplayEGL::CleanupMetalSharedEvent() { |
| objc_storage_.reset(); |
| } |
| |
| } // namespace gl |