mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-8159: kill CVDisplayLink zombies (sleep / wake-up with multiple monitors in mirroring) + deal with display link thread shutdown (destroy threads at sleep) and restart when needed
This commit is contained in:
@@ -863,7 +863,7 @@ MTLBlitLoops_CopyArea(JNIEnv *env,
|
||||
MTLCommandBufferWrapper * cbwrapper =
|
||||
[mtlc pullCommandBufferWrapper];
|
||||
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
|
||||
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
|
||||
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> cb) {
|
||||
[cbwrapper release];
|
||||
}];
|
||||
[commandbuf commit];
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
@property (strong) id<MTLCommandQueue> blitCommandQueue;
|
||||
@property (strong) id<MTLBuffer> vertexBuffer;
|
||||
|
||||
@property (readonly) NSMutableDictionary<NSNumber*, NSValue*>* displayLinks;
|
||||
@property (readonly) NSMutableDictionary<NSNumber*, NSValue*>* displayLinkStates;
|
||||
|
||||
@property (readonly) EncoderManager * encoderManager;
|
||||
@property (readonly) MTLSamplerManager * samplerManager;
|
||||
@@ -111,10 +111,11 @@
|
||||
|
||||
+ (NSMutableDictionary*) contextStore;
|
||||
+ (MTLContext*) createContextWithDeviceIfAbsent:(jint)displayID shadersLib:(NSString*)mtlShadersLib;
|
||||
- (id)initWithDevice:(id<MTLDevice>)device display:(jint) displayID shadersLib:(NSString*)mtlShadersLib;
|
||||
- (id)initWithDevice:(id<MTLDevice>)device shadersLib:(NSString*)mtlShadersLib;
|
||||
- (void)dealloc;
|
||||
|
||||
- (void)handleDisplayLink:(BOOL)enabled display:(jint)display source:(const char*)src;
|
||||
- (NSArray<NSNumber*>*)getDisplayLinkDisplayIds;
|
||||
- (void)handleDisplayLink:(BOOL)enabled displayID:(jint)displayID source:(const char*)src;
|
||||
- (void)createDisplayLinkIfAbsent: (jint)displayID;
|
||||
|
||||
/**
|
||||
@@ -253,6 +254,7 @@
|
||||
- (void)commitCommandBuffer:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
|
||||
- (void)startRedraw:(MTLLayer*)layer;
|
||||
- (void)stopRedraw:(MTLLayer*)layer;
|
||||
- (void)stopRedraw:(jint)displayID layer:(MTLLayer*)layer;
|
||||
- (void)haltRedraw;
|
||||
@end
|
||||
|
||||
|
||||
@@ -39,9 +39,13 @@
|
||||
// scenarios with multiple subsequent updates.
|
||||
#define KEEP_ALIVE_COUNT 4
|
||||
|
||||
// Min interval between 2 display link callbacks (Main thread may be busy)
|
||||
// ~ 2ms (shorter than best monitor frame rate = 500 hz)
|
||||
#define KEEP_ALIVE_MIN_INTERVAL 2.0 / 1000.0
|
||||
#define TO_MS(x) (1000.0 * (x))
|
||||
#define TO_FPS(x) (1.0 / (x))
|
||||
|
||||
// Min interval(s) between 2 display link callbacks as the appkit (Main)
|
||||
// thread may be 'blocked' (waiting), then callbacks will be invoked in batches.
|
||||
// ~ 0.250ms (shorter than best monitor frame rate = 4 khz)
|
||||
#define KEEP_ALIVE_MIN_INTERVAL (0.250 / 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
|
||||
@@ -53,20 +57,36 @@ extern BOOL isDisplaySyncEnabled();
|
||||
extern BOOL MTLLayer_isExtraRedrawEnabled();
|
||||
extern int getBPPFromModeString(CFStringRef mode);
|
||||
|
||||
#define STATS_CVLINK 0
|
||||
#define STATS_CVLINK 0
|
||||
|
||||
#define TRACE_CVLINK 0
|
||||
#define TRACE_DISPLAY 0
|
||||
#define TRACE_PWM_NOTIF 0
|
||||
|
||||
#define CHECK_CVLINK(op, source, cmd) \
|
||||
{ \
|
||||
CVReturn ret = (CVReturn) (cmd); \
|
||||
if (ret != kCVReturnSuccess) { \
|
||||
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, "CVDisplayLink[%s - %s] Error: %d", \
|
||||
op, (source != nil) ? source : "", ret); \
|
||||
} \
|
||||
#define TRACE_CVLINK 0
|
||||
#define TRACE_CVLINK_WARN 0
|
||||
#define TRACE_CVLINK_DEBUG 0
|
||||
|
||||
#define TRACE_DISPLAY 0
|
||||
|
||||
#define CHECK_CVLINK(op, source, dl, cmd) \
|
||||
{ \
|
||||
CVReturn ret = (CVReturn) (cmd); \
|
||||
if (ret != kCVReturnSuccess) { \
|
||||
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, "CVDisplayLink[%s - %s][%p] Error: %d", \
|
||||
op, (source != nil) ? source : "", dl, ret); \
|
||||
} else if (TRACE_CVLINK) { \
|
||||
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "CVDisplayLink[%s - %s][%p]: OK", \
|
||||
op, (source != nil) ? source : "", dl); \
|
||||
} \
|
||||
}
|
||||
|
||||
boolean_t mtlc_IsDisplayReallyActive(CGDirectDisplayID displayID) {
|
||||
return CGDisplayIsActive(displayID) & !CGDisplayIsAsleep(displayID) && CGDisplayIsOnline(displayID);
|
||||
}
|
||||
|
||||
/* 60 fps typically => exponential smoothing on 0.5s */
|
||||
static const NSTimeInterval EXP_AVG_WEIGHT = (1.0 / 30.0);
|
||||
static const NSTimeInterval EXP_INV_WEIGHT = (1.0 - EXP_AVG_WEIGHT);
|
||||
|
||||
static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
|
||||
{{-1.0, 1.0}, {0.0, 0.0}},
|
||||
{{1.0, 1.0}, {1.0, 0.0}},
|
||||
@@ -80,7 +100,15 @@ typedef struct {
|
||||
jint displayID;
|
||||
CVDisplayLinkRef displayLink;
|
||||
MTLContext* mtlc;
|
||||
} DLParams;
|
||||
jint redrawCount;
|
||||
|
||||
jint avgDisplayLinkSamples;
|
||||
CFTimeInterval lastRedrawTime;
|
||||
CFTimeInterval lastDisplayLinkTime;
|
||||
CFTimeInterval avgDisplayLinkTime;
|
||||
CFTimeInterval lastStatTime;
|
||||
|
||||
} MTLDisplayLinkState;
|
||||
|
||||
@implementation MTLCommandBufferWrapper {
|
||||
id<MTLCommandBuffer> _commandBuffer;
|
||||
@@ -142,13 +170,7 @@ typedef struct {
|
||||
|
||||
@implementation MTLContext {
|
||||
MTLCommandBufferWrapper * _commandBufferWrapper;
|
||||
NSMutableSet* _layers;
|
||||
int _displayLinkCount;
|
||||
CFTimeInterval _lastDisplayLinkTime;
|
||||
CFTimeInterval _avgDisplayLinkTime;
|
||||
CFTimeInterval _lastRedrawTime;
|
||||
|
||||
CFTimeInterval _lastStatTime;
|
||||
NSMutableSet* _layers;
|
||||
|
||||
MTLComposite * _composite;
|
||||
MTLPaint * _paint;
|
||||
@@ -157,7 +179,7 @@ typedef struct {
|
||||
MTLClip * _clip;
|
||||
|
||||
NSObject* _bufImgOp; // TODO: pass as parameter of IsoBlit
|
||||
EncoderManager * _encoderManager;
|
||||
EncoderManager * _encoderManager;
|
||||
MTLSamplerManager * _samplerManager;
|
||||
MTLStencilManager * _stencilManager;
|
||||
}
|
||||
@@ -171,19 +193,107 @@ typedef struct {
|
||||
|
||||
extern void initSamplers(id<MTLDevice> device);
|
||||
|
||||
+ (void)mtlc_systemOrScreenWillSleep:(NSNotification*)notification {
|
||||
if (TRACE_PWM_NOTIF) {
|
||||
NSLog(@"MTLContext_systemOrScreenWillSleep[%@]", [notification name]);
|
||||
}
|
||||
if (isDisplaySyncEnabled()) {
|
||||
[ThreadUtilities performOnMainThreadNowOrLater:NO // critical
|
||||
block:^(){
|
||||
|
||||
for (MTLContext *mtlc in [MTLContext.contextStore allValues]) {
|
||||
const NSArray<NSNumber*> *displayIDs = [mtlc getDisplayLinkDisplayIds]; // old ids
|
||||
if (TRACE_PWM_NOTIF) {
|
||||
NSLog(@"MTLContext_systemOrScreenWillSleep: ctx=%p (%d displayLinks)",
|
||||
mtlc, (int) displayIDs.count);
|
||||
}
|
||||
for (NSNumber* displayIDVal in displayIDs) {
|
||||
const jint displayID = [displayIDVal intValue];
|
||||
const BOOL active = mtlc_IsDisplayReallyActive(displayID);
|
||||
|
||||
if (TRACE_PWM_NOTIF) {
|
||||
NSLog(@"MTLContext_systemOrScreenWillSleep: displayId=%d active=%d", displayID, active);
|
||||
}
|
||||
if (TRACE_DISPLAY) {
|
||||
[MTLContext dumpDisplayInfo:displayID];
|
||||
}
|
||||
if ((notification.name == NSWorkspaceWillSleepNotification)|| !active) {
|
||||
[mtlc destroyDisplayLink:displayID];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)mtlc_systemOrScreenDidWake:(NSNotification*)notification {
|
||||
if (TRACE_PWM_NOTIF) {
|
||||
NSLog(@"MTLContext_systemOrScreenDidWake[%@]", [notification name]);
|
||||
}
|
||||
if (isDisplaySyncEnabled()) {
|
||||
[ThreadUtilities performOnMainThreadNowOrLater:NO // critical
|
||||
block:^(){
|
||||
|
||||
for (MTLContext *mtlc in [MTLContext.contextStore allValues]) {
|
||||
const NSArray<NSNumber*>* displayIDs = [mtlc getDisplayLinkDisplayIds]; // old ids
|
||||
if (TRACE_PWM_NOTIF) {
|
||||
NSLog(@"MTLContext_systemOrScreenDidWake: ctx=%p (%d displayLinks)",
|
||||
mtlc, (int)displayIDs.count);
|
||||
}
|
||||
for (NSNumber* displayIDVal in displayIDs) {
|
||||
const jint displayID = [displayIDVal intValue];
|
||||
const BOOL active = mtlc_IsDisplayReallyActive(displayID);
|
||||
|
||||
if (TRACE_PWM_NOTIF) {
|
||||
NSLog(@"MTLContext_systemOrScreenDidWake: displayId=%d active=%d", displayID, active);
|
||||
}
|
||||
if (TRACE_DISPLAY) {
|
||||
[MTLContext dumpDisplayInfo:displayID];
|
||||
}
|
||||
if (active) {
|
||||
// (if needed will start a new display link thread):
|
||||
[mtlc createDisplayLinkIfAbsent:displayID];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)registerForSystemAndScreenNotifications {
|
||||
static BOOL notificationRegistered = false;
|
||||
if (!notificationRegistered) {
|
||||
notificationRegistered = true;
|
||||
|
||||
NSNotificationCenter *ctr = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
Class clz = [MTLContext class];
|
||||
|
||||
[ctr addObserver:clz selector:@selector(mtlc_systemOrScreenWillSleep:) name:NSWorkspaceWillSleepNotification object:nil];
|
||||
[ctr addObserver:clz selector:@selector(mtlc_systemOrScreenDidWake:) name:NSWorkspaceDidWakeNotification object:nil];
|
||||
|
||||
// NSWorkspaceScreensDidWakeNotification is first sent:
|
||||
[ctr addObserver:clz selector:@selector(mtlc_systemOrScreenWillSleep:) name:NSWorkspaceScreensDidSleepNotification object:nil];
|
||||
[ctr addObserver:clz selector:@selector(mtlc_systemOrScreenDidWake:) name:NSWorkspaceScreensDidWakeNotification object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSMutableDictionary*) contextStore {
|
||||
static NSMutableDictionary<NSNumber*, MTLContext*> *_contextStore;
|
||||
static NSMutableDictionary<id<NSCopying>, MTLContext*> *_contextStore;
|
||||
static dispatch_once_t oncePredicate;
|
||||
|
||||
dispatch_once(&oncePredicate, ^{
|
||||
_contextStore = [[NSMutableDictionary alloc] init];
|
||||
});
|
||||
|
||||
[MTLContext registerForSystemAndScreenNotifications];
|
||||
});
|
||||
return _contextStore;
|
||||
}
|
||||
|
||||
+ (MTLContext*) createContextWithDeviceIfAbsent:(jint)displayID shadersLib:(NSString*)mtlShadersLib {
|
||||
// Initialization code here.
|
||||
// note: the device reference is NS_RETURNS_RETAINED, should be released by the caller:
|
||||
bool shouldReleaseDevice = true;
|
||||
|
||||
id<MTLDevice> device = CGDirectDisplayCopyCurrentMetalDevice(displayID);
|
||||
if (device == nil) {
|
||||
J2dRlsTraceLn1(J2D_TRACE_ERROR, "MTLContext_createContextWithDeviceIfAbsent(): Cannot create device from "
|
||||
@@ -200,61 +310,73 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
}
|
||||
}
|
||||
|
||||
id<NSCopying> devID = nil;
|
||||
|
||||
id<NSCopying> deviceID = nil;
|
||||
if (@available(macOS 10.13, *)) {
|
||||
devID = @(device.registryID);
|
||||
deviceID = @(device.registryID);
|
||||
} else {
|
||||
devID = device.name;
|
||||
deviceID = device.name;
|
||||
}
|
||||
|
||||
MTLContext* mtlc = MTLContext.contextStore[devID];
|
||||
MTLContext* mtlc = MTLContext.contextStore[deviceID];
|
||||
if (mtlc == nil) {
|
||||
mtlc = [[MTLContext alloc] initWithDevice:device display:displayID shadersLib:mtlShadersLib];
|
||||
mtlc = [[MTLContext alloc] initWithDevice:device shadersLib:mtlShadersLib];
|
||||
if (mtlc != nil) {
|
||||
MTLContext.contextStore[devID] = mtlc;
|
||||
shouldReleaseDevice = false;
|
||||
// transfer ownership (objc ref):
|
||||
MTLContext.contextStore[deviceID] = mtlc;
|
||||
[mtlc release];
|
||||
J2dRlsTraceLn4(J2D_TRACE_INFO,
|
||||
"MTLContext_createContextWithDeviceIfAbsent: new context(%p) for display=%d device=\"%s\" "
|
||||
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String], mtlc.retainCount)
|
||||
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String],
|
||||
mtlc.retainCount);
|
||||
}
|
||||
} else {
|
||||
if (![mtlc.shadersLib isEqualToString:mtlShadersLib]) {
|
||||
J2dRlsTraceLn3(J2D_TRACE_ERROR,
|
||||
"MTLContext_createContextWithDeviceIfAbsent: cannot reuse context(%p) for display=%d "
|
||||
"device=\"%s\", shaders lib has been changed", mtlc, displayID, [mtlc.device.name UTF8String])
|
||||
[device release];
|
||||
return nil;
|
||||
}
|
||||
[mtlc retain];
|
||||
J2dRlsTraceLn4(J2D_TRACE_INFO,
|
||||
"MTLContext_createContextWithDeviceIfAbsent: reuse context(%p) for display=%d device=\"%s\" "
|
||||
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String], mtlc.retainCount)
|
||||
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String],
|
||||
mtlc.retainCount);
|
||||
}
|
||||
if (shouldReleaseDevice) {
|
||||
[device release];
|
||||
}
|
||||
// (will start a new display link thread if needed):
|
||||
[mtlc createDisplayLinkIfAbsent:displayID];
|
||||
return mtlc;
|
||||
}
|
||||
|
||||
+ (void) releaseContext:(MTLContext*) mtlc {
|
||||
id<NSCopying> devID = nil;
|
||||
|
||||
id<NSCopying> deviceID = nil;
|
||||
if (@available(macOS 10.13, *)) {
|
||||
devID = @(mtlc.device.registryID);
|
||||
deviceID = @(mtlc.device.registryID);
|
||||
} else {
|
||||
devID = mtlc.device.name;
|
||||
deviceID = mtlc.device.name;
|
||||
}
|
||||
MTLContext* ctx = MTLContext.contextStore[devID];
|
||||
MTLContext* ctx = MTLContext.contextStore[deviceID];
|
||||
if (mtlc == ctx) {
|
||||
if (mtlc.retainCount > 1) {
|
||||
[mtlc release];
|
||||
J2dRlsTraceLn2(J2D_TRACE_INFO, "MTLContext_releaseContext: release context(%p) retainCount=%d", mtlc, mtlc.retainCount);
|
||||
} else {
|
||||
[MTLContext.contextStore removeObjectForKey:devID];
|
||||
// explicit halt redraw to shutdown CVDisplayLink threads before dealloc():
|
||||
[mtlc haltRedraw];
|
||||
// remove ownership (objc ref):
|
||||
[MTLContext.contextStore removeObjectForKey:deviceID];
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLContext_releaseContext: dealloc context(%p)", mtlc);
|
||||
}
|
||||
} else {
|
||||
J2dRlsTraceLn2(J2D_TRACE_ERROR, "MTLContext_releaseContext: cannot release context(%p) != context store(%p)",
|
||||
mtlc, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithDevice:(id<MTLDevice>)mtlDevice display:(jint) displayID shadersLib:(NSString*)mtlShadersLib {
|
||||
- (id)initWithDevice:(id<MTLDevice>)mtlDevice shadersLib:(NSString*)mtlShadersLib {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
device = mtlDevice;
|
||||
@@ -288,14 +410,13 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
blitCommandQueue = [device newCommandQueue];
|
||||
|
||||
_tempTransform = [[MTLTransform alloc] init];
|
||||
_displayLinkCount = 0;
|
||||
_lastDisplayLinkTime = 0;
|
||||
_avgDisplayLinkTime = 0;
|
||||
_lastRedrawTime = 0.0;
|
||||
_lastStatTime = 0.0;
|
||||
|
||||
if (isDisplaySyncEnabled()) {
|
||||
_displayLinks = [[NSMutableDictionary alloc] init];
|
||||
_displayLinkStates = [[NSMutableDictionary alloc] init];
|
||||
_layers = [[NSMutableSet alloc] init];
|
||||
} else {
|
||||
_displayLinkStates = nil;
|
||||
_layers = nil;
|
||||
}
|
||||
_glyphCacheLCD = [[MTLGlyphCache alloc] initWithContext:self];
|
||||
_glyphCacheAA = [[MTLGlyphCache alloc] initWithContext:self];
|
||||
@@ -397,67 +518,140 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber*>*)getDisplayLinkDisplayIds {
|
||||
return [_displayLinkStates allKeys];
|
||||
}
|
||||
|
||||
- (void)createDisplayLinkIfAbsent: (jint)displayID {
|
||||
if (isDisplaySyncEnabled() && _displayLinks[@(displayID)] == nil) {
|
||||
|
||||
if (isDisplaySyncEnabled()) {
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if ((dlState != nil) && (dlState->displayLink != nil)) {
|
||||
return;
|
||||
}
|
||||
if (TRACE_DISPLAY) {
|
||||
[MTLContext dumpDisplayInfo:displayID];
|
||||
}
|
||||
|
||||
CVDisplayLinkRef _displayLink;
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_createDisplayLinkIfAbsent: "
|
||||
"ctx=%p displayID=%d", self, displayID);
|
||||
}
|
||||
CHECK_CVLINK("CreateWithCGDisplay", nil,
|
||||
CHECK_CVLINK("CreateWithCGDisplay", nil, &_displayLink,
|
||||
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink));
|
||||
|
||||
if (_displayLink == nil) {
|
||||
J2dRlsTraceLn(J2D_TRACE_ERROR,
|
||||
"MTLContext_createDisplayLinkIfAbsent: Failed to initialize CVDisplayLink.");
|
||||
} else {
|
||||
DLParams* dlParams = malloc(sizeof (DLParams ));
|
||||
dlParams->displayID = displayID;
|
||||
dlParams->displayLink = _displayLink;
|
||||
dlParams->mtlc = self;
|
||||
_displayLinks[@(displayID)] = [NSValue valueWithPointer:dlParams];
|
||||
CHECK_CVLINK("SetOutputCallback", nil,
|
||||
J2dRlsTraceLn3(J2D_TRACE_INFO, "MTLContext_destroyDisplayLinkMTLContext_createDisplayLinkIfAbsent["
|
||||
"ctx=%p displayID=%d] displayLink=%p",
|
||||
self, displayID, _displayLink);
|
||||
bool isNewDisplayLink = false;
|
||||
if (dlState == nil) {
|
||||
dlState = malloc(sizeof(MTLDisplayLinkState));
|
||||
isNewDisplayLink = true;
|
||||
}
|
||||
// update:
|
||||
dlState->displayID = displayID;
|
||||
dlState->displayLink = _displayLink;
|
||||
dlState->mtlc = self;
|
||||
|
||||
dlState->redrawCount = 0;
|
||||
dlState->avgDisplayLinkSamples = 0;
|
||||
dlState->lastRedrawTime = 0.0;
|
||||
dlState->lastDisplayLinkTime = 0.0;
|
||||
dlState->avgDisplayLinkTime = 0.0;
|
||||
dlState->lastStatTime = 0.0;
|
||||
|
||||
if (isNewDisplayLink) {
|
||||
// publish fully initialized object:
|
||||
_displayLinkStates[@(displayID)] = [NSValue valueWithPointer:dlState];
|
||||
}
|
||||
|
||||
CHECK_CVLINK("SetOutputCallback", nil, &_displayLink,
|
||||
CVDisplayLinkSetOutputCallback(_displayLink, &mtlDisplayLinkCallback,
|
||||
(__bridge DLParams*) dlParams));
|
||||
(__bridge MTLDisplayLinkState*) dlState));
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void)handleDisplayLink: (BOOL)enabled display:(jint) display source:(const char*)src {
|
||||
if (_displayLinks == nil) {
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_handleDisplayLink[%s]: "
|
||||
"ctx=%p - displayLinks = nil", src, self);
|
||||
|
||||
- (NSValue*)getDisplayLinkState: (jint)displayID {
|
||||
if (_displayLinkStates == nil) {
|
||||
if (TRACE_CVLINK_WARN) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_getDisplayLinkState[ctx=%p displayID=%d]: "
|
||||
"displayLinkStates is nil!", self, displayID);
|
||||
}
|
||||
} else {
|
||||
NSValue* dlParamsVal = _displayLinks[@(display)];
|
||||
if (dlParamsVal == nil) {
|
||||
J2dRlsTraceLn3(J2D_TRACE_ERROR, "MTLContext_handleDisplayLink[%s]: "
|
||||
"ctx=%p, no display link for %d ", src, self, display);
|
||||
return nil;
|
||||
}
|
||||
NSValue* dlStateVal = _displayLinkStates[@(displayID)];
|
||||
if (dlStateVal == nil) {
|
||||
if (TRACE_CVLINK_WARN) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_ERROR, "MTLContext_getDisplayLinkState[ctx=%p displayID=%d]: "
|
||||
"dlState is nil!", self, displayID);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
return [dlStateVal pointerValue];
|
||||
}
|
||||
|
||||
- (void)destroyDisplayLink: (jint)displayID {
|
||||
if (isDisplaySyncEnabled()) {
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if (dlState == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
DLParams *dlParams = [dlParamsVal pointerValue];
|
||||
CVDisplayLinkRef _displayLink = dlParams->displayLink;
|
||||
if (enabled) {
|
||||
if (!CVDisplayLinkIsRunning(_displayLink)) {
|
||||
CHECK_CVLINK("Start", src, CVDisplayLinkStart(_displayLink));
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStart[%s]: "
|
||||
"ctx=%p", src, self);
|
||||
}
|
||||
CVDisplayLinkRef _displayLink = dlState->displayLink;
|
||||
if (_displayLink == nil) {
|
||||
return;
|
||||
}
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_destroyDisplayLink: "
|
||||
"ctx=%p, displayID=%d", self, displayID);
|
||||
}
|
||||
if (CVDisplayLinkIsRunning(_displayLink)) {
|
||||
CHECK_CVLINK("Stop", "destroyDisplayLink", &_displayLink,
|
||||
CVDisplayLinkStop(_displayLink));
|
||||
}
|
||||
|
||||
J2dRlsTraceLn3(J2D_TRACE_INFO, "MTLContext_destroyDisplayLink["
|
||||
"ctx=%p displayID=%d] displayLink=%p",
|
||||
self, displayID, _displayLink);
|
||||
// Release display link thread:
|
||||
CVDisplayLinkRelease(_displayLink);
|
||||
dlState->displayLink = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleDisplayLink: (BOOL)enabled displayID:(jint)displayID source:(const char*)src {
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if (dlState == nil) {
|
||||
return;
|
||||
}
|
||||
CVDisplayLinkRef _displayLink = dlState->displayLink;
|
||||
if (_displayLink == nil) {
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_handleDisplayLink[%s]: "
|
||||
"displayLink is nil (disabled).", src);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (enabled) {
|
||||
if (!CVDisplayLinkIsRunning(_displayLink)) {
|
||||
CHECK_CVLINK("Start", src, &_displayLink,
|
||||
CVDisplayLinkStart(_displayLink));
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStart[%s]: "
|
||||
"ctx=%p", src, self);
|
||||
}
|
||||
} else {
|
||||
if (CVDisplayLinkIsRunning(_displayLink)) {
|
||||
CHECK_CVLINK("Stop", src, CVDisplayLinkStop(_displayLink));
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStop[%s]: "
|
||||
"ctx=%p", src, self);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (CVDisplayLinkIsRunning(_displayLink)) {
|
||||
CHECK_CVLINK("Stop", src, &_displayLink,
|
||||
CVDisplayLinkStop(_displayLink));
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkStop[%s]: "
|
||||
"ctx=%p", src, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,8 +660,10 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
- (void)dealloc {
|
||||
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.dealloc");
|
||||
|
||||
if (_displayLinks != nil) {
|
||||
if (_displayLinkStates != nil) {
|
||||
[self haltRedraw];
|
||||
[_displayLinkStates release];
|
||||
_displayLinkStates = nil;
|
||||
}
|
||||
|
||||
[shadersLib release];
|
||||
@@ -538,7 +734,7 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
|
||||
- (MTLCommandBufferWrapper *) getCommandBufferWrapper {
|
||||
if (_commandBufferWrapper == nil) {
|
||||
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext : commandBuffer is NULL");
|
||||
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext : commandBuffer is nil!");
|
||||
// NOTE: Command queues are thread-safe and allow multiple outstanding command buffers to be encoded simultaneously.
|
||||
_commandBufferWrapper = [[MTLCommandBufferWrapper alloc] initWithCommandBuffer:[self.commandQueue commandBuffer]];// released in [layer blitTexture]
|
||||
}
|
||||
@@ -814,7 +1010,7 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
MTLCommandBufferWrapper * cbwrapper = [self pullCommandBufferWrapper];
|
||||
if (cbwrapper != nil) {
|
||||
id <MTLCommandBuffer> commandbuf =[cbwrapper getCommandBuffer];
|
||||
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
|
||||
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> cb) {
|
||||
[cbwrapper release];
|
||||
}];
|
||||
[commandbuf commit];
|
||||
@@ -825,94 +1021,169 @@ extern void initSamplers(id<MTLDevice> device);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) redraw:(NSNumber*)displayIDNum {
|
||||
- (void) redraw:(NSNumber*)displayIDNumber {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
const CFTimeInterval now = CACurrentMediaTime();
|
||||
|
||||
const jint displayID = [displayIDNumber intValue];
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if (dlState == nil) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* 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) : -1.0;
|
||||
const CFTimeInterval elapsed = (dlState->lastRedrawTime != 0.0) ? (now - dlState->lastRedrawTime) : -1.0;
|
||||
|
||||
if ((elapsed >= 0.0) && (elapsed <= KEEP_ALIVE_MIN_INTERVAL)) {
|
||||
CFTimeInterval threshold = (dlState->avgDisplayLinkSamples >= 10) ?
|
||||
(dlState->avgDisplayLinkTime / 20.0) : KEEP_ALIVE_MIN_INTERVAL;
|
||||
|
||||
if (threshold < KEEP_ALIVE_MIN_INTERVAL) {
|
||||
threshold = KEEP_ALIVE_MIN_INTERVAL;
|
||||
}
|
||||
|
||||
if ((elapsed >= 0.0) && (elapsed <= threshold)) {
|
||||
if (TRACE_CVLINK) {
|
||||
NSLog(@"MTLContext_redraw[displayID: %d]: %.3f < %.3f ms, skip redraw.",
|
||||
displayID, TO_MS(elapsed), TO_MS(threshold));
|
||||
}
|
||||
return;
|
||||
}
|
||||
_lastRedrawTime = now;
|
||||
if (TRACE_CVLINK_DEBUG) {
|
||||
NSLog(@"MTLContext_redraw[displayID: %d]: elapsed: %.3f ms (> %.3f)",
|
||||
displayID, TO_MS(elapsed), TO_MS(threshold));
|
||||
}
|
||||
dlState->lastRedrawTime = now;
|
||||
|
||||
// Process layers:
|
||||
for (MTLLayer *layer in _layers) {
|
||||
[layer setNeedsDisplay];
|
||||
}
|
||||
if (_displayLinkCount > 0) {
|
||||
_displayLinkCount--;
|
||||
} else {
|
||||
if (_layers.count > 0) {
|
||||
[_layers removeAllObjects];
|
||||
if (layer.displayID == displayID) {
|
||||
[layer setNeedsDisplay];
|
||||
}
|
||||
[self handleDisplayLink:NO display:[displayIDNum integerValue] source:"redraw"];
|
||||
}
|
||||
if (dlState->redrawCount > 0) {
|
||||
dlState->redrawCount--;
|
||||
} else {
|
||||
// dlState->redrawCount == 0:
|
||||
if (_layers.count > 0) {
|
||||
for (MTLLayer *layer in _layers.allObjects) {
|
||||
if (layer.displayID == displayID) {
|
||||
[_layers removeObject:layer];
|
||||
}
|
||||
}
|
||||
}
|
||||
[self handleDisplayLink:NO displayID:displayID source:"redraw"];
|
||||
}
|
||||
}
|
||||
|
||||
CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* nowTime, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
|
||||
{
|
||||
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_mtlDisplayLinkCallback: ctx=%p", displayLinkContext);
|
||||
@autoreleasepool {
|
||||
const DLParams* dlParams = (__bridge DLParams* *)displayLinkContext;
|
||||
const MTLContext* mtlc = dlParams->mtlc;
|
||||
const jint displayID = dlParams->displayID;
|
||||
CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* nowTime, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) {
|
||||
JNI_COCOA_ENTER(env);
|
||||
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_mtlDisplayLinkCallback: ctx=%p", displayLinkContext);
|
||||
|
||||
MTLDisplayLinkState *dlState = (__bridge MTLDisplayLinkState*) displayLinkContext;
|
||||
if (dlState == nil) {
|
||||
if (TRACE_CVLINK_WARN) {
|
||||
NSLog(@"MTLContext_mtlDisplayLinkCallback: dlState is nil!");
|
||||
}
|
||||
return kCVReturnError;
|
||||
}
|
||||
const MTLContext *mtlc = dlState->mtlc;
|
||||
const jint displayID = dlState->displayID;
|
||||
|
||||
if (STATS_CVLINK) {
|
||||
const CFTimeInterval now = outputTime->videoTime / (double)outputTime->videoTimeScale; // seconds
|
||||
const CFTimeInterval delta = (mtlc->_lastDisplayLinkTime != 0.0) ? (now - mtlc->_lastDisplayLinkTime) : -1.0;
|
||||
mtlc->_lastDisplayLinkTime = now;
|
||||
const CFTimeInterval now = outputTime->videoTime / (double) outputTime->videoTimeScale; // seconds
|
||||
const CFTimeInterval delta = (dlState->lastDisplayLinkTime != 0.0) ? (now - dlState->lastDisplayLinkTime)
|
||||
: -1.0;
|
||||
dlState->lastDisplayLinkTime = now;
|
||||
|
||||
const NSTimeInterval a = 1.0 / 30.0; // 60 fps typically => exponential smoothing on 0.5s:
|
||||
mtlc->_avgDisplayLinkTime = delta * a + mtlc->_avgDisplayLinkTime * (1.0 - a);
|
||||
dlState->avgDisplayLinkSamples++;
|
||||
dlState->avgDisplayLinkTime = EXP_AVG_WEIGHT * delta + EXP_INV_WEIGHT * dlState->avgDisplayLinkTime;
|
||||
|
||||
if (mtlc->_lastStatTime == 0.0) {
|
||||
mtlc->_lastStatTime = now;
|
||||
} else if ((now - mtlc->_lastStatTime) > 1.0) {
|
||||
mtlc->_lastStatTime = now;
|
||||
if (dlState->lastStatTime == 0.0) {
|
||||
dlState->lastStatTime = now;
|
||||
} else if ((now - dlState->lastStatTime) > 1.0) {
|
||||
dlState->lastStatTime = now;
|
||||
// dump stats:
|
||||
NSLog(@"mtlDisplayLinkCallback[%d]: avgDisplayLinkTime = %.3lf ms", displayID,
|
||||
1000.0 * mtlc->_avgDisplayLinkTime);
|
||||
NSLog(@"mtlDisplayLinkCallback[displayID: %d]: avg interval = %.3lf ms (%.1lf fps) on %d samples", displayID,
|
||||
TO_MS(dlState->avgDisplayLinkTime), TO_FPS(dlState->avgDisplayLinkTime), dlState->avgDisplayLinkSamples);
|
||||
}
|
||||
}
|
||||
|
||||
[ThreadUtilities performOnMainThread:@selector(redraw:) on:mtlc withObject:@(displayID)
|
||||
waitUntilDone:NO useJavaModes:NO]; // critical
|
||||
}
|
||||
const BOOL active = mtlc_IsDisplayReallyActive(displayID);
|
||||
if (TRACE_CVLINK_DEBUG) {
|
||||
NSLog(@"MTLContext_mtlDisplayLinkCallback: ctx=%p displayID=%d active=%d (display link = %p)",
|
||||
mtlc, displayID, active, dlState->displayLink);
|
||||
}
|
||||
if (!active) {
|
||||
if (TRACE_CVLINK) {
|
||||
NSLog(@"MTLContext_mtlDisplayLinkCallback: displayId=%d active=%d "
|
||||
"=> destroyDisplayLink", displayID, active);
|
||||
}
|
||||
if (TRACE_DISPLAY) {
|
||||
[MTLContext dumpDisplayInfo:displayID];
|
||||
}
|
||||
[mtlc destroyDisplayLink:displayID];
|
||||
} else {
|
||||
[ThreadUtilities performOnMainThread:@selector(redraw:) on:mtlc withObject:@(displayID)
|
||||
waitUntilDone:NO useJavaModes:NO]; // critical
|
||||
}
|
||||
JNI_COCOA_EXIT(env);
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
- (void)startRedraw:(MTLLayer*)layer {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
layer.redrawCount = REDRAW_COUNT;
|
||||
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_startRedraw: ctx=%p layer=%p", self, layer);
|
||||
_displayLinkCount = KEEP_ALIVE_COUNT;
|
||||
const jint displayID = layer.displayID;
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if (dlState == nil) {
|
||||
return;
|
||||
}
|
||||
dlState->redrawCount = KEEP_ALIVE_COUNT;
|
||||
|
||||
layer.redrawCount = REDRAW_COUNT;
|
||||
[_layers addObject:layer];
|
||||
|
||||
if (MTLLayer_isExtraRedrawEnabled()) {
|
||||
// Request for redraw before starting display link to avoid rendering problem on M2 processor
|
||||
[layer setNeedsDisplay];
|
||||
}
|
||||
[self handleDisplayLink:YES display:layer.displayID source:"startRedraw"];
|
||||
[self handleDisplayLink:YES displayID:displayID source:"startRedraw"];
|
||||
}
|
||||
|
||||
- (void)stopRedraw:(MTLLayer*) layer {
|
||||
[self stopRedraw:layer.displayID layer:layer];
|
||||
}
|
||||
|
||||
- (void)stopRedraw:(jint)displayID layer:(MTLLayer*)layer {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_stopRedraw: ctx=%p layer=%p", self, layer);
|
||||
if (_displayLinks != nil) {
|
||||
if (--layer.redrawCount <= 0) {
|
||||
[_layers removeObject:layer];
|
||||
layer.redrawCount = 0;
|
||||
}
|
||||
if ((_layers.count == 0) && (_displayLinkCount == 0)) {
|
||||
[self handleDisplayLink:NO display:layer.displayID source:"stopRedraw"];
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "MTLContext_stopRedraw: ctx=%p displayID=%d layer=%p",
|
||||
self, displayID, layer);
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if (dlState == nil) {
|
||||
return;
|
||||
}
|
||||
if (--layer.redrawCount <= 0) {
|
||||
[_layers removeObject:layer];
|
||||
layer.redrawCount = 0;
|
||||
}
|
||||
bool hasLayers = false;
|
||||
if (_layers.count > 0) {
|
||||
for (MTLLayer *l in _layers) {
|
||||
if (l.displayID == displayID) {
|
||||
hasLayers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasLayers && (dlState->redrawCount == 0)) {
|
||||
[self handleDisplayLink:NO displayID:displayID source:"stopRedraw"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)haltRedraw {
|
||||
if (_displayLinks != nil) {
|
||||
if (_displayLinkStates != nil) {
|
||||
if (TRACE_CVLINK) {
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_haltRedraw: ctx=%p", self);
|
||||
}
|
||||
@@ -922,17 +1193,19 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
|
||||
}
|
||||
[_layers removeAllObjects];
|
||||
}
|
||||
_displayLinkCount = 0;
|
||||
NSEnumerator<NSNumber*>* keyEnum = _displayLinks.keyEnumerator;
|
||||
NSNumber* displayIDVal;
|
||||
while ((displayIDVal = [keyEnum nextObject])) {
|
||||
[self handleDisplayLink:NO display:[displayIDVal integerValue] source:"haltRedraw"];
|
||||
DLParams *dlParams = [(NSValue*)_displayLinks[displayIDVal] pointerValue];
|
||||
CVDisplayLinkRelease(dlParams->displayLink);
|
||||
free(dlParams);
|
||||
if (_displayLinkStates.count > 0) {
|
||||
for (NSNumber* displayIDVal in [self getDisplayLinkDisplayIds]) {
|
||||
const jint displayID = [displayIDVal intValue];
|
||||
[self destroyDisplayLink:displayID];
|
||||
|
||||
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
|
||||
if (dlState != nil) {
|
||||
// Remove reference:
|
||||
[_displayLinkStates removeObjectForKey:@(displayID)];
|
||||
free(dlState);
|
||||
}
|
||||
}
|
||||
}
|
||||
[_displayLinks release];
|
||||
_displayLinks = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
- (void) startRedraw;
|
||||
- (void) startRedrawIfNeeded;
|
||||
- (void) stopRedraw:(BOOL)force;
|
||||
- (void) stopRedraw:(MTLContext*)mtlc displayID:(jint)displayID force:(BOOL)force;
|
||||
- (void) flushBuffer;
|
||||
- (void) commitCommandBuffer:(MTLContext*)mtlc wait:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
|
||||
- (void) countFramePresentedCallback;
|
||||
|
||||
@@ -39,7 +39,8 @@ static jclass jc_JavaLayer = NULL;
|
||||
#define GET_MTL_LAYER_CLASS() \
|
||||
GET_CLASS(jc_JavaLayer, "sun/java2d/metal/MTLLayer");
|
||||
|
||||
#define TRACE_DISPLAY 0
|
||||
#define TRACE_DISPLAY 0
|
||||
#define TRACE_DISPLAY_CHANGED 0
|
||||
|
||||
const NSTimeInterval DF_BLIT_FRAME_TIME=1.0/120.0;
|
||||
|
||||
@@ -120,7 +121,8 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
if (self == nil) return self;
|
||||
|
||||
self.javaLayer = layer;
|
||||
|
||||
self.ctx = nil;
|
||||
self.displayID = -1;
|
||||
self.contentsGravity = kCAGravityTopLeft;
|
||||
|
||||
//Disable CALayer's default animation
|
||||
@@ -417,14 +419,20 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
|
||||
}
|
||||
|
||||
- (void)stopRedraw:(BOOL)force {
|
||||
if (isDisplaySyncEnabled()) {
|
||||
[self stopRedraw:self.ctx displayID:self.displayID force:force];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) stopRedraw:(MTLContext*)mtlc displayID:(jint)displayID force:(BOOL)force {
|
||||
if (isDisplaySyncEnabled()) {
|
||||
if (force) {
|
||||
self.redrawCount = 0;
|
||||
}
|
||||
if (self.ctx != nil) {
|
||||
if (mtlc != nil) {
|
||||
[ThreadUtilities performOnMainThreadNowOrLater:NO // critical
|
||||
block:^(){
|
||||
[self.ctx stopRedraw:self];
|
||||
[mtlc stopRedraw:displayID layer:self];
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -557,9 +565,31 @@ Java_sun_java2d_metal_MTLLayer_validate
|
||||
BMTLSDOps *bmtlsdo = (BMTLSDOps*) SurfaceData_GetOps(env, surfaceData);
|
||||
layer.buffer = &bmtlsdo->pTexture;
|
||||
layer.outBuffer = &bmtlsdo->pOutTexture;
|
||||
layer.ctx = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->context;
|
||||
layer.displayID = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->displayID;
|
||||
layer.device = layer.ctx.device;
|
||||
|
||||
// Backup layer's context (device) and displayId to unregister if needed:
|
||||
MTLContext* oldMtlc = layer.ctx;
|
||||
NSInteger oldDisplayID = layer.displayID;
|
||||
|
||||
MTLContext* newMtlc = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->context;
|
||||
NSInteger newDisplayID = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->displayID;
|
||||
|
||||
if (isDisplaySyncEnabled()) {
|
||||
if (oldDisplayID != -1) {
|
||||
if ((oldMtlc != newMtlc) || (oldDisplayID != newDisplayID)) {
|
||||
if (TRACE_DISPLAY_CHANGED) {
|
||||
J2dRlsTraceLn5(J2D_TRACE_INFO, "MTLLayer_validate: layer[%p] mtlc/displayID changed: "
|
||||
"[%p - %d] => [%p - %d]",
|
||||
layer, oldMtlc, oldDisplayID, newMtlc, newDisplayID);
|
||||
}
|
||||
// unregister with display link on old mtlc/display before updating layer's state:
|
||||
[layer stopRedraw:oldMtlc displayID:oldDisplayID force:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update layer's context (device) and displayId:
|
||||
layer.ctx = newMtlc;
|
||||
layer.device = layer.ctx.device;
|
||||
layer.displayID = newDisplayID;
|
||||
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
if (!isColorMatchingEnabled() && (layer.colorspace != nil)) {
|
||||
@@ -570,6 +600,7 @@ Java_sun_java2d_metal_MTLLayer_validate
|
||||
layer.drawableSize =
|
||||
CGSizeMake((*layer.buffer).width,
|
||||
(*layer.buffer).height);
|
||||
|
||||
if (isDisplaySyncEnabled()) {
|
||||
[layer startRedraw];
|
||||
} else {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define __THREADUTILITIES_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <stdatomic.h>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
@@ -175,14 +176,21 @@ __attribute__((visibility("default")))
|
||||
+ (NSString*)criticalRunLoopMode;
|
||||
+ (NSString*)javaRunLoopMode;
|
||||
|
||||
+ (ThreadTraceContext*) getTraceContext;
|
||||
+ (void) removeTraceContext;
|
||||
+ (void) resetTraceContext;
|
||||
+ (ThreadTraceContext*)getTraceContext;
|
||||
+ (void)removeTraceContext;
|
||||
+ (void)resetTraceContext;
|
||||
|
||||
+ (ThreadTraceContext*)recordTraceContext;
|
||||
+ (ThreadTraceContext*)recordTraceContext:(NSString*) prefix;
|
||||
+ (ThreadTraceContext*)recordTraceContext:(NSString*)prefix;
|
||||
+ (ThreadTraceContext*)recordTraceContext:(NSString*)prefix actionId:(long)actionId useJavaModes:(BOOL)useJavaModes operation:(char*) operation;
|
||||
|
||||
+ (NSString*)getThreadTraceContexts;
|
||||
|
||||
+ (void)registerForSystemAndScreenNotifications;
|
||||
+ (BOOL)isWithinPowerTransition;
|
||||
|
||||
+ (BOOL)nanoUpTime:(atomic_uint_least64_t*)nanotime;
|
||||
+ (BOOL)nowNearTime:(NSString*)src refTime:(atomic_uint_least64_t*)refTime;
|
||||
@end
|
||||
|
||||
JNIEXPORT void OSXAPP_SetJavaVM(JavaVM *vm);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <objc/message.h>
|
||||
#import <stdatomic.h>
|
||||
|
||||
#import "JNIUtilities.h"
|
||||
#import "PropertiesUtilities.h"
|
||||
@@ -62,6 +61,7 @@ JavaVM *jvm = NULL;
|
||||
static BOOL isNSApplicationOwner = NO;
|
||||
static JNIEnv *appKitEnv = NULL;
|
||||
static jobject appkitThreadGroup = NULL;
|
||||
|
||||
static NSString* CriticalRunLoopMode = @"AWTCriticalRunLoopMode";
|
||||
static NSString* JavaRunLoopMode = @"AWTRunLoopMode";
|
||||
static NSArray<NSString*> *javaModes = nil;
|
||||
@@ -69,13 +69,28 @@ static NSArray<NSString*> *allModesExceptJava = nil;
|
||||
|
||||
/* Traceability data */
|
||||
static const BOOL enableTracing = NO;
|
||||
static const BOOL enableTracingLog = NO;
|
||||
static const BOOL enableCallStacks = NO;
|
||||
static const BOOL enableRunLoopObserver = NO;
|
||||
|
||||
/* Traceability data */
|
||||
static const BOOL TRACE_PWM = NO;
|
||||
static const BOOL TRACE_PWM_EVENTS = NO;
|
||||
static const BOOL TRACE_CLOCKS = NO;
|
||||
|
||||
/* 10s period arround reference times (sleep/wake-up...)
|
||||
* to ensure all displays are awaken properly */
|
||||
static const uint64_t NANOS_PER_SEC = 1000000000ULL;
|
||||
static const uint64_t RDV_PERIOD = 10ULL * NANOS_PER_SEC;
|
||||
|
||||
/* RunLoop traceability identifier generators */
|
||||
static atomic_long runLoopId = 0L;
|
||||
static atomic_long mainThreadActionId = 0L;
|
||||
|
||||
static atomic_uint_least64_t sleepTime = 0LL;
|
||||
static atomic_uint_least64_t wakeUpTime = 0LL;
|
||||
|
||||
|
||||
static inline void attachCurrentThread(void** env) {
|
||||
if ([NSThread isMainThread]) {
|
||||
JavaVMAttachArgs args;
|
||||
@@ -157,66 +172,71 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
if (enableTracing) {
|
||||
// Record thread stack now and return another copy (auto-released):
|
||||
[ThreadUtilities recordTraceContext];
|
||||
|
||||
if (enableRunLoopObserver) {
|
||||
CFRunLoopObserverRef logObserver = CFRunLoopObserverCreateWithHandler(
|
||||
NULL, // CFAllocator
|
||||
kCFRunLoopAllActivities, // CFOptionFlags
|
||||
true, // repeats
|
||||
NSIntegerMin, // order = max priority
|
||||
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
|
||||
if ([[NSThread currentThread] isMainThread]) {
|
||||
char* activityName = NULL;
|
||||
switch (activity) {
|
||||
default:
|
||||
break;
|
||||
case kCFRunLoopEntry:
|
||||
activityName = "RunLoopEntry";
|
||||
/* Increment global main RunLoop id */
|
||||
runLoopId++;
|
||||
break;
|
||||
case kCFRunLoopBeforeTimers:
|
||||
activityName = "RunLoopBeforeTimers";
|
||||
break;
|
||||
case kCFRunLoopBeforeSources :
|
||||
activityName = "RunLoopBeforeSources";
|
||||
break;
|
||||
case kCFRunLoopBeforeWaiting:
|
||||
activityName = "RunLoopBeforeWaiting";
|
||||
break;
|
||||
case kCFRunLoopAfterWaiting:
|
||||
activityName = "RunLoopAfterWaiting";
|
||||
break;
|
||||
case kCFRunLoopExit:
|
||||
activityName = "RunLoopExit";
|
||||
break;
|
||||
case kCFRunLoopAllActivities:
|
||||
activityName = "RunLoopAllActivities";
|
||||
break;
|
||||
@try {
|
||||
if (enableRunLoopObserver) {
|
||||
CFRunLoopObserverRef logObserver = CFRunLoopObserverCreateWithHandler(
|
||||
NULL, // CFAllocator
|
||||
kCFRunLoopAllActivities, // CFOptionFlags
|
||||
true, // repeats
|
||||
NSIntegerMin, // order = max priority
|
||||
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
|
||||
if ([[NSThread currentThread] isMainThread]) {
|
||||
char *activityName = NULL;
|
||||
switch (activity) {
|
||||
default:
|
||||
break;
|
||||
case kCFRunLoopEntry:
|
||||
activityName = "RunLoopEntry";
|
||||
/* Increment global main RunLoop id */
|
||||
runLoopId++;
|
||||
break;
|
||||
case kCFRunLoopBeforeTimers:
|
||||
activityName = "RunLoopBeforeTimers";
|
||||
break;
|
||||
case kCFRunLoopBeforeSources :
|
||||
activityName = "RunLoopBeforeSources";
|
||||
break;
|
||||
case kCFRunLoopBeforeWaiting:
|
||||
activityName = "RunLoopBeforeWaiting";
|
||||
break;
|
||||
case kCFRunLoopAfterWaiting:
|
||||
activityName = "RunLoopAfterWaiting";
|
||||
break;
|
||||
case kCFRunLoopExit:
|
||||
activityName = "RunLoopExit";
|
||||
break;
|
||||
case kCFRunLoopAllActivities:
|
||||
activityName = "RunLoopAllActivities";
|
||||
break;
|
||||
}
|
||||
if (activityName != NULL) {
|
||||
NSLog(@"RunLoop[on %s][%lu]: processing %s on mode = '%@'",
|
||||
NSThread.currentThread.name.UTF8String, runLoopId, activityName,
|
||||
NSRunLoop.currentRunLoop.currentMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activityName != NULL) {
|
||||
NSLog(@"RunLoop[on %s][%lu]: processing %s on mode = '%@'",
|
||||
NSThread.currentThread.name.UTF8String, runLoopId, activityName,
|
||||
NSRunLoop.currentRunLoop.currentMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
CFRunLoopRef runLoop = [[NSRunLoop mainRunLoop] getCFRunLoop];
|
||||
CFRunLoopAddObserver(runLoop, logObserver, kCFRunLoopDefaultMode);
|
||||
CFRunLoopRef runLoop = [[NSRunLoop mainRunLoop] getCFRunLoop];
|
||||
CFRunLoopAddObserver(runLoop, logObserver, kCFRunLoopDefaultMode);
|
||||
|
||||
CFStringRef criticalModeRef = (__bridge CFStringRef)CriticalRunLoopMode;
|
||||
CFRunLoopAddObserver(runLoop, logObserver, criticalModeRef);
|
||||
CFStringRef criticalModeRef = (__bridge CFStringRef) CriticalRunLoopMode;
|
||||
CFRunLoopAddObserver(runLoop, logObserver, criticalModeRef);
|
||||
|
||||
CFStringRef javaModeRef = (__bridge CFStringRef)JavaRunLoopMode;
|
||||
CFRunLoopAddObserver(runLoop, logObserver, javaModeRef);
|
||||
CFStringRef javaModeRef = (__bridge CFStringRef) JavaRunLoopMode;
|
||||
CFRunLoopAddObserver(runLoop, logObserver, javaModeRef);
|
||||
|
||||
CFRelease(javaModeRef);
|
||||
CFRelease(criticalModeRef);
|
||||
CFRelease(logObserver);
|
||||
CFRelease(javaModeRef);
|
||||
CFRelease(criticalModeRef);
|
||||
CFRelease(logObserver);
|
||||
}
|
||||
} @finally {
|
||||
// Finally reset Main thread context in context store:
|
||||
[ThreadUtilities resetTraceContext];
|
||||
}
|
||||
}
|
||||
[ThreadUtilities registerForSystemAndScreenNotifications];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -226,8 +246,8 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
* Note : if waiting cross-thread, possibly the stack allocated copy is accessible ?
|
||||
*/
|
||||
+ (void)invokeBlockCopy:(void (^)(void))blockCopy {
|
||||
blockCopy();
|
||||
Block_release(blockCopy);
|
||||
blockCopy();
|
||||
Block_release(blockCopy);
|
||||
}
|
||||
|
||||
+ (NSString*)getCaller:(NSString*)prefixSymbol {
|
||||
@@ -403,11 +423,12 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
callerCtx = [ThreadUtilities recordTraceContext:nil actionId:actionId useJavaModes:useJavaModes operation:operation];
|
||||
[callerCtx retain];
|
||||
|
||||
lwc_plog(env, "%s performOnMainThread[caller]", toCString([callerCtx description]));
|
||||
|
||||
if ([callerCtx callStack] != nil) {
|
||||
lwc_plog(env, "%s performOnMainThread[caller]: call stack:\n%s",
|
||||
[callerCtx identifier], toCString([callerCtx callStack]));
|
||||
if (enableTracingLog) {
|
||||
lwc_plog(env, "%s performOnMainThread[caller]", toCString([callerCtx description]));
|
||||
if ([callerCtx callStack] != nil) {
|
||||
lwc_plog(env, "%s performOnMainThread[caller]: call stack:\n%s",
|
||||
[callerCtx identifier], toCString([callerCtx callStack]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,14 +448,16 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
// Record thread stack now and return another copy (auto-released):
|
||||
runCtx = [ThreadUtilities recordTraceContext];
|
||||
|
||||
const double latencyMs = ([runCtx timestamp] - [callerCtx timestamp]) * 1000.0;
|
||||
if (enableTracingLog) {
|
||||
const double latencyMs = ([runCtx timestamp] - [callerCtx timestamp]) * 1000.0;
|
||||
|
||||
lwc_plog(runEnv, "%s performOnMainThread[blockCopy]: latency = %.3lf ms. Calling: [%s]",
|
||||
[callerCtx identifier], latencyMs, aSelector);
|
||||
lwc_plog(runEnv, "%s performOnMainThread[blockCopy]: latency = %.5lf ms. Calling: [%s]",
|
||||
[callerCtx identifier], latencyMs, aSelector);
|
||||
|
||||
if ([runCtx callStack] != nil) {
|
||||
lwc_plog(runEnv, "%s performOnMainThread[blockCopy]: run stack:\n%s",
|
||||
[callerCtx identifier], toCString([runCtx callStack]));
|
||||
if ([runCtx callStack] != nil) {
|
||||
lwc_plog(runEnv, "%s performOnMainThread[blockCopy]: run stack:\n%s",
|
||||
[callerCtx identifier], toCString([runCtx callStack]));
|
||||
}
|
||||
}
|
||||
}
|
||||
const CFTimeInterval start = (enableTracing) ? CACurrentMediaTime() : 0.0;
|
||||
@@ -448,10 +471,12 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
setBlockingEventDispatchThread(NO);
|
||||
}
|
||||
if (enableTracing) {
|
||||
const double elapsedMs = (CACurrentMediaTime() - start) * 1000.0;
|
||||
if (elapsedMs > mtThreshold) {
|
||||
lwc_plog(runEnv, "%s performOnMainThread[blockCopy]: time = %.3lf ms. Caller=[%s]",
|
||||
[callerCtx identifier], elapsedMs, toCString([callerCtx caller]));
|
||||
if (enableTracingLog) {
|
||||
const double elapsedMs = (CACurrentMediaTime() - start) * 1000.0;
|
||||
if (elapsedMs > mtThreshold) {
|
||||
lwc_plog(runEnv, "%s performOnMainThread[blockCopy]: time = %.5lf ms. Caller=[%s]",
|
||||
[callerCtx identifier], elapsedMs, toCString([callerCtx caller]));
|
||||
}
|
||||
}
|
||||
[callerCtx release];
|
||||
|
||||
@@ -464,7 +489,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
if (invokeDirect) {
|
||||
[ThreadUtilities invokeBlockCopy:blockCopy];
|
||||
} else {
|
||||
if (enableTracing) {
|
||||
if (enableTracingLog) {
|
||||
lwc_plog(env, "%s performOnMainThread[caller]: waiting on MainThread(%s). Caller=[%s] [%s]",
|
||||
[callerCtx identifier], aSelector, toCString([callerCtx caller]),
|
||||
wait ? "WAIT" : "ASYNC");
|
||||
@@ -472,7 +497,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
[ThreadUtilities performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:wait modes:runLoopModes];
|
||||
|
||||
if (enableTracing) {
|
||||
if (enableTracingLog) {
|
||||
lwc_plog(env, "%s performOnMainThread[caller]: finished on MainThread(%s). Caller=[%s] [DONE]",
|
||||
[callerCtx identifier], aSelector, toCString([callerCtx caller]));
|
||||
}
|
||||
@@ -489,7 +514,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
return JavaRunLoopMode;
|
||||
}
|
||||
|
||||
+ (NSMutableDictionary*) threadContextStore {
|
||||
+ (NSMutableDictionary*)threadContextStore {
|
||||
static NSMutableDictionary<NSString*, ThreadTraceContext*>* _threadTraceContextPerName;
|
||||
static dispatch_once_t oncePredicate;
|
||||
|
||||
@@ -500,7 +525,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
return _threadTraceContextPerName;
|
||||
}
|
||||
|
||||
+ (ThreadTraceContext*) getTraceContext {
|
||||
+ (ThreadTraceContext*)getTraceContext {
|
||||
const NSString* thName = [[NSThread currentThread] name];
|
||||
|
||||
NSMutableDictionary* allContexts = [ThreadUtilities threadContextStore];
|
||||
@@ -522,19 +547,19 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
[[ThreadUtilities threadContextStore] removeObjectForKey:thName];
|
||||
}
|
||||
|
||||
+ (void) resetTraceContext {
|
||||
+ (void)resetTraceContext {
|
||||
[[ThreadUtilities getTraceContext] reset];
|
||||
}
|
||||
|
||||
+ (ThreadTraceContext*) recordTraceContext {
|
||||
+ (ThreadTraceContext*)recordTraceContext {
|
||||
return [ThreadUtilities recordTraceContext:@"recordTraceContext"];
|
||||
}
|
||||
|
||||
+ (ThreadTraceContext*) recordTraceContext:(NSString*) prefix {
|
||||
+ (ThreadTraceContext*)recordTraceContext:(NSString*) prefix {
|
||||
return [ThreadUtilities recordTraceContext:prefix actionId:-1 useJavaModes:NO operation:""];
|
||||
}
|
||||
|
||||
+ (ThreadTraceContext*) recordTraceContext:(NSString*) prefix
|
||||
+ (ThreadTraceContext*)recordTraceContext:(NSString*) prefix
|
||||
actionId:(long) actionId
|
||||
useJavaModes:(BOOL) useJavaModes
|
||||
operation:(char*) operation
|
||||
@@ -549,6 +574,218 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
return [thCtx set:actionId operation:operation useJavaModes:useJavaModes caller:caller callstack:callStack];
|
||||
}
|
||||
|
||||
+ (NSString*)getThreadTraceContexts
|
||||
{
|
||||
NSMutableDictionary* allContexts = [ThreadUtilities threadContextStore];
|
||||
|
||||
// CHECK LEAK ?
|
||||
NSMutableString* dump = [[[NSMutableString alloc] initWithCapacity:4096] autorelease];
|
||||
[dump appendString:@"[\n"];
|
||||
|
||||
for (NSString* thName in allContexts) {
|
||||
ThreadTraceContext *thCtx = allContexts[thName];
|
||||
|
||||
[dump appendString:@"\n["];
|
||||
[dump appendFormat:@"\n thread:'%@'", thName];
|
||||
[dump appendString:@"\n traceContext: "];
|
||||
|
||||
if (thCtx == nil) {
|
||||
[dump appendString:@"null"];
|
||||
} else {
|
||||
[dump appendString:@"["];
|
||||
[dump appendFormat:@"\n %@", thCtx.description];
|
||||
[dump appendString:@"\n ]"];
|
||||
}
|
||||
[dump appendString:@"\n] \n"];
|
||||
}
|
||||
[dump appendString:@"]"];
|
||||
[dump retain];
|
||||
return dump;
|
||||
}
|
||||
|
||||
+ (BOOL)isWithinPowerTransition {
|
||||
if (wakeUpTime != 0LL) {
|
||||
// check last wake-up time:
|
||||
if (nowNearTime("wake-up", &wakeUpTime)) {
|
||||
return true;
|
||||
}
|
||||
// reset invalid time:
|
||||
wakeUpTime = 0LL;
|
||||
} else if (sleepTime != 0LL) {
|
||||
// check last sleep time:
|
||||
if (nowNearTime("sleep", &sleepTime)) {
|
||||
return true;
|
||||
}
|
||||
// reset invalid time:
|
||||
sleepTime = 0LL;
|
||||
} else if (TRACE_PWM) {
|
||||
NSLog(@"EAWT: isWithinPowerTransition: no times");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+ (void)_systemOrScreenWillSleep:(NSNotification*)notification {
|
||||
atomic_uint_least64_t now;
|
||||
if (nanoUpTime(&now))
|
||||
{
|
||||
// keep most-recent wake-up time (system or display):
|
||||
sleepTime = now;
|
||||
|
||||
if (TRACE_PWM_EVENTS) {
|
||||
NSLog(@"EAWT: _systemOrScreenWillSleep[%@]: sleep time = %.5lf (%.5lf)",
|
||||
[notification name], 1e-9 * sleepTime,
|
||||
[NSProcessInfo processInfo].systemUptime);
|
||||
}
|
||||
// reset wake-up time (system or display):
|
||||
wakeUpTime = 0LL;
|
||||
|
||||
if (TRACE_CLOCKS) {
|
||||
dumpClocks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)_systemOrScreenDidWake:(NSNotification*)notification {
|
||||
atomic_uint_least64_t now;
|
||||
if (nanoUpTime(&now))
|
||||
{
|
||||
// keep most-recent wake-up time (system or display):
|
||||
wakeUpTime = now;
|
||||
|
||||
if (TRACE_PWM_EVENTS) {
|
||||
NSLog(@"EAWT: _systemOrScreenDidWake[%@]: wake-up time = %.5lf (%.5lf)",
|
||||
[notification name], 1e-9 * wakeUpTime,
|
||||
[NSProcessInfo processInfo].systemUptime);
|
||||
}
|
||||
// CHECK
|
||||
if (sleepTime != 0LL) {
|
||||
if (now > sleepTime) {
|
||||
// check last sleep time:
|
||||
now -= sleepTime; // delta in ns
|
||||
if (TRACE_PWM_EVENTS) {
|
||||
NSLog(@"EAWT: _systemOrScreenDidWake: SLEEP duration = %.5lf ms", 1e-6 * now);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TRACE_CLOCKS) {
|
||||
dumpClocks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)nanoUpTime:(atomic_uint_least64_t*)nanotime {
|
||||
return nanoUpTime(nanotime);
|
||||
}
|
||||
|
||||
+ (BOOL)nowNearTime:(NSString*)src refTime:(atomic_uint_least64_t*)refTime {
|
||||
return nowNearTime(src.UTF8String, refTime);
|
||||
}
|
||||
|
||||
bool nowNearTime(const char* src, atomic_uint_least64_t *refTime) {
|
||||
if (*refTime != 0LL) {
|
||||
atomic_uint_least64_t now;
|
||||
if (nanoUpTime(&now)) {
|
||||
if (now < *refTime) {
|
||||
// should not happen with monotonic clocks, but:
|
||||
now = *refTime;
|
||||
}
|
||||
// check absolute delta in nanoseconds:
|
||||
now -= *refTime;
|
||||
|
||||
if (TRACE_PWM) {
|
||||
NSLog(@"EAWT: nowNearTime[%s]: delta time = %.5lf ms", src, 1e-6 * now);
|
||||
}
|
||||
return (now <= RDV_PERIOD);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nanoUpTime(atomic_uint_least64_t *nanotime) {
|
||||
// Use a monotonic clock (linearly increasing by each tick)
|
||||
// but not counting the time while sleeping.
|
||||
// NOTE:CLOCK_UPTIME_RAW seems counting more elapsed time
|
||||
// arround sleep/wake-up cycle than CLOCK_PROCESS_CPUTIME_ID (adopted):
|
||||
return getTime_nanos(CLOCK_PROCESS_CPUTIME_ID, nanotime);
|
||||
}
|
||||
|
||||
bool getTime_nanos(clockid_t clock_id, atomic_uint_least64_t *nanotime) {
|
||||
struct timespec tp;
|
||||
// Use the given clock:
|
||||
int status = clock_gettime(clock_id, &tp);
|
||||
if (status != 0) {
|
||||
return false;
|
||||
}
|
||||
*nanotime = tp.tv_sec * NANOS_PER_SEC + tp.tv_nsec;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dumpClocks() {
|
||||
if (TRACE_CLOCKS) {
|
||||
logTime_nanos(CLOCK_REALTIME);
|
||||
logTime_nanos(CLOCK_MONOTONIC);
|
||||
logTime_nanos(CLOCK_MONOTONIC_RAW);
|
||||
logTime_nanos(CLOCK_MONOTONIC_RAW_APPROX);
|
||||
logTime_nanos(CLOCK_UPTIME_RAW);
|
||||
logTime_nanos(CLOCK_UPTIME_RAW_APPROX);
|
||||
logTime_nanos(CLOCK_PROCESS_CPUTIME_ID);
|
||||
logTime_nanos(CLOCK_THREAD_CPUTIME_ID);
|
||||
}
|
||||
}
|
||||
|
||||
void logTime_nanos(clockid_t clock_id) {
|
||||
if (TRACE_CLOCKS) {
|
||||
atomic_uint_least64_t now;
|
||||
if (getTime_nanos(clock_id, &now)) {
|
||||
const char *clock_name;
|
||||
switch (clock_id) {
|
||||
case CLOCK_REALTIME:
|
||||
clock_name = "CLOCK_REALTIME";
|
||||
break;
|
||||
case CLOCK_MONOTONIC:
|
||||
clock_name = "CLOCK_MONOTONIC";
|
||||
break;
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
clock_name = "CLOCK_MONOTONIC_RAW";
|
||||
break;
|
||||
case CLOCK_MONOTONIC_RAW_APPROX:
|
||||
clock_name = "CLOCK_MONOTONIC_RAW_APPROX";
|
||||
break;
|
||||
case CLOCK_UPTIME_RAW:
|
||||
clock_name = "CLOCK_UPTIME_RAW";
|
||||
break;
|
||||
case CLOCK_UPTIME_RAW_APPROX:
|
||||
clock_name = "CLOCK_UPTIME_RAW_APPROX";
|
||||
break;
|
||||
case CLOCK_PROCESS_CPUTIME_ID:
|
||||
clock_name = "CLOCK_PROCESS_CPUTIME_ID";
|
||||
break;
|
||||
case CLOCK_THREAD_CPUTIME_ID:
|
||||
clock_name = "CLOCK_THREAD_CPUTIME_ID";
|
||||
break;
|
||||
default:
|
||||
clock_name = "unknown";
|
||||
}
|
||||
NSLog(@"EAWT: logTime_nanos[%27s] time: %.6lf s", clock_name, 1e-9 * now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)registerForSystemAndScreenNotifications {
|
||||
static BOOL notificationRegistered = false;
|
||||
if (!notificationRegistered) {
|
||||
notificationRegistered = true;
|
||||
|
||||
NSNotificationCenter *ctr = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
Class clz = [ThreadUtilities class];
|
||||
|
||||
[ctr addObserver:clz selector:@selector(_systemOrScreenWillSleep:) name:NSWorkspaceWillSleepNotification object:nil];
|
||||
[ctr addObserver:clz selector:@selector(_systemOrScreenDidWake:) name:NSWorkspaceDidWakeNotification object:nil];
|
||||
|
||||
[ctr addObserver:clz selector:@selector(_systemOrScreenWillSleep:) name:NSWorkspaceScreensDidSleepNotification object:nil];
|
||||
[ctr addObserver:clz selector:@selector(_systemOrScreenDidWake:) name:NSWorkspaceScreensDidWakeNotification object:nil];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
void OSXAPP_SetJavaVM(JavaVM *vm)
|
||||
@@ -712,9 +949,10 @@ JNIEXPORT void lwc_plog(JNIEnv* env, const char *formatMsg, ...) {
|
||||
timestamp, actionId, operation]);
|
||||
}
|
||||
|
||||
- (NSString *)getDescription {
|
||||
return [NSString stringWithFormat:@"%s useJavaModes=%d sleep=%d caller=[%@]",
|
||||
[self identifier], useJavaModes, sleep, caller];
|
||||
- (NSString *)description {
|
||||
// creates autorelease string:
|
||||
return [NSString stringWithFormat:@"%s useJavaModes=%d sleep=%d caller=[%@] callStack={\n%@}",
|
||||
[self identifier], useJavaModes, sleep, caller, callStack];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user