JBR-6696: hardened MTLGC_DestroyMTLGraphicsConfig to ensure any aync callbacks are performed before deallocation, added CVDisplayLink checks + skip delayed callbacks, added traces and refactored code + improved drawable lifecycle + hardened appkit main thread usage + instrumentation to debug latency + fixed ThreadUtilities to observe all performOnMainThread usages + enable apple MainThreadChecker in DEBUG builds + fixed few MainThread violations (PrinterView init) (WIP)

This commit is contained in:
bourgesl
2024-02-12 23:55:28 +01:00
parent b304693028
commit cef9230256
16 changed files with 663 additions and 173 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -288,6 +288,73 @@ JLI_NotifyAWTLoaded()
static int (*main_fptr)(int argc, char **argv) = NULL;
void useMainThreadChecker() {
BOOL hasEnvMTC = (getenv("JAVA_AWT_MTC") != NULL);
/* always enable MTC in debug builds */
#ifndef DEBUG
/* check if MTC is enabled on product releases (env variable 'JAVA_AWT_MTC' is set) */
if (!hasEnvMTC) {
return;
}
#endif
/*
* See MTC config from https://bryce.co/main-thread-checker-configuration/
*/
setenv("MTC_VERBOSE", "0", 1);
setenv("MTC_LOG_REPORTS_TO_OS_LOG", "0", 1);
setenv("MTC_CALL_BREAKPOINT_SYMBOL", "0", 1);
// enable MTC_CRASH_ON_REPORT for testing (jtreg...):
#ifdef DEBUG
setenv("MTC_CRASH_ON_REPORT", "1", 1);
#else
/* check if the env variable 'JAVA_AWT_MTC_CRASH' is set */
if (getenv("JAVA_AWT_MTC_CRASH") != NULL) {
setenv("MTC_CRASH_ON_REPORT", "1", 1);
} else {
setenv("MTC_CRASH_ON_REPORT", "0", 1);
}
#endif
/* keep repeated violations */
setenv("MTC_MAX_HIT_COUNT", "99999", 1);
// disables Ignore flags to get more information:
setenv("MTC_IGNORE_DUPS_BY_THREAD_PC", "0", 1);
setenv("MTC_IGNORE_INLINE_CALLS", "0", 1);
setenv("MTC_IGNORE_DEALLOC", "1", 1);
setenv("MTC_IGNORE_RETAIN_RELEASE", "1", 1);
if (0) {
// Report also violations within apple frameworks (DEV ONLY):
setenv("MTC_SUPPRESS_SYSTEM_REPORTS", "0", 1);
} else {
// disabling internals violations:
setenv("MTC_SUPPRESS_SYSTEM_REPORTS", "1", 1);
}
if (0) {
// dump selector stats at exit (DEV ONLY):
setenv("MTC_PRINT_SELECTOR_STATS", "1", 1);
}
// TODO: get DL_PATH from env vars (no hard-coded path):
void *result = dlopen("/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib",
RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE | RTLD_FIRST
);
if (hasEnvMTC) {
if (result == NULL) {
printf("useMainThreadChecker: Failed loading libMainThreadChecker...\n");
} else {
printf("useMainThreadChecker: Loaded libMainThreadChecker...\n");
}
}
}
/*
* Unwrap the arguments and re-run main()
*/
@@ -334,6 +401,7 @@ static void MacOSXStartup(int argc, char *argv[]) {
// Thread already started?
static jboolean started = false;
if (started) {
useMainThreadChecker();
return;
}
started = true;

View File

@@ -2108,7 +2108,6 @@ JNI_COCOA_ENTER(env);
jobject platformWindow = (*env)->NewWeakGlobalRef(env, obj);
NSView *contentView = OBJC(contentViewPtr);
NSRect frameRect = NSMakeRect(x, y, w, h);
AWTWindow *owner = [OBJC(ownerPtr) delegate];
BOOL isIgnoreMouseEvents = NO;
GET_CPLATFORM_WINDOW_CLASS_RETURN(0);
@@ -2121,6 +2120,7 @@ JNI_COCOA_ENTER(env);
(*env)->DeleteLocalRef(env, awtWindow);
}
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
AWTWindow *owner = [OBJC(ownerPtr) delegate];
window = [[AWTWindow alloc] initWithPlatformWindow:platformWindow
ownerWindow:owner
@@ -2534,8 +2534,8 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowTi
JNI_COCOA_ENTER(env);
NSWindow *nsWindow = OBJC(windowPtr);
[nsWindow performSelectorOnMainThread:@selector(setTitle:)
withObject:JavaStringToNSString(env, jtitle)
[ThreadUtilities performOnMainThread:@selector(setTitle:) on:nsWindow
withObject:JavaStringToNSString(env, jtitle)
waitUntilDone:NO];
JNI_COCOA_EXIT(env);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -293,7 +293,6 @@ AWT_ASSERT_APPKIT_THREAD;
NSString *url = [[openURLEvent paramDescriptorForKeyword:keyDirectObject] stringValue];
//fprintf(stderr,"jm_handleOpenURL\n");
JNIEnv *env = [ThreadUtilities getJNIEnv];
jstring jURL = NSStringToJavaString(env, url);
GET_APPEVENTHANDLER_CLASS();
@@ -337,7 +336,6 @@ AWT_ASSERT_APPKIT_THREAD;
return;
}
//fprintf(stderr,"jm_handleOpenFile\n");
JNIEnv *env = [ThreadUtilities getJNIEnv];
// if these files were opened from a Spotlight query, try to get the search text from the current AppleEvent
@@ -364,7 +362,6 @@ AWT_ASSERT_APPKIT_THREAD;
AWT_ASSERT_APPKIT_THREAD;
if (!fHandlesDocumentTypes) return NSPrintingCancelled;
//fprintf(stderr,"jm_handlePrintFile\n");
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject jFileNamesArray = [self _createFilePathArrayFrom:fileNames withEnv:env];
GET_APPEVENTHANDLER_CLASS_RETURN(NSPrintingCancelled);
@@ -381,7 +378,6 @@ AWT_ASSERT_APPKIT_THREAD;
+ (void)_notifyJava:(jint)notificationType {
AWT_ASSERT_APPKIT_THREAD;
//fprintf(stderr,"jm_handleOpenApplication\n");
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_APPEVENTHANDLER_CLASS();
DECLARE_STATIC_METHOD(jm_handleNativeNotification, sjc_AppEventHandler, "handleNativeNotification", "(I)V");
@@ -401,7 +397,7 @@ AWT_ASSERT_APPKIT_THREAD;
// Open app handler, registered in -init
+ (void)_willFinishLaunching {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_OPEN_APP];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_OPEN_APP];
}
// ReOpen app handler
@@ -430,47 +426,47 @@ AWT_ASSERT_APPKIT_THREAD;
}
+ (void)_systemWillPowerOff {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SHUTDOWN];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SHUTDOWN];
}
+ (void)_appDidActivate {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ACTIVE_APP_GAINED];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ACTIVE_APP_GAINED];
}
+ (void)_appDidDeactivate {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ACTIVE_APP_LOST];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_ACTIVE_APP_LOST];
}
+ (void)_appDidHide {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_APP_HIDDEN];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_APP_HIDDEN];
}
+ (void)_appDidUnhide {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_APP_SHOWN];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_APP_SHOWN];
}
+ (void)_sessionDidActivate {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_USER_SESSION_ACTIVE];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_USER_SESSION_ACTIVE];
}
+ (void)_sessionDidDeactivate {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_USER_SESSION_INACTIVE];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_USER_SESSION_INACTIVE];
}
+ (void)_screenDidSleep {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_SLEEP];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_SLEEP];
}
+ (void)_screenDidWake {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_WAKE];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_WAKE];
}
+ (void)_systemDidSleep {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SYSTEM_SLEEP];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SYSTEM_SLEEP];
}
+ (void)_systemDidWake {
[self _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SYSTEM_WAKE];
[ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SYSTEM_WAKE];
}
+ (void)_registerForNotification:(NSNumber *)notificationTypeNum {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -593,7 +593,7 @@ static BOOL sNeedsEnter;
{
AWT_ASSERT_NOT_APPKIT_THREAD;
[self performSelectorOnMainThread:@selector(doDrag) withObject:nil waitUntilDone:YES];
[ThreadUtilities performOnMainThread:@selector(doDrag) on:self withObject:nil waitUntilDone:YES];
}
/******************************** BEGIN NSDraggingSource Interface ********************************/

View File

@@ -660,8 +660,14 @@ JNI_COCOA_ENTER(env);
jobject pageFormatArea = (*env)->CallObjectMethod(env, jthis, jm_getPageFormatArea, page); // AWT_THREADING Safe (!appKit)
CHECK_EXCEPTION();
PrinterView* printerView = [[PrinterView alloc] initWithFrame:JavaToNSRect(env, pageFormatArea) withEnv:env withPrinterJob:jthis];
[printerView setFirstPage:firstPage lastPage:lastPage];
NSRect rect = JavaToNSRect(env, pageFormatArea);
__block PrinterView *printerView = nil;
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
printerView = [[PrinterView alloc] initWithFrame:rect withEnv:env withPrinterJob:jthis];
[printerView setFirstPage:firstPage lastPage:lastPage];
}];
GET_NSPRINTINFO_METHOD_RETURN(NO)
NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr((*env)->CallLongMethod(env, jthis, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -109,6 +109,8 @@
- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib;
- (void)dealloc;
- (void)handleDisplayLink: (BOOL)enabled source:(const char*)src;
/**
* Resets the current clip state (disables both scissor and depth tests).
*/
@@ -245,6 +247,7 @@
- (void)commitCommandBuffer:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
- (void)startRedraw:(MTLLayer*)layer;
- (void)stopRedraw:(MTLLayer*)layer;
- (void)haltRedraw;
@end
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,6 +39,10 @@
// scenarios with multiple subsequent updates.
#define KEEP_ALIVE_COUNT 4
// Min interval between 2 display link callbacks (Main thread may be blocked)
// ~ 1ms (shorter than best monitor frame rate = 1000 hz)
#define KEEP_ALIVE_MIN_INTERVAL 1.0 / 1000.0
// Amount of blit operations per update to make sure that everything is
// rendered into the window drawable. It does not slow things down as we
// use separate command queue for blitting.
@@ -48,6 +52,15 @@ extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, MTLSDOps *mtlsdo);
extern BOOL isDisplaySyncEnabled();
extern BOOL MTLLayer_isExtraRedrawEnabled();
#define CHECK_CVLINK(op, cmd) \
{ \
CVReturn ret = (CVReturn) (cmd); \
if (ret != kCVReturnSuccess) { \
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, "CVDisplayLink[%s] Error: %d", \
op, ret); \
} \
}
static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
{{-1.0, 1.0}, {0.0, 0.0}},
{{1.0, 1.0}, {1.0, 0.0}},
@@ -122,6 +135,7 @@ MTLTransform* tempTransform = nil;
CVDisplayLinkRef _displayLink;
NSMutableSet* _layers;
int _displayLinkCount;
CFTimeInterval _lastRedrawTime;
MTLComposite * _composite;
MTLPaint * _paint;
@@ -145,6 +159,8 @@ MTLTransform* tempTransform = nil;
extern void initSamplers(id<MTLDevice> device);
- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib {
AWT_ASSERT_APPKIT_THREAD;
// Initialize ourselves
self = [super init];
if (self) {
// Initialization code here.
@@ -161,6 +177,8 @@ extern void initSamplers(id<MTLDevice> device);
return nil;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "initWithDevice: created MTLContext[%p]", self);
texturePool = [[MTLTexturePool alloc] initWithDevice:device];
vertexBuffer = [device newBufferWithBytes:verts
@@ -184,11 +202,35 @@ extern void initSamplers(id<MTLDevice> device);
blitCommandQueue = [device newCommandQueue];
_tempTransform = [[MTLTransform alloc] init];
_displayLinkCount = 0;
_lastRedrawTime = 0.0;
if (isDisplaySyncEnabled()) {
_layers = [[NSMutableSet alloc] init];
_displayLinkCount = 0;
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink);
CVDisplayLinkSetOutputCallback(_displayLink, &mtlDisplayLinkCallback, (__bridge void *) self);
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkCreateWithCGDisplay: "
"ctx=%p displayID=%d", self, displayID);
CHECK_CVLINK("CreateWithCGDisplay",
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink)
);
if (_displayLink == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext.initWithDevice(): Failed to initialize CVDisplayLink.");
return nil;
} else {
// LBO: should retain ?
/* CVDisplayLinkRetain(_displayLink); */
CHECK_CVLINK("SetOutputCallback",
CVDisplayLinkSetOutputCallback(_displayLink,
&mtlDisplayLinkCallback,
(__bridge void *) self)
);
}
} else {
_displayLink = nil;
}
_glyphCacheLCD = [[MTLGlyphCache alloc] initWithContext:self];
_glyphCacheAA = [[MTLGlyphCache alloc] initWithContext:self];
@@ -196,9 +238,39 @@ extern void initSamplers(id<MTLDevice> device);
return self;
}
- (void)handleDisplayLink: (BOOL)enabled source:(const char*)src {
/* should be called by APPKIT_THREAD except from dealloc: */
// AWT_ASSERT_APPKIT_THREAD
if (_displayLink == nil) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_handleDisplayLink[%s]: "
"ctx=%p - displayLink = nil", src, self);
} else {
if (enabled) {
if (!CVDisplayLinkIsRunning(_displayLink)) {
CHECK_CVLINK("Start", CVDisplayLinkStart(_displayLink));
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStart[%s]: "
"ctx=%p", src, self);
}
} else {
if (CVDisplayLinkIsRunning(_displayLink)) {
CHECK_CVLINK("Stop", CVDisplayLinkStop(_displayLink));
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStop[%s]: "
"ctx=%p", src, self);
}
}
}
}
- (void)dealloc {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.dealloc");
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLContext.dealloc: MTLContext[%p]", self);
if (_displayLink != nil) {
AWT_DEBUG_LOG(@"CVDisplayLink still running in dealloc.");
[self haltRedraw];
}
// TODO : Check that texturePool is completely released.
// texturePool content is released in MTLCommandBufferWrapper.onComplete()
//self.texturePool = nil;
@@ -255,15 +327,6 @@ extern void initSamplers(id<MTLDevice> device);
_layers = nil;
}
if (_displayLink != NULL) {
if (CVDisplayLinkIsRunning(_displayLink)) {
CVDisplayLinkStop(_displayLink);
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStop: ctx=%p", self);
}
CVDisplayLinkRelease(_displayLink);
_displayLink = NULL;
}
[super dealloc];
}
@@ -564,6 +627,20 @@ extern void initSamplers(id<MTLDevice> device);
- (void) redraw {
AWT_ASSERT_APPKIT_THREAD;
/*
* Avoid repeated invocations by UIKit Main Thread
* if blocked while many mtlDisplayLinkCallback() are dispatched
*/
const CFTimeInterval now = CACurrentMediaTime();
const CFTimeInterval elapsed = (_lastRedrawTime != 0.0) ? (now - _lastRedrawTime) : 0.0;
if ((elapsed != 0.0) && (elapsed <= KEEP_ALIVE_MIN_INTERVAL)) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "[%.3lf] MTLContext_redraw: elapsed: %.3lf ms => SKIP",
now, 1000.0 * elapsed);
return;
}
_lastRedrawTime = now;
// Process layers:
for (MTLLayer *layer in _layers) {
[layer setNeedsDisplay];
}
@@ -573,10 +650,7 @@ extern void initSamplers(id<MTLDevice> device);
if (_layers.count > 0) {
[_layers removeAllObjects];
}
if (CVDisplayLinkIsRunning(_displayLink)) {
CVDisplayLinkStop(_displayLink);
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStop: ctx=%p", self);
}
[self handleDisplayLink:NO source:"redraw"];
}
}
@@ -585,7 +659,7 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_mtlDisplayLinkCallback: ctx=%p", displayLinkContext);
@autoreleasepool {
MTLContext *ctx = (__bridge MTLContext *)displayLinkContext;
[ctx performSelectorOnMainThread:@selector(redraw) withObject:nil waitUntilDone:NO];
[ThreadUtilities performOnMainThread:@selector(redraw) on:ctx withObject:nil waitUntilDone:NO];
}
return kCVReturnSuccess;
}
@@ -600,10 +674,7 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
// Request for redraw before starting display link to avoid rendering problem on M2 processor
[layer setNeedsDisplay];
}
if (_displayLink != NULL && !CVDisplayLinkIsRunning(_displayLink)) {
CVDisplayLinkStart(_displayLink);
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStart: ctx=%p", self);
}
[self handleDisplayLink:YES source:"startRedraw"];
}
- (void)stopRedraw:(MTLLayer*) layer {
@@ -614,13 +685,29 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
[_layers removeObject:layer];
layer.redrawCount = 0;
}
if (_layers.count == 0 && _displayLinkCount == 0) {
if (CVDisplayLinkIsRunning(_displayLink)) {
CVDisplayLinkStop(_displayLink);
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStop: ctx=%p", self);
}
if ((_layers.count == 0) && (_displayLinkCount == 0)) {
[self handleDisplayLink:NO source:"stopRedraw"];
}
}
}
- (void)haltRedraw {
/* should be called by APPKIT_THREAD except from dealloc: */
// AWT_ASSERT_APPKIT_THREAD
if (_displayLink != nil) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_haltRedraw: ctx=%p", self);
if (_layers.count > 0) {
for (MTLLayer *layer in _layers) {
layer.redrawCount = 0;
}
[_layers removeAllObjects];
}
_displayLinkCount = 0;
[self handleDisplayLink:NO source:"haltRedraw"];
CVDisplayLinkRelease(_displayLink);
_displayLink = NULL;
}
}
@end

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -70,6 +70,7 @@ JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_getMTLConfigInfo
(JNIEnv *env, jclass mtlgc, jint displayID, jstring mtlShadersLib)
{
__block MTLContext* mtlc = nil;
__block MTLGraphicsConfigInfo* mtlinfo = nil;
JNI_COCOA_ENTER(env);
@@ -78,7 +79,7 @@ JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
MTLContext* mtlc = [[MTLContext alloc] initWithDevice:displayID
mtlc = [[MTLContext alloc] initWithDevice:displayID
shadersLib:path];
if (mtlc != 0L) {
// create the MTLGraphicsConfigInfo record for this context

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,7 @@
@property (readwrite, assign) int topInset;
@property (readwrite, assign) int leftInset;
@property (readwrite, atomic) int redrawCount;
@property (readwrite, atomic) CFTimeInterval lastPresentedTime;
@property (readwrite, atomic) NSTimeInterval avgBlitFrameTime;
- (id) initWithJavaLayer:(jobject)layer;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,10 @@
#define MAX_DRAWABLE 3
#define LAST_DRAWABLE (MAX_DRAWABLE - 1)
#define TRACE_DISPLAY 1
#define TRACE_DRAWABLE_LATENCY 0
const BOOL USE_CATRANSACTION = NO;
const NSTimeInterval DF_BLIT_FRAME_TIME=1.0/120.0;
BOOL isDisplaySyncEnabled() {
@@ -113,7 +117,9 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
return (BOOL)redrawEnabled;
}
@implementation MTLLayer
@implementation MTLLayer {
NSLock* _lock;
}
- (id) initWithJavaLayer:(jobject)layer
{
@@ -122,6 +128,8 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
self = [super init];
if (self == nil) return self;
J2dRlsTraceLn1(J2D_TRACE_INFO, "initWithJavaLayer: created MTLLayer[%p]", self);
self.javaLayer = layer;
self.contentsGravity = kCAGravityTopLeft;
@@ -149,14 +157,17 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
self.nextDrawableCount = 0;
self.opaque = YES;
self.redrawCount = 0;
self.lastPresentedTime = 0.0;
self.avgBlitFrameTime = DF_BLIT_FRAME_TIME;
if (@available(macOS 10.13, *)) {
self.displaySyncEnabled = isDisplaySyncEnabled();
}
if (@available(macOS 10.13.2, *)) {
self.maximumDrawableCount = MAX_DRAWABLE;
}
self.presentsWithTransaction = NO;
self.avgBlitFrameTime = DF_BLIT_FRAME_TIME;
self.presentsWithTransaction = (USE_CATRANSACTION && isDisplaySyncEnabled());
_lock = [[NSLock alloc] init];
return self;
}
@@ -171,91 +182,217 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
return;
}
if (self.nextDrawableCount >= LAST_DRAWABLE) {
if (!isDisplaySyncEnabled()) {
[self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
}
return;
}
// Perform blit:
[self stopRedraw:NO];
@autoreleasepool {
if (((*self.buffer).width == 0) || ((*self.buffer).height == 0)) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: cannot create drawable of size 0");
return;
}
// MTLDrawable pool barrier:
BOOL skipDrawable = YES;
NSUInteger src_x = self.leftInset * self.contentsScale;
NSUInteger src_y = self.topInset * self.contentsScale;
NSUInteger src_w = (*self.buffer).width - src_x;
NSUInteger src_h = (*self.buffer).height - src_y;
if (src_h <= 0 || src_w <= 0) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: Invalid src width or height.");
return;
}
id<MTLCommandBuffer> commandBuf = [self.ctx createBlitCommandBuffer];
if (commandBuf == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: commandBuf is null");
return;
}
id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
if (mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nextDrawable is null)");
return;
}
// increment used drawables:
self.nextDrawableCount++;
id <MTLBlitCommandEncoder> blitEncoder = [commandBuf blitCommandEncoder];
[blitEncoder
copyFromTexture:(isDisplaySyncEnabled()) ? (*self.buffer) : (*self.outBuffer)
sourceSlice:0 sourceLevel:0
sourceOrigin:MTLOriginMake(src_x, src_y, 0)
sourceSize:MTLSizeMake(src_w, src_h, 1)
toTexture:mtlDrawable.texture destinationSlice:0 destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blitEncoder endEncoding];
if (isDisplaySyncEnabled()) {
[commandBuf presentDrawable:mtlDrawable];
} else {
if (@available(macOS 10.15.4, *)) {
[commandBuf presentDrawable:mtlDrawable afterMinimumDuration:self.avgBlitFrameTime];
} else {
[commandBuf presentDrawable:mtlDrawable];
[_lock lock];
@try {
if (self.nextDrawableCount < LAST_DRAWABLE) {
// increment used drawables to act as the CPU fence:
self.nextDrawableCount++;
skipDrawable = NO;
}
} @finally {
[_lock unlock];
}
[self retain];
[commandBuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
// free drawable:
self.nextDrawableCount--;
if (skipDrawable) {
if (TRACE_DISPLAY) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "[%.6lf] MTLLayer_blitTexture: skip drawable [skip blit] nextDrawableCount = %d",
CACurrentMediaTime(), self.nextDrawableCount);
}
[self startRedraw];
return;
}
// Perform blit:
// Decrement redrawCount:
[self stopRedraw:NO];
// try-finally block to ensure releasing the CPU fence (abort blit):
BOOL releaseFence = YES;
@try {
if (((*self.buffer).width == 0) || ((*self.buffer).height == 0)) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: cannot create drawable of size 0");
return;
}
NSUInteger src_x = self.leftInset * self.contentsScale;
NSUInteger src_y = self.topInset * self.contentsScale;
NSUInteger src_w = (*self.buffer).width - src_x;
NSUInteger src_h = (*self.buffer).height - src_y;
if (src_h <= 0 || src_w <= 0) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: Invalid src width or height.");
return;
}
id<MTLCommandBuffer> commandBuf = [self.ctx createBlitCommandBuffer];
if (commandBuf == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: commandBuf is null");
return;
}
const CFTimeInterval beforeNextDrawableTime = (TRACE_DRAWABLE_LATENCY) ? CACurrentMediaTime() : 0.0;
const id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
const CFTimeInterval nextDrawableTime = (TRACE_DISPLAY || TRACE_DRAWABLE_LATENCY) ? CACurrentMediaTime() : 0.0;
if (mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nextDrawable is null)");
return;
}
if (TRACE_DRAWABLE_LATENCY) {
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeNextDrawableTime);
J2dRlsTraceLn5(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: layer=%p nextDrawable = drawable(%d) - nextDrawableCount = %d - nextDrawableLatency = %.3lf ms",
CACurrentMediaTime(), self, [mtlDrawable drawableID], self.nextDrawableCount,
1000.0 * nextDrawableLatency);
}
// Keep Fence from now:
releaseFence = NO;
MTLDrawablePresentedHandler presentedHandler = nil;
if (@available(macOS 10.15.4, *)) {
if (!isDisplaySyncEnabled()) {
// Exponential smoothing on elapsed time:
const NSTimeInterval gpuTime = commandbuf.GPUEndTime - commandbuf.GPUStartTime;
const NSTimeInterval a = 0.25;
self.avgBlitFrameTime = gpuTime * a + self.avgBlitFrameTime * (1.0 - a);
[self retain];
presentedHandler = ^(id <MTLDrawable> drawable) {
// note: called anyway even if drawable.present() not called!
// free drawable only once presented:
[self freeDrawableCount];
const CFTimeInterval presentedTime = drawable.presentedTime;
if (presentedTime != 0.0) {
if (TRACE_DISPLAY) {
const CFTimeInterval now = CACurrentMediaTime();
const CFTimeInterval presentedOffset = (now - presentedTime);
const CFTimeInterval presentedHandlerLatency = (now - nextDrawableTime);
const CFTimeInterval frameInterval = (self.lastPresentedTime != 0.0) ? (presentedTime - self.lastPresentedTime) : -1.0;
J2dRlsTraceLn5(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: PresentedHandler: drawable(%d) presented"
" - presentedHandlerLatency = %.3lf ms (offset: %.3lf ms) frameInterval = %.3lf ms",
CACurrentMediaTime(), drawable.drawableID,
1000.0 * presentedHandlerLatency, 1000.0 * presentedOffset,
1000.0 * frameInterval
);
}
self.lastPresentedTime = presentedTime;
} else {
if (TRACE_DISPLAY) {
const CFTimeInterval now = CACurrentMediaTime();
const CFTimeInterval presentedHandlerLatency = (now - nextDrawableTime);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: PresentedHandler: drawable(%d) skipped"
" - presentedHandlerLatency = %.3lf ms",
CACurrentMediaTime(), drawable.drawableID, 1000.0 * presentedHandlerLatency
);
}
}
[self release];
};
}
id <MTLBlitCommandEncoder> blitEncoder = [commandBuf blitCommandEncoder];
[blitEncoder
copyFromTexture:(isDisplaySyncEnabled()) ? (*self.buffer) : (*self.outBuffer)
sourceSlice:0 sourceLevel:0
sourceOrigin:MTLOriginMake(src_x, src_y, 0)
sourceSize:MTLSizeMake(src_w, src_h, 1)
toTexture:mtlDrawable.texture destinationSlice:0 destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blitEncoder endEncoding];
if (!isDisplaySyncEnabled()) {
if (presentedHandler != nil) {
[mtlDrawable addPresentedHandler:presentedHandler];
}
if (@available(macOS 10.15.4, *)) {
[commandBuf presentDrawable:mtlDrawable afterMinimumDuration:self.avgBlitFrameTime];
} else {
[commandBuf presentDrawable:mtlDrawable];
}
}
[self release];
}];
[commandBuf commit];
[self retain];
[mtlDrawable retain];
[commandBuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
if (isDisplaySyncEnabled()) {
if (TRACE_DISPLAY) {
J2dRlsTraceLn3(J2D_TRACE_INFO,
"[%.6lf] MTLLayer.blitTexture: CompletedHandler: layer[%p] present drawable(%d)",
CACurrentMediaTime(), self, mtlDrawable.drawableID);
}
// present drawable:
if (presentedHandler != nil) {
[mtlDrawable addPresentedHandler:presentedHandler];
}
[ThreadUtilities performOnMainThread:@selector(present) on:mtlDrawable withObject:nil waitUntilDone:NO];
}
if (presentedHandler == nil) {
// free drawable:
[self freeDrawableCount];
}
if (!isDisplaySyncEnabled()) {
if (@available(macOS 10.15.4, *)) {
const NSTimeInterval gpuTime = commandBuf.GPUEndTime - commandBuf.GPUStartTime;
const NSTimeInterval a = 0.25;
self.avgBlitFrameTime = gpuTime * a + self.avgBlitFrameTime * (1.0 - a);
}
}
[mtlDrawable release];
[self release];
}];
[commandBuf commit];
} @finally {
// try-finally block to ensure releasing the CPU fence:
if (releaseFence) {
// free drawable:
[self freeDrawableCount];
if (isDisplaySyncEnabled()) {
// Increment redrawCount:
[self startRedraw];
}
}
}
}
}
- (void) freeDrawableCount {
[_lock lock];
@try {
--self.nextDrawableCount;
} @finally {
[_lock unlock];
}
}
- (void) dealloc {
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLLayer.dealloc: MTLLayer[%p]", self);
// LBO: dangerous as async ?
[self stopRedraw:YES];
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
(*env)->DeleteWeakGlobalRef(env, self.javaLayer);
self.javaLayer = nil;
[self stopRedraw:YES];
self.buffer = NULL;
self.ctx = NULL;
[_lock release];
_lock = nil;
[super dealloc];
}
@@ -286,6 +423,7 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
AWT_ASSERT_APPKIT_THREAD;
if (isDisplaySyncEnabled()) {
if (self.redrawCount == 0) {
if (0) J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLLayer_startRedrawIfNeeded: layer = %p redrawCount = %d", self, self.redrawCount);
[self.ctx startRedraw:self];
}
}
@@ -294,10 +432,14 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
- (void)startRedraw {
if (isDisplaySyncEnabled()) {
if (self.ctx != nil) {
[self.ctx performSelectorOnMainThread:@selector(startRedraw:) withObject:self waitUntilDone:NO];
[ThreadUtilities performOnMainThreadNowOrLater:^(){
[self.ctx startRedraw:self];
}];
}
} else {
[self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
[ThreadUtilities performOnMainThreadNowOrLater:^(){
[self setNeedsDisplay];
}];
}
}
@@ -307,7 +449,9 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
self.redrawCount = 0;
}
if (self.ctx != nil) {
[self.ctx performSelectorOnMainThread:@selector(stopRedraw:) withObject:self waitUntilDone:NO];
[ThreadUtilities performOnMainThreadNowOrLater:^(){
[self.ctx stopRedraw:self];
}];
}
}
}
@@ -344,7 +488,7 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
[cbwrapper release];
if (updateDisplay && isDisplaySyncEnabled()) {
// Ensure layer will be redrawn asap to display new content:
[self performSelectorOnMainThread:@selector(startRedrawIfNeeded) withObject:nil waitUntilDone:NO];
[ThreadUtilities performOnMainThread:@selector(startRedrawIfNeeded) on:self withObject:nil waitUntilDone:NO];
}
[self release];
}];
@@ -382,12 +526,15 @@ JNI_COCOA_ENTER(env);
jobject javaLayer = (*env)->NewWeakGlobalRef(env, obj);
// Wait and ensure main thread creates the MTLLayer instance now:
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
AWT_ASSERT_APPKIT_THREAD;
layer = [[MTLLayer alloc] initWithJavaLayer: javaLayer];
}];
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLLayer_nativeCreateLayer: created MTLLayer[%p]", layer);
JNI_COCOA_EXIT(env);
return ptr_to_jlong(layer);
@@ -419,6 +566,7 @@ Java_sun_java2d_metal_MTLLayer_validate
layer.drawableSize =
CGSizeMake((*layer.buffer).width,
(*layer.buffer).height);
if (isDisplaySyncEnabled()) {
[layer startRedraw];
} else {
@@ -436,11 +584,14 @@ Java_sun_java2d_metal_MTLLayer_nativeSetScale
{
JNI_COCOA_ENTER(env);
MTLLayer *layer = jlong_to_ptr(layerPtr);
// We always call all setXX methods asynchronously, exception is only in
// this method where we need to change native texture size and layer's scale
// in one call on appkit, otherwise we'll get window's contents blinking,
// during screen-2-screen moving.
[ThreadUtilities performOnMainThreadWaiting:[NSThread isMainThread] block:^(){
// Ensure main thread changes the MTLLayer instance later:
[ThreadUtilities performOnMainThreadNowOrLater:^(){
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLLayer_nativeSetScale: layer = %p scale = %.3lf", layer, scale);
layer.contentsScale = scale;
}];
JNI_COCOA_EXIT(env);
@@ -451,19 +602,26 @@ Java_sun_java2d_metal_MTLLayer_nativeSetInsets
(JNIEnv *env, jclass cls, jlong layerPtr, jint top, jint left)
{
MTLLayer *layer = jlong_to_ptr(layerPtr);
layer.topInset = top;
layer.leftInset = left;
[ThreadUtilities performOnMainThreadNowOrLater:^() {
J2dRlsTraceLn3(J2D_TRACE_VERBOSE, "MTLLayer_nativeSetInsets: layer = %p top = %d left = %d", layer, top, left);
layer.topInset = top;
layer.leftInset = left;
}];
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_blitTexture
(JNIEnv *env, jclass cls, jlong layerPtr)
{
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blitTexture");
JNI_COCOA_ENTER(env);
MTLLayer *layer = jlong_to_ptr(layerPtr);
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLLayer_blitTexture: layer = %p", layer);
MTLContext * ctx = layer.ctx;
if (layer == nil || ctx == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blit : Layer or Context is null");
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blitTexture : Layer or Context is null");
if (layer != nil) {
[layer stopRedraw:YES];
}
@@ -471,6 +629,7 @@ Java_sun_java2d_metal_MTLLayer_blitTexture
}
[layer blitTexture];
JNI_COCOA_EXIT(env);
}
JNIEXPORT void JNICALL
@@ -478,11 +637,12 @@ Java_sun_java2d_metal_MTLLayer_nativeSetOpaque
(JNIEnv *env, jclass cls, jlong layerPtr, jboolean opaque)
{
JNI_COCOA_ENTER(env);
MTLLayer *layer = jlong_to_ptr(layerPtr);
MTLLayer *mtlLayer = OBJC(layerPtr);
// Ensure main thread changes the MTLLayer instance later:
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[mtlLayer setOpaque:(opaque == JNI_TRUE)];
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLLayer_nativeSetOpaque: layer = %p opaque = %d", layer, opaque);
[layer setOpaque:(opaque == JNI_TRUE)];
}];
JNI_COCOA_EXIT(env);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,11 @@
#include "MTLTextRenderer.h"
#import "ThreadUtilities.h"
#define CHECK_MAIN_THREAD_LATENCY 1
// ~ 2ms is high for main thread:
#define LATENCY_HIGH_THRESHOLD 2.0
/**
* References to the "current" context and destination surface.
*/
@@ -48,6 +53,22 @@ jint mtlPreviousOp = MTL_OP_INIT;
extern BOOL isDisplaySyncEnabled();
extern void MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo);
void checkMainThreadLatency() {
const CFTimeInterval start = CACurrentMediaTime();
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
const CFTimeInterval now = CACurrentMediaTime();
const CFTimeInterval elapsedMs = 1000.0 * (now - start);
if ((elapsedMs != 0.0) && (elapsedMs >= LATENCY_HIGH_THRESHOLD)) {
J2dRlsTraceLn3(J2D_TRACE_VERBOSE, "[%.3lf] checkMainThreadLatency: MainThread latency: %.3lf ms (> %.3lf ms)",
now, elapsedMs, LATENCY_HIGH_THRESHOLD);
return;
}
}];
}
void MTLRenderQueue_CheckPreviousOp(jint op) {
if (mtlPreviousOp == op) {
@@ -651,10 +672,24 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pConfigInfo = NEXT_LONG(b);
CONTINUE_IF_NULL(mtlc);
J2dRlsTraceLn1(J2D_TRACE_INFO, "RQ: DISPOSE_CONFIG: start MTLContext[%p] -----", mtlc);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
[mtlc haltRedraw];
}];
[mtlc commitCommandBuffer:YES display:NO];
// free resources:
if (mtlPreviousOp == MTL_OP_MASK_OP) {
MTLVertexCache_DisableMaskCache(mtlc);
}
[mtlc.glyphCacheAA free];
[mtlc.glyphCacheLCD free];
MTLGC_DestroyMTLGraphicsConfig(pConfigInfo);
J2dRlsTraceLn1(J2D_TRACE_INFO, "RQ: DISPOSE_CONFIG: end MTLContext[%p] -----", mtlc);
mtlc = NULL;
break;
}
@@ -880,7 +915,7 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
"MTLRenderQueue_flushBuffer: invalid opcode=%d", opcode);
return;
}
}
} // buffer processing
if (mtlc != NULL) {
if (mtlPreviousOp == MTL_OP_MASK_OP) {
@@ -894,6 +929,10 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
}
}
RESET_PREVIOUS_OP();
#if CHECK_MAIN_THREAD_LATENCY == 1
checkMainThreadLatency();
#endif
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -225,7 +225,9 @@ Java_sun_java2d_opengl_CGLLayer_nativeSetScale
// this method where we need to change native texture size and layer's scale
// in one call on appkit, otherwise we'll get window's contents blinking,
// during screen-2-screen moving.
[ThreadUtilities performOnMainThreadWaiting:[NSThread isMainThread] block:^(){
// Ensure main thread changes the MTLLayer instance later:
[ThreadUtilities performOnMainThreadNowOrLater:^(){
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "CGLLayer_nativeSetScale: layer = %p scale = %.3lf", layer, scale);
layer.contentsScale = scale;
}];
JNI_COCOA_EXIT(env);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -139,9 +139,14 @@ __attribute__((visibility("default")))
+ (void)detachCurrentThread;
+ (void)setAppkitThreadGroup:(jobject)group;
+ (NSString*)getCaller;
+ (void)performOnMainThreadNowOrLater:(void (^)())block;
+ (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block;
+ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait;
+ (NSString*)javaRunLoopMode;
@end
JNIEXPORT void OSXAPP_SetJavaVM(JavaVM *vm);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,23 @@
#import <AppKit/AppKit.h>
#import <objc/message.h>
#include <mach/mach_time.h>
#import "ThreadUtilities.h"
/*
* Enable the MainThread task monitor to detect slow tasks that may cause high latencies or delays
*/
#define CHECK_MAIN_THREAD 0
#define TRACE_MAIN_THREAD 0
// ~ 2ms is already high for main thread:
#define LATENCY_HIGH_THRESHOLD 2.0
#define DECORATE_MAIN_THREAD (TRACE_MAIN_THREAD || CHECK_MAIN_THREAD)
// The following must be named "jvm", as there are extern references to it in AWT
JavaVM *jvm = NULL;
static JNIEnv *appKitEnv = NULL;
@@ -110,32 +123,109 @@ AWT_ASSERT_APPKIT_THREAD;
Block_release(blockCopy);
}
+ (void)performOnMainThreadNowOrLater:(void (^)())block {
if ([NSThread isMainThread]) {
block();
} else {
[self performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:NO];
}
}
+ (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block {
if ([NSThread isMainThread] && wait == YES) {
if ([NSThread isMainThread] && wait) {
block();
} else {
[self performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:wait];
}
}
+ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait {
if ([NSThread isMainThread] && wait == YES) {
[target performSelector:aSelector withObject:arg];
} else {
if (wait && isEventDispatchThread()) {
void (^blockCopy)(void) = Block_copy(^(){
setBlockingEventDispatchThread(YES);
@try {
[target performSelector:aSelector withObject:arg];
} @finally {
setBlockingEventDispatchThread(NO);
}
});
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:YES modes:javaModes];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
}
+ (NSString*)getCaller {
#if DECORATE_MAIN_THREAD > 0
const NSArray<NSString*> *symbols = NSThread.callStackSymbols;
for (NSUInteger i = 2, len = [symbols count]; i < len; i++) {
NSString* symbol = [symbols objectAtIndex:i];
if (![symbol hasPrefix: @"performOnMainThread"]) {
return symbol;
}
}
#endif
return nil;
}
+ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait {
#if DECORATE_MAIN_THREAD == 0
if ([NSThread isMainThread] && wait) {
[target performSelector:aSelector withObject:arg];
} else if (wait && isEventDispatchThread()) {
void (^blockCopy)(void) = Block_copy(^() {
setBlockingEventDispatchThread(YES);
@try {
[target performSelector:aSelector withObject:arg];
} @finally {
setBlockingEventDispatchThread(NO);
}
});
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:YES modes:javaModes];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
}
#else
// Perform instrumentation on selector:
static mach_timebase_info_data_t* timebase = nil;
if (timebase == nil) {
timebase = malloc(sizeof (mach_timebase_info_data_t));
mach_timebase_info(timebase);
}
const NSString* caller = [self getCaller];
BOOL invokeDirect = NO;
BOOL blockingEDT;
if ([NSThread isMainThread] && wait) {
invokeDirect = YES;
blockingEDT = NO;
} else if (wait && isEventDispatchThread()) {
blockingEDT = YES;
} else {
blockingEDT = NO;
}
const char* operation = (invokeDirect ? "now " : (blockingEDT ? "block" : "later"));
void (^blockCopy)(void) = Block_copy(^(){
const uint64_t start = mach_absolute_time();
if (TRACE_MAIN_THREAD) {
NSLog(@"performOnMainThread(%s)[before block]: [%@]", operation, caller);
}
if (blockingEDT) {
setBlockingEventDispatchThread(YES);
}
@try {
[target performSelector:aSelector withObject:arg];
} @finally {
if (blockingEDT) {
setBlockingEventDispatchThread(NO);
}
const double elapsedMs = ((mach_absolute_time() - start) * timebase->numer) / (1000000.0 * timebase->denom);
if (TRACE_MAIN_THREAD) {
NSLog(@"performOnMainThread(%s)[after block - time: %.3lf ms]: [%@]", operation, elapsedMs, caller);
}
if (CHECK_MAIN_THREAD && (elapsedMs >= LATENCY_HIGH_THRESHOLD)) {
NSLog(@"performOnMainThread(%s)[time: %.3lf ms]: [%@]", operation, elapsedMs, caller);
}
}
});
if (invokeDirect) {
[self performSelector:@selector(invokeBlockCopy:) withObject:blockCopy];
} else {
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:wait modes:javaModes];
}
#endif
}
+ (NSString*)javaRunLoopMode {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
#import "JNIUtilities.h"
#import <JavaRuntimeSupport/JavaRuntimeSupport.h>
#import <ThreadUtilities.h>
#import "apple_laf_JRSUIControl.h"
#import "apple_laf_JRSUIConstants_DoubleValue.h"
@@ -158,9 +159,14 @@ JNIEXPORT jint JNICALL Java_apple_laf_JRSUIControl_syncChanges
static inline jint doPaintCGContext(CGContextRef cgRef, jlong controlPtr, jlong oldProperties, jlong newProperties, jdouble x, jdouble y, jdouble w, jdouble h)
{
JRSUIControlRef control = (JRSUIControlRef)jlong_to_ptr(controlPtr);
_SyncEncodedProperties(control, oldProperties, newProperties);
CGRect bounds = CGRectMake(x, y, w, h);
JRSUIControlDraw(gRenderer, control, cgRef, bounds);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CGRect bounds = CGRectMake(x, y, w, h);
JRSUIControlDraw(gRenderer, control, cgRef, bounds);
}];
return 0;
}
@@ -248,7 +254,13 @@ JNIEXPORT jint JNICALL Java_apple_laf_JRSUIControl_getNativeHitPart
CGRect bounds = CGRectMake(x, y, w, h);
CGPoint point = CGPointMake(pointX, pointY);
return JRSUIControlGetHitPart(gRenderer, control, bounds, point);
__block JRSUIPartHit result;
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
result = JRSUIControlGetHitPart(gRenderer, control, bounds, point);
}];
return (jint)result;
}
/*
@@ -259,7 +271,12 @@ JNIEXPORT jint JNICALL Java_apple_laf_JRSUIControl_getNativeHitPart
JNIEXPORT jboolean JNICALL Java_apple_laf_JRSUIUtils_00024ScrollBar_shouldUseScrollToClick
(JNIEnv *env, jclass clazz)
{
return JRSUIControlShouldScrollToClick();
__block Boolean result;
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
result = JRSUIControlShouldScrollToClick();
}];
return result;
}
/*
@@ -273,8 +290,12 @@ JNIEXPORT void JNICALL Java_apple_laf_JRSUIControl_getNativePartBounds
JRSUIControlRef control = (JRSUIControlRef)jlong_to_ptr(controlPtr);
_SyncEncodedProperties(control, oldProperties, newProperties);
CGRect frame = CGRectMake(x, y, w, h);
CGRect partBounds = JRSUIControlGetScrollBarPartBounds(control, frame, part);
__block CGRect partBounds;
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CGRect frame = CGRectMake(x, y, w, h);
partBounds = JRSUIControlGetScrollBarPartBounds(control, frame, part);
}];
jdouble *rect = (*env)->GetPrimitiveArrayCritical(env, rectArray, NULL);
if (rect != NULL) {
@@ -297,6 +318,11 @@ JNIEXPORT jdouble JNICALL Java_apple_laf_JRSUIControl_getNativeScrollBarOffsetCh
JRSUIControlRef control = (JRSUIControlRef)jlong_to_ptr(controlPtr);
_SyncEncodedProperties(control, oldProperties, newProperties);
CGRect frame = CGRectMake(x, y, w, h);
return (jdouble)JRSUIControlGetScrollBarOffsetFor(control, frame, offset, visibleAmount, extent);
__block jdouble result;
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CGRect frame = CGRectMake(x, y, w, h);
result = (jdouble)JRSUIControlGetScrollBarOffsetFor(control, frame, offset, visibleAmount, extent);
}];
return result;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -154,6 +154,9 @@ J2dTraceImpl(int level, jboolean cr, const char *string, ...);
#define J2dRlsTrace5(level, string, arg1, arg2, arg3, arg4, arg5) { \
J2dTraceImpl(level, JNI_FALSE, string, arg1, arg2, arg3, arg4, arg5); \
}
#define J2dRlsTrace6(level, string, arg1, arg2, arg3, arg4, arg5, arg6) { \
J2dTraceImpl(level, JNI_FALSE, string, arg1, arg2, arg3, arg4, arg5, arg6); \
}
#define J2dRlsTraceLn(level, string) { \
J2dTraceImpl(level, JNI_TRUE, string); \
}
@@ -172,6 +175,9 @@ J2dTraceImpl(int level, jboolean cr, const char *string, ...);
#define J2dRlsTraceLn5(level, string, arg1, arg2, arg3, arg4, arg5) { \
J2dTraceImpl(level, JNI_TRUE, string, arg1, arg2, arg3, arg4, arg5); \
}
#define J2dRlsTraceLn6(level, string, arg1, arg2, arg3, arg4, arg5, arg6) { \
J2dTraceImpl(level, JNI_TRUE, string, arg1, arg2, arg3, arg4, arg5, arg6); \
}
#ifdef __cplusplus
}