Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/platform/mac/PAGView.m → src/platform/mac/PAGView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import "PAGView.h"
#import "PAGPlayer.h"
#import "platform/cocoa/private/PAGAnimator.h"
#import "platform/mac/private/GPUDrawable.h"

@interface PAGView () <PAGAnimatorUpdater, PAGAnimatorListener>
@end
Expand Down Expand Up @@ -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 {
Expand All @@ -65,6 +70,7 @@ - (void)dealloc {
[filePath release];
[listeners release];
[listenerLock release];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}

Expand Down Expand Up @@ -351,4 +357,8 @@ - (CGRect)getBounds:(PAGLayer*)pagLayer {
}
return CGRectNull;
}

- (void)onAsyncSurfacePrepared:(NSNotification*)notification {
[animator update];
}
@end
7 changes: 7 additions & 0 deletions src/platform/mac/private/GPUDrawable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<GPUDrawable> FromView(NSView* view);
Expand All @@ -46,11 +49,15 @@ class GPUDrawable : public Drawable {
void onFreeSurface() override;

private:
std::weak_ptr<GPUDrawable> weakThis;
int _width = 0;
int _height = 0;
NSView* view = nil;
std::shared_ptr<tgfx::CGLWindow> window = nullptr;
std::atomic<bool> bufferPreparing = false;

explicit GPUDrawable(NSView* view);

void tryCreateSurface();
};
} // namespace pag
60 changes: 57 additions & 3 deletions src/platform/mac/private/GPUDrawable.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,42 @@
#include "tgfx/gpu/opengl/cgl/CGLWindow.h"

namespace pag {
NSString* const kAsyncSurfacePreparedNotification = @"io.pag.AsyncSurfacePrepared";

std::shared_ptr<GPUDrawable> GPUDrawable::FromView(NSView* view) {
if (view == nil) {
return nullptr;
}
return std::shared_ptr<GPUDrawable>(new GPUDrawable(view));
auto drawable = std::shared_ptr<GPUDrawable>(new GPUDrawable(view));
drawable->weakThis = drawable;
drawable->tryCreateSurface();
return drawable;
}

GPUDrawable::GPUDrawable(NSView* view) : view(view) {
// do not retain view here, otherwise it can cause circular reference.
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<int>(roundf(size.width));
Expand All @@ -50,10 +74,40 @@
}

std::shared_ptr<tgfx::Surface> 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() {
Expand Down
Loading