diff --git a/src/platform/mac/PAGView.m b/src/platform/mac/PAGView.mm similarity index 94% rename from src/platform/mac/PAGView.m rename to src/platform/mac/PAGView.mm index dc6a48d3e1..a3e55f417f 100644 --- a/src/platform/mac/PAGView.m +++ b/src/platform/mac/PAGView.mm @@ -19,6 +19,7 @@ #import "PAGView.h" #import "PAGPlayer.h" #import "platform/cocoa/private/PAGAnimator.h" +#import "platform/mac/private/GPUDrawable.h" @interface PAGView () @end @@ -54,6 +55,10 @@ - (void)initPAG { // The animator must be set to sync mode. Otherwise, the internal surface in the PAGSurface could // not be created. [animator setSync:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onAsyncSurfacePrepared:) + name:pag::kAsyncSurfacePreparedNotification + object:self]; } - (void)dealloc { @@ -65,6 +70,7 @@ - (void)dealloc { [filePath release]; [listeners release]; [listenerLock release]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } @@ -351,4 +357,8 @@ - (CGRect)getBounds:(PAGLayer*)pagLayer { } return CGRectNull; } + +- (void)onAsyncSurfacePrepared:(NSNotification*)notification { + [animator update]; +} @end diff --git a/src/platform/mac/private/GPUDrawable.h b/src/platform/mac/private/GPUDrawable.h index 26bf188e58..fa8ccc48c7 100644 --- a/src/platform/mac/private/GPUDrawable.h +++ b/src/platform/mac/private/GPUDrawable.h @@ -22,6 +22,9 @@ #include "tgfx/gpu/opengl/cgl/CGLWindow.h" namespace pag { + +extern NSString* const kAsyncSurfacePreparedNotification; + class GPUDrawable : public Drawable { public: static std::shared_ptr FromView(NSView* view); @@ -46,11 +49,15 @@ class GPUDrawable : public Drawable { void onFreeSurface() override; private: + std::weak_ptr weakThis; int _width = 0; int _height = 0; NSView* view = nil; std::shared_ptr window = nullptr; + std::atomic bufferPreparing = false; explicit GPUDrawable(NSView* view); + + void tryCreateSurface(); }; } // namespace pag diff --git a/src/platform/mac/private/GPUDrawable.mm b/src/platform/mac/private/GPUDrawable.mm index 1497042582..e4342148bd 100644 --- a/src/platform/mac/private/GPUDrawable.mm +++ b/src/platform/mac/private/GPUDrawable.mm @@ -21,11 +21,16 @@ #include "tgfx/gpu/opengl/cgl/CGLWindow.h" namespace pag { +NSString* const kAsyncSurfacePreparedNotification = @"io.pag.AsyncSurfacePrepared"; + std::shared_ptr GPUDrawable::FromView(NSView* view) { if (view == nil) { return nullptr; } - return std::shared_ptr(new GPUDrawable(view)); + auto drawable = std::shared_ptr(new GPUDrawable(view)); + drawable->weakThis = drawable; + drawable->tryCreateSurface(); + return drawable; } GPUDrawable::GPUDrawable(NSView* view) : view(view) { @@ -33,6 +38,25 @@ updateSize(); } +void GPUDrawable::tryCreateSurface() { + // try to create the surface if the view is ready, because CGLWindow needs to access the NSView + // geometry on the main thread. + if (!NSThread.isMainThread || _width <= 0 || _height <= 0) { + return; + } + if (window == nullptr) { + window = tgfx::CGLWindow::MakeFrom(view); + } + if (window != nullptr) { + auto device = window->getDevice(); + auto context = device->lockContext(); + if (context != nullptr) { + surface = tgfx::Surface::MakeFrom(context, window); + device->unlock(); + } + } +} + void GPUDrawable::updateSize() { CGSize size = [view convertSizeToBacking:view.bounds.size]; _width = static_cast(roundf(size.width)); @@ -50,10 +74,40 @@ } std::shared_ptr GPUDrawable::onCreateSurface(tgfx::Context* context) { - if (window == nullptr) { + if (window == nullptr || bufferPreparing) { + return nullptr; + } + // https://github.com/Tencent/libpag/issues/1870 + // Creating a surface in a non-main thread may lead to a crash because CGLWindow needs to access + // the NSView geometry on the main thread. + if (NSThread.isMainThread) { + return tgfx::Surface::MakeFrom(context, window); + } + bufferPreparing = true; + auto strongThis = weakThis.lock(); + if (strongThis == nullptr) { + bufferPreparing = false; return nullptr; } - return tgfx::Surface::MakeFrom(context, window); + [view retain]; + dispatch_async(dispatch_get_main_queue(), ^{ + auto context = strongThis->window->getDevice()->lockContext(); + if (context == nullptr) { + strongThis->bufferPreparing = false; + [strongThis->view release]; + return; + } + strongThis->surface = tgfx::Surface::MakeFrom(context, strongThis->window); + strongThis->bufferPreparing = false; + strongThis->window->getDevice()->unlock(); + if (strongThis->surface) { + [[NSNotificationCenter defaultCenter] postNotificationName:kAsyncSurfacePreparedNotification + object:strongThis->view + userInfo:nil]; + } + [strongThis->view release]; + }); + return nullptr; } void GPUDrawable::onFreeSurface() {