Unverified Commit 333d444e authored by Igor Demin's avatar Igor Demin Committed by GitHub

Fix memory leaks on macOs, enable Arc compilation (#340)

parent ee8e4cc9
...@@ -723,6 +723,7 @@ project.tasks.register<Exec>("objcCompile") { ...@@ -723,6 +723,7 @@ project.tasks.register<Exec>("objcCompile") {
"-I$skiaDir", "-I$skiaDir",
"-I$skiaDir/include", "-I$skiaDir/include",
"-I$skiaDir/include/gpu", "-I$skiaDir/include/gpu",
"-fobjc-arc",
"-DSK_METAL", "-DSK_METAL",
"-std=c++17", "-std=c++17",
"-c", "-c",
......
package org.jetbrains.skiko
internal external fun getApplicationWindowCount(): Int
package org.jetbrains.skiko.redrawer package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
...@@ -73,14 +74,11 @@ internal class MetalRedrawer( ...@@ -73,14 +74,11 @@ internal class MetalRedrawer(
// Dispatchers.IO: 50 FPS, 200% CPU // Dispatchers.IO: 50 FPS, 200% CPU
layer.inDrawScope { layer.inDrawScope {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val handle = startRendering()
try {
performDraw() performDraw()
} finally {
endRendering(handle)
}
} }
} }
if (isDisposed) throw CancellationException()
// When window is not visible - it doesn't make sense to redraw fast to avoid battery drain. // When window is not visible - it doesn't make sense to redraw fast to avoid battery drain.
// In theory, we could be more precise, and just suspend rendering in // In theory, we could be more precise, and just suspend rendering in
// `NSWindowDidChangeOcclusionStateNotification`, but current approach seems to work as well in practise. // `NSWindowDidChangeOcclusionStateNotification`, but current approach seems to work as well in practise.
...@@ -90,7 +88,12 @@ internal class MetalRedrawer( ...@@ -90,7 +88,12 @@ internal class MetalRedrawer(
private fun performDraw() = synchronized(drawLock) { private fun performDraw() = synchronized(drawLock) {
if (!isDisposed) { if (!isDisposed) {
val handle = startRendering()
try {
layer.draw() layer.draw()
} finally {
endRendering(handle)
}
} }
} }
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
@property jobject canvasGlobalRef; @property jobject canvasGlobalRef;
@property (retain, strong) CALayer *container; @property (retain, strong) CALayer *container;
@property (retain, strong) NSWindow *window; @property (weak) NSWindow *window;
@end @end
...@@ -59,14 +59,6 @@ ...@@ -59,14 +59,6 @@
return self; return self;
} }
-(void) dealloc
{
self.canvasGlobalRef = NULL;
[self.container release];
[self.window release];
[super dealloc];
}
- (void) setUpCustomHeader - (void) setUpCustomHeader
{ {
/** /**
...@@ -265,7 +257,7 @@ NSWindow *findCALayerWindow(NSView *rootView, CALayer *layer) { ...@@ -265,7 +257,7 @@ NSWindow *findCALayerWindow(NSView *rootView, CALayer *layer) {
NSWindow *findWindow(jlong platformInfoPtr) NSWindow *findWindow(jlong platformInfoPtr)
{ {
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr; NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) ((void*)platformInfoPtr);
CALayer* ca_layer = [dsi_mac windowLayer]; CALayer* ca_layer = [dsi_mac windowLayer];
NSWindow* target_window = nil; NSWindow* target_window = nil;
...@@ -281,74 +273,88 @@ NSWindow *findWindow(jlong platformInfoPtr) ...@@ -281,74 +273,88 @@ NSWindow *findWindow(jlong platformInfoPtr)
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv *env, jobject canvas, jlong platformInfoPtr) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv *env, jobject canvas, jlong platformInfoPtr)
{ {
@autoreleasepool {
if (layerStorage == nil) if (layerStorage == nil)
{ {
layerStorage = [[NSMutableSet alloc] init]; layerStorage = [[NSMutableSet alloc] init];
} }
LayerHandler *layersSet = [[LayerHandler alloc] init]; LayerHandler *layer = [[LayerHandler alloc] init];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr; NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) (void*) platformInfoPtr;
layersSet.container = [dsi_mac windowLayer]; layer.container = [dsi_mac windowLayer];
jobject canvasGlobalRef = env->NewGlobalRef(canvas); jobject canvasGlobalRef = env->NewGlobalRef(canvas);
[layersSet setCanvasGlobalRef: canvasGlobalRef]; [layer setCanvasGlobalRef: canvasGlobalRef];
layersSet.window = findWindow(platformInfoPtr); layer.window = findWindow(platformInfoPtr);
[layerStorage addObject: layersSet]; [layerStorage addObject: layer];
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeDispose(JNIEnv *env, jobject canvas) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeDispose(JNIEnv *env, jobject canvas)
{ {
@autoreleasepool {
LayerHandler *layer = findByObject(env, canvas); LayerHandler *layer = findByObject(env, canvas);
if (layer != NULL) if (layer != NULL)
{ {
[layerStorage removeObject: layer]; [layerStorage removeObject: layer];
env->DeleteGlobalRef(layer.canvasGlobalRef); env->DeleteGlobalRef(layer.canvasGlobalRef);
[layer release]; }
} }
} }
JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxIsFullscreenNative(JNIEnv *env, jobject properties, jobject component) JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxIsFullscreenNative(JNIEnv *env, jobject properties, jobject component)
{ {
@autoreleasepool {
LayerHandler *layer = findByObject(env, component); LayerHandler *layer = findByObject(env, component);
if (layer != NULL) if (layer != NULL)
{ {
return [layer isFullScreen]; return [layer isFullScreen];
} }
return false; return false;
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxSetFullscreenNative(JNIEnv *env, jobject properties, jobject component, jboolean value) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxSetFullscreenNative(JNIEnv *env, jobject properties, jobject component, jboolean value)
{ {
@autoreleasepool {
LayerHandler *layer = findByObject(env, component); LayerHandler *layer = findByObject(env, component);
if (layer != NULL) if (layer != NULL)
{ {
[layer makeFullscreen:value]; [layer makeFullscreen:value];
} }
}
} }
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_HardwareLayer_getWindowHandle(JNIEnv *env, jobject canvas, jlong platformInfoPtr) JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_HardwareLayer_getWindowHandle(JNIEnv *env, jobject component, jlong platformInfoPtr)
{ {
NSWindow* window = findWindow(platformInfoPtr); @autoreleasepool {
return (jlong)window; LayerHandler *layer = findByObject(env, component);
return (jlong) (__bridge void*) layer.window;
}
} }
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_HardwareLayer_getContentHandle(JNIEnv *env, jobject canvas, jlong platformInfoPtr) JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_HardwareLayer_getContentHandle(JNIEnv *env, jobject component, jlong platformInfoPtr)
{ {
NSWindow* window = findWindow(platformInfoPtr); @autoreleasepool {
return (jlong)window; LayerHandler *layer = findByObject(env, component);
return (jlong) (__bridge void*) layer.window;
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxDisableTitleBar(JNIEnv *env, jobject properties, jobject component, jfloat customHeaderHeight) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxDisableTitleBar(JNIEnv *env, jobject properties, jobject component, jfloat customHeaderHeight)
{ {
@autoreleasepool {
LayerHandler *layer = findByObject(env, component); LayerHandler *layer = findByObject(env, component);
if (layer != NULL) if (layer != NULL)
{ {
[layer disableTitlebar:((CGFloat) customHeaderHeight)]; [layer disableTitlebar:((CGFloat) customHeaderHeight)];
} }
}
} }
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_SystemTheme_1jvmKt_getCurrentSystemTheme(JNIEnv *env, jobject topLevel) JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_SystemTheme_1jvmKt_getCurrentSystemTheme(JNIEnv *env, jobject topLevel)
{ {
@autoreleasepool {
NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
if ([@"Dark" isEqualToString:osxMode]) { if ([@"Dark" isEqualToString:osxMode]) {
// Dark. // Dark.
...@@ -357,14 +363,15 @@ JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_SystemTheme_1jvmKt_getCurrentSys ...@@ -357,14 +363,15 @@ JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_SystemTheme_1jvmKt_getCurrentSys
// Light. // Light.
return 0; return 0;
} }
}
} }
void getMetalDeviceAndQueue(void** device, void** queue) void getMetalDeviceAndQueue(void** device, void** queue)
{ {
id<MTLDevice> fDevice = MTLCreateSystemDefaultDevice(); id<MTLDevice> fDevice = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> fQueue = [fDevice newCommandQueue]; id<MTLCommandQueue> fQueue = [fDevice newCommandQueue];
*device = (__bridge void*)fDevice; *device = (__bridge void*) fDevice;
*queue = (__bridge void*)fQueue; *queue = (__bridge void*) fQueue;
} }
} // extern C } // extern C
\ No newline at end of file
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
@interface MetalDevice : NSObject @interface MetalDevice : NSObject
@property (retain, strong) CALayer *container; @property (weak) CALayer *container;
@property (retain, strong) AWTMetalLayer *layer; @property (retain, strong) AWTMetalLayer *layer;
@property (retain, strong) id<MTLDevice> device; @property (retain, strong) id<MTLDevice> device;
@property (retain, strong) id<MTLCommandQueue> queue; @property (retain, strong) id<MTLCommandQueue> queue;
...@@ -71,15 +71,6 @@ ...@@ -71,15 +71,6 @@
return self; return self;
} }
-(void)dealloc {
[self.layer removeFromSuperlayer];
[self.layer release];
[self.device release];
[self.queue release];
[self.drawableHandle release];
[super dealloc];
}
@end @end
extern "C" extern "C"
...@@ -88,26 +79,30 @@ extern "C" ...@@ -88,26 +79,30 @@ extern "C"
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalContext( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalContext(
JNIEnv* env, jobject redrawer, jlong devicePtr) JNIEnv* env, jobject redrawer, jlong devicePtr)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void*) devicePtr;
GrMtlBackendContext backendContext = {}; GrMtlBackendContext backendContext = {};
backendContext.fDevice.retain((GrMTLHandle) device.device); backendContext.fDevice.retain((__bridge GrMTLHandle) device.device);
backendContext.fQueue.retain((GrMTLHandle) device.queue); backendContext.fQueue.retain((__bridge GrMTLHandle) device.queue);
return (jlong) GrDirectContext::MakeMetal(backendContext).release(); return (jlong) GrDirectContext::MakeMetal(backendContext).release();
}
} }
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalRenderTarget( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalRenderTarget(
JNIEnv * env, jobject redrawer, jlong devicePtr, jint width, jint height) JNIEnv * env, jobject redrawer, jlong devicePtr, jint width, jint height)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
GrBackendRenderTarget* renderTarget = NULL; GrBackendRenderTarget* renderTarget = NULL;
id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable]; id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable];
if (!currentDrawable) return 0; if (!currentDrawable) return 0;
device.drawableHandle = currentDrawable; device.drawableHandle = currentDrawable;
GrMtlTextureInfo info; GrMtlTextureInfo info;
info.fTexture.retain(currentDrawable.texture); info.fTexture.retain((__bridge GrMTLHandle) currentDrawable.texture);
renderTarget = new GrBackendRenderTarget(width, height, 0, info); renderTarget = new GrBackendRenderTarget(width, height, 0, info);
return (jlong) renderTarget; return (jlong) renderTarget;
}
} }
extern "C" void* objc_autoreleasePoolPush(void); extern "C" void* objc_autoreleasePoolPush(void);
...@@ -185,9 +180,10 @@ id<MTLDevice> MTLCreateIntegratedDevice(int adapterPriority) { ...@@ -185,9 +180,10 @@ id<MTLDevice> MTLCreateIntegratedDevice(int adapterPriority) {
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMetalDevice( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMetalDevice(
JNIEnv *env, jobject redrawer, jlong windowPtr, jboolean transparency, jint adapterPriority, jlong platformInfoPtr) JNIEnv *env, jobject redrawer, jlong windowPtr, jboolean transparency, jint adapterPriority, jlong platformInfoPtr)
{ {
@autoreleasepool {
MetalDevice *device = [MetalDevice new]; MetalDevice *device = [MetalDevice new];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr; NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) (void*) platformInfoPtr;
CALayer *container = [dsi_mac windowLayer]; CALayer *container = [dsi_mac windowLayer];
[container removeAllAnimations]; [container removeAllAnimations];
...@@ -216,17 +212,19 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMe ...@@ -216,17 +212,19 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMe
if (transparency) if (transparency)
{ {
NSWindow* window = (NSWindow*)windowPtr; NSWindow* window = (__bridge NSWindow*) (void *) windowPtr;
window.hasShadow = NO; window.hasShadow = NO;
} }
return (jlong) device; return (jlong) (__bridge_retained void *) device;
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLayers( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLayers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint x, jint y, jint width, jint height) JNIEnv *env, jobject redrawer, jlong devicePtr, jint x, jint y, jint width, jint height)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
float scale = device.layer.contentsScale; float scale = device.layer.contentsScale;
CGRect frame = CGRectMake(x, y, width, height); CGRect frame = CGRectMake(x, y, width, height);
CGSize drawableSize = CGSizeMake(width * scale, height * scale); CGSize drawableSize = CGSizeMake(width * scale, height * scale);
...@@ -240,11 +238,13 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLay ...@@ -240,11 +238,13 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLay
} }
[CATransaction commit]; [CATransaction commit];
[CATransaction flush]; [CATransaction flush];
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setContentScale(JNIEnv *env, jobject obj, jlong devicePtr, jfloat contentScale) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setContentScale(JNIEnv *env, jobject obj, jlong devicePtr, jfloat contentScale)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
if (!device || !device.layer || device.layer.contentsScale == contentScale) { if (!device || !device.layer || device.layer.contentsScale == contentScale) {
return; return;
} }
...@@ -255,18 +255,22 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setConten ...@@ -255,18 +255,22 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setConten
device.layer.contentsScale = contentScale; device.layer.contentsScale = contentScale;
[CATransaction commit]; [CATransaction commit];
[CATransaction flush]; [CATransaction flush];
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setVSyncEnabled(JNIEnv *env, jobject obj, jlong devicePtr, jboolean enabled) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setVSyncEnabled(JNIEnv *env, jobject obj, jlong devicePtr, jboolean enabled)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
device.layer.displaySyncEnabled = enabled; device.layer.displaySyncEnabled = enabled;
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFrame( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFrame(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
id<CAMetalDrawable> currentDrawable = device.drawableHandle; id<CAMetalDrawable> currentDrawable = device.drawableHandle;
...@@ -277,37 +281,46 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFra ...@@ -277,37 +281,46 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFra
[commandBuffer commit]; [commandBuffer commit];
device.drawableHandle = nil; device.drawableHandle = nil;
} }
}
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge_transfer MetalDevice *) (void *) devicePtr;
env->DeleteGlobalRef(device.layer.javaRef); env->DeleteGlobalRef(device.layer.javaRef);
[device release]; [device.layer removeFromSuperlayer];
}
} }
JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapterName( JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapterName(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
const char *currentAdapterName = [[device.device name] cStringUsingEncoding:NSASCIIStringEncoding]; const char *currentAdapterName = [[device.device name] cStringUsingEncoding:NSASCIIStringEncoding];
jstring result = env->NewStringUTF(currentAdapterName); jstring result = env->NewStringUTF(currentAdapterName);
return result; return result;
}
} }
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapterMemorySize( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapterMemorySize(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
MetalDevice *device = (MetalDevice *) devicePtr; @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
uint64_t totalMemory = [device.device recommendedMaxWorkingSetSize]; uint64_t totalMemory = [device.device recommendedMaxWorkingSetSize];
return (jlong)totalMemory; return (jlong)totalMemory;
}
} }
JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_isOccluded( JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_isOccluded(
JNIEnv *env, jobject redrawer, jlong windowPtr) { JNIEnv *env, jobject redrawer, jlong windowPtr) {
NSWindow* window = (NSWindow*)windowPtr; @autoreleasepool {
NSWindow* window = (__bridge NSWindow*) (void *) windowPtr;
return ([window occlusionState] & NSWindowOcclusionStateVisible) == 0; return ([window occlusionState] & NSWindowOcclusionStateVisible) == 0;
}
} }
} // extern C } // extern C
......
#import "jawt.h"
#import "jawt_md.h"
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
extern "C"
{
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_NativeApplicationKt_getApplicationWindowCount(JNIEnv *env, jobject obj)
{
@autoreleasepool {
return [[[NSApplication sharedApplication] windows] count];
}
}
}
#import "jawt.h"
#import "jawt_md.h"
#define GL_SILENCE_DEPRECATION
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGL/gl3.h>
JavaVM *jvm = NULL;
@interface AWTGLLayer : CAOpenGLLayer
@property jobject javaRef;
@end
@implementation AWTGLLayer
- (id)init
{
self = [super init];
assert(self != NULL);
[self removeAllAnimations];
[self setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
return self;
}
-(BOOL)canDrawInCGLContext:(CGLContextObj)ctx
pixelFormat:(CGLPixelFormatObj)pf
forLayerTime:(CFTimeInterval)t
displayTime:(const CVTimeStamp *)ts
{
assert(jvm != NULL);
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
static jclass cls = NULL;
static jmethodID method = NULL;
if (!cls) cls = env->GetObjectClass(self.javaRef);
if (!method) method = env->GetMethodID(cls, "canDraw", "()Z");
return env->CallBooleanMethod(self.javaRef, method);
}
-(void)drawInCGLContext:(CGLContextObj)ctx
pixelFormat:(CGLPixelFormatObj)pf
forLayerTime:(CFTimeInterval)t
displayTime:(const CVTimeStamp *)ts
{
CGLSetCurrentContext(ctx);
assert(jvm != NULL);
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
static jclass cls = NULL;
static jmethodID method = NULL;
if (!cls) cls = env->GetObjectClass(self.javaRef);
if (!method) method = env->GetMethodID(cls, "performDraw", "()V");
env->CallVoidMethod(self.javaRef, method);
[super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts];
}
@end
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_initContainer(JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{
env->GetJavaVM(&jvm);
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr;
CALayer *container = [dsi_mac windowLayer];
[container removeAllAnimations];
[container setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[container setNeedsDisplayOnBoundsChange: YES];
return (jlong) container;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_setContentScale(JNIEnv *env, jobject obj, jlong layerPtr, jfloat contentScale)
{
CALayer *layer = (CALayer *) layerPtr;
assert(contentScale != 0);
layer.contentsScale = contentScale;
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_initAWTGLLayer(JNIEnv *env, jobject obj, jlong containerPtr, jobject layer, jboolean setNeedsDisplayOnBoundsChange)
{
CALayer *container = (CALayer *) containerPtr;
AWTGLLayer *glLayer = [AWTGLLayer new];
glLayer.javaRef = env->NewGlobalRef(layer);
[glLayer setNeedsDisplayOnBoundsChange: setNeedsDisplayOnBoundsChange];
[container addSublayer: glLayer];
return (jlong) glLayer;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_disposeAWTGLLayer(JNIEnv *env, jobject obj, jlong ptr)
{
AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
[glLayer removeFromSuperlayer];
env->DeleteGlobalRef(glLayer.javaRef);
[glLayer release];
}
JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_isAsynchronous(JNIEnv *env, jobject obj, jlong ptr)
{
AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
return glLayer.isAsynchronous;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_setAsynchronous(JNIEnv *env, jobject obj, jlong ptr, jboolean isAsynchronous)
{
AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
[glLayer setAsynchronous: isAsynchronous];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_setNeedsDisplayOnMainThread(JNIEnv *env, jobject obj, jlong ptr)
{
AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
[glLayer performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:0 waitUntilDone:NO];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_setFrame(JNIEnv *env, jobject obj, jlong containerPtr, jlong ptr, jfloat x, jfloat y, jfloat width, jfloat height)
{
CALayer *container = (AWTGLLayer *) containerPtr;
AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
y = (int)container.frame.size.height - y - height;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions]; // disable animations
glLayer.frame = CGRectMake(x, y, width, height);
[CATransaction commit];
}
} // extern C
\ No newline at end of file
...@@ -15,6 +15,7 @@ import org.jetbrains.skiko.context.ContextHandler ...@@ -15,6 +15,7 @@ import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.util.ScreenshotTestRule import org.jetbrains.skiko.util.ScreenshotTestRule
import org.jetbrains.skiko.util.swingTest import org.jetbrains.skiko.util.swingTest
import org.junit.Assert
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assume.assumeTrue import org.junit.Assume.assumeTrue
import org.junit.Rule import org.junit.Rule
...@@ -63,6 +64,42 @@ class SkiaWindowTest { ...@@ -63,6 +64,42 @@ class SkiaWindowTest {
@get:Rule @get:Rule
val screenshots = ScreenshotTestRule() val screenshots = ScreenshotTestRule()
@Test
fun `should not leak native windows`() = swingTest {
assumeTrue(hostOs.isMacOS)
suspend fun createAndDisposeWindow() {
val layer = SkiaLayer()
val frame = JFrame()
frame.contentPane.add(layer)
frame.size = Dimension(200, 200)
frame.isVisible = true
delay(30)
layer.dispose()
frame.dispose()
}
// warm caches
repeat(8) {
createAndDisposeWindow()
}
delay(1000)
val initialWindowCount = getApplicationWindowCount()
repeat(32) {
createAndDisposeWindow()
}
delay(1000)
val actualWindowCount = getApplicationWindowCount()
assertTrue(
initialWindowCount >= actualWindowCount,
"initialWindowCount=$initialWindowCount, actualWindowCount=$actualWindowCount"
)
}
@Test @Test
fun `render single window`() = swingTest { fun `render single window`() = swingTest {
val window = SkiaWindow() val window = SkiaWindow()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment