1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// 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>();
}
GLDisplayEGL::~GLDisplayEGL() = default;
bool GLDisplayEGL::CreateMetalSharedEvent(id<MTLSharedEvent>* shared_event_out,
uint64_t* signal_value_out) {
CHECK(ext->b_EGL_ANGLE_metal_shared_event_sync);
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) {
CHECK(objc_storage_);
if (objc_storage_->metal_shared_event.get() == shared_event) {
// If the event is owned by this display, skip the wait.
// Currently ANGLE/Metal is only single threaded. There is no need to issue
// a GPU wait for an event that was signaled by the same display. Because
// the works before and after the signal belong to the same metal queue
// hence they are already synchronized with each other implicitly.
CHECK_GE(objc_storage_->metal_signaled_value, signal_value);
return;
}
CHECK(ext->b_EGL_ANGLE_metal_shared_event_sync);
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,
};
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::InitMetalSharedEventStorage() {
objc_storage_ = std::make_unique<ObjCStorage>();
}
void GLDisplayEGL::CleanupMetalSharedEventStorage() {
objc_storage_.reset();
}
} // namespace gl
|