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") {
"-I$skiaDir",
"-I$skiaDir/include",
"-I$skiaDir/include/gpu",
"-fobjc-arc",
"-DSK_METAL",
"-std=c++17",
"-c",
......
package org.jetbrains.skiko
internal external fun getApplicationWindowCount(): Int
package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.swing.Swing
......@@ -73,14 +74,11 @@ internal class MetalRedrawer(
// Dispatchers.IO: 50 FPS, 200% CPU
layer.inDrawScope {
withContext(Dispatchers.IO) {
val handle = startRendering()
try {
performDraw()
} finally {
endRendering(handle)
}
performDraw()
}
}
if (isDisposed) throw CancellationException()
// 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
// `NSWindowDidChangeOcclusionStateNotification`, but current approach seems to work as well in practise.
......@@ -90,7 +88,12 @@ internal class MetalRedrawer(
private fun performDraw() = synchronized(drawLock) {
if (!isDisposed) {
layer.draw()
val handle = startRendering()
try {
layer.draw()
} finally {
endRendering(handle)
}
}
}
......
......@@ -34,7 +34,7 @@
@property jobject canvasGlobalRef;
@property (retain, strong) CALayer *container;
@property (retain, strong) NSWindow *window;
@property (weak) NSWindow *window;
@end
......@@ -59,14 +59,6 @@
return self;
}
-(void) dealloc
{
self.canvasGlobalRef = NULL;
[self.container release];
[self.window release];
[super dealloc];
}
- (void) setUpCustomHeader
{
/**
......@@ -265,7 +257,7 @@ NSWindow *findCALayerWindow(NSView *rootView, CALayer *layer) {
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];
NSWindow* target_window = nil;
......@@ -281,81 +273,96 @@ NSWindow *findWindow(jlong platformInfoPtr)
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv *env, jobject canvas, jlong platformInfoPtr)
{
if (layerStorage == nil)
{
layerStorage = [[NSMutableSet alloc] init];
}
@autoreleasepool {
if (layerStorage == nil)
{
layerStorage = [[NSMutableSet alloc] init];
}
LayerHandler *layersSet = [[LayerHandler alloc] init];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr;
layersSet.container = [dsi_mac windowLayer];
jobject canvasGlobalRef = env->NewGlobalRef(canvas);
[layersSet setCanvasGlobalRef: canvasGlobalRef];
layersSet.window = findWindow(platformInfoPtr);
LayerHandler *layer = [[LayerHandler alloc] init];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) (void*) platformInfoPtr;
layer.container = [dsi_mac windowLayer];
jobject canvasGlobalRef = env->NewGlobalRef(canvas);
[layer setCanvasGlobalRef: canvasGlobalRef];
layer.window = findWindow(platformInfoPtr);
[layerStorage addObject: layersSet];
[layerStorage addObject: layer];
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeDispose(JNIEnv *env, jobject canvas)
{
LayerHandler *layer = findByObject(env, canvas);
if (layer != NULL)
{
[layerStorage removeObject: layer];
env->DeleteGlobalRef(layer.canvasGlobalRef);
[layer release];
@autoreleasepool {
LayerHandler *layer = findByObject(env, canvas);
if (layer != NULL)
{
[layerStorage removeObject: layer];
env->DeleteGlobalRef(layer.canvasGlobalRef);
}
}
}
JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxIsFullscreenNative(JNIEnv *env, jobject properties, jobject component)
{
LayerHandler *layer = findByObject(env, component);
if (layer != NULL)
{
return [layer isFullScreen];
@autoreleasepool {
LayerHandler *layer = findByObject(env, component);
if (layer != NULL)
{
return [layer isFullScreen];
}
return false;
}
return false;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_PlatformOperationsKt_osxSetFullscreenNative(JNIEnv *env, jobject properties, jobject component, jboolean value)
{
LayerHandler *layer = findByObject(env, component);
if (layer != NULL)
{
[layer makeFullscreen:value];
@autoreleasepool {
LayerHandler *layer = findByObject(env, component);
if (layer != NULL)
{
[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);
return (jlong)window;
@autoreleasepool {
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);
return (jlong)window;
@autoreleasepool {
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)
{
LayerHandler *layer = findByObject(env, component);
if (layer != NULL)
{
[layer disableTitlebar:((CGFloat) customHeaderHeight)];
@autoreleasepool {
LayerHandler *layer = findByObject(env, component);
if (layer != NULL)
{
[layer disableTitlebar:((CGFloat) customHeaderHeight)];
}
}
}
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_SystemTheme_1jvmKt_getCurrentSystemTheme(JNIEnv *env, jobject topLevel)
{
NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
if ([@"Dark" isEqualToString:osxMode]) {
// Dark.
return 1;
} else {
// Light.
return 0;
@autoreleasepool {
NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
if ([@"Dark" isEqualToString:osxMode]) {
// Dark.
return 1;
} else {
// Light.
return 0;
}
}
}
......@@ -363,8 +370,8 @@ void getMetalDeviceAndQueue(void** device, void** queue)
{
id<MTLDevice> fDevice = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> fQueue = [fDevice newCommandQueue];
*device = (__bridge void*)fDevice;
*queue = (__bridge void*)fQueue;
*device = (__bridge void*) fDevice;
*queue = (__bridge void*) fQueue;
}
} // extern C
\ No newline at end of file
......@@ -29,7 +29,7 @@
@interface MetalDevice : NSObject
@property (retain, strong) CALayer *container;
@property (weak) CALayer *container;
@property (retain, strong) AWTMetalLayer *layer;
@property (retain, strong) id<MTLDevice> device;
@property (retain, strong) id<MTLCommandQueue> queue;
......@@ -71,15 +71,6 @@
return self;
}
-(void)dealloc {
[self.layer removeFromSuperlayer];
[self.layer release];
[self.device release];
[self.queue release];
[self.drawableHandle release];
[super dealloc];
}
@end
extern "C"
......@@ -88,26 +79,30 @@ extern "C"
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalContext(
JNIEnv* env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
GrMtlBackendContext backendContext = {};
backendContext.fDevice.retain((GrMTLHandle) device.device);
backendContext.fQueue.retain((GrMTLHandle) device.queue);
return (jlong) GrDirectContext::MakeMetal(backendContext).release();
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void*) devicePtr;
GrMtlBackendContext backendContext = {};
backendContext.fDevice.retain((__bridge GrMTLHandle) device.device);
backendContext.fQueue.retain((__bridge GrMTLHandle) device.queue);
return (jlong) GrDirectContext::MakeMetal(backendContext).release();
}
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalRenderTarget(
JNIEnv * env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
MetalDevice *device = (MetalDevice *) devicePtr;
GrBackendRenderTarget* renderTarget = NULL;
id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable];
if (!currentDrawable) return 0;
device.drawableHandle = currentDrawable;
GrMtlTextureInfo info;
info.fTexture.retain(currentDrawable.texture);
renderTarget = new GrBackendRenderTarget(width, height, 0, info);
return (jlong) renderTarget;
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
GrBackendRenderTarget* renderTarget = NULL;
id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable];
if (!currentDrawable) return 0;
device.drawableHandle = currentDrawable;
GrMtlTextureInfo info;
info.fTexture.retain((__bridge GrMTLHandle) currentDrawable.texture);
renderTarget = new GrBackendRenderTarget(width, height, 0, info);
return (jlong) renderTarget;
}
}
extern "C" void* objc_autoreleasePoolPush(void);
......@@ -185,129 +180,147 @@ id<MTLDevice> MTLCreateIntegratedDevice(int adapterPriority) {
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMetalDevice(
JNIEnv *env, jobject redrawer, jlong windowPtr, jboolean transparency, jint adapterPriority, jlong platformInfoPtr)
{
MetalDevice *device = [MetalDevice new];
@autoreleasepool {
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];
[container removeAllAnimations];
[container setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[container setNeedsDisplayOnBoundsChange: YES];
CALayer *container = [dsi_mac windowLayer];
[container removeAllAnimations];
[container setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[container setNeedsDisplayOnBoundsChange: YES];
AWTMetalLayer *layer = [AWTMetalLayer new];
[container addSublayer: layer];
layer.javaRef = env->NewGlobalRef(redrawer);
AWTMetalLayer *layer = [AWTMetalLayer new];
[container addSublayer: layer];
layer.javaRef = env->NewGlobalRef(redrawer);
id<MTLDevice> fDevice = MTLCreateIntegratedDevice(adapterPriority);
id<MTLCommandQueue> fQueue = [fDevice newCommandQueue];
id<MTLDevice> fDevice = MTLCreateIntegratedDevice(adapterPriority);
id<MTLCommandQueue> fQueue = [fDevice newCommandQueue];
device.container = container;
device.layer = layer;
device.device = fDevice;
device.queue = fQueue;
device.container = container;
device.layer = layer;
device.device = fDevice;
device.queue = fQueue;
device.layer.device = device.device;
device.layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
device.layer.contentsGravity = kCAGravityTopLeft;
device.layer.device = device.device;
device.layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
device.layer.contentsGravity = kCAGravityTopLeft;
CGFloat transparent[] = { 0.0f, 0.0f, 0.0f, 0.0f };
device.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), transparent);
device.layer.opaque = NO;
if (transparency)
{
NSWindow* window = (NSWindow*)windowPtr;
window.hasShadow = NO;
}
CGFloat transparent[] = { 0.0f, 0.0f, 0.0f, 0.0f };
device.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), transparent);
device.layer.opaque = NO;
if (transparency)
{
NSWindow* window = (__bridge NSWindow*) (void *) windowPtr;
window.hasShadow = NO;
}
return (jlong) device;
return (jlong) (__bridge_retained void *) device;
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLayers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint x, jint y, jint width, jint height)
{
MetalDevice *device = (MetalDevice *) devicePtr;
float scale = device.layer.contentsScale;
CGRect frame = CGRectMake(x, y, width, height);
CGSize drawableSize = CGSizeMake(width * scale, height * scale);
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
device.layer.frame = frame;
// to avoid warning in console:
// CAMetalLayer ignoring invalid setDrawableSize width=0.000000 height=0.000000
if (width > 0 && height > 0) {
device.layer.drawableSize = drawableSize;
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
float scale = device.layer.contentsScale;
CGRect frame = CGRectMake(x, y, width, height);
CGSize drawableSize = CGSizeMake(width * scale, height * scale);
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
device.layer.frame = frame;
// to avoid warning in console:
// CAMetalLayer ignoring invalid setDrawableSize width=0.000000 height=0.000000
if (width > 0 && height > 0) {
device.layer.drawableSize = drawableSize;
}
[CATransaction commit];
[CATransaction flush];
}
[CATransaction commit];
[CATransaction flush];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setContentScale(JNIEnv *env, jobject obj, jlong devicePtr, jfloat contentScale)
{
MetalDevice *device = (MetalDevice *) devicePtr;
if (!device || !device.layer || device.layer.contentsScale == contentScale) {
return;
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
if (!device || !device.layer || device.layer.contentsScale == contentScale) {
return;
}
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
assert(contentScale != 0);
device.container.contentsScale = contentScale;
device.layer.contentsScale = contentScale;
[CATransaction commit];
[CATransaction flush];
}
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
assert(contentScale != 0);
device.container.contentsScale = contentScale;
device.layer.contentsScale = contentScale;
[CATransaction commit];
[CATransaction flush];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setVSyncEnabled(JNIEnv *env, jobject obj, jlong devicePtr, jboolean enabled)
{
MetalDevice *device = (MetalDevice *) devicePtr;
device.layer.displaySyncEnabled = enabled;
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
device.layer.displaySyncEnabled = enabled;
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFrame(
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;
if (currentDrawable) {
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present";
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
device.drawableHandle = nil;
if (currentDrawable) {
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present";
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
device.drawableHandle = nil;
}
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
env->DeleteGlobalRef(device.layer.javaRef);
[device release];
@autoreleasepool {
MetalDevice *device = (__bridge_transfer MetalDevice *) (void *) devicePtr;
env->DeleteGlobalRef(device.layer.javaRef);
[device.layer removeFromSuperlayer];
}
}
JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapterName(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
const char *currentAdapterName = [[device.device name] cStringUsingEncoding:NSASCIIStringEncoding];
jstring result = env->NewStringUTF(currentAdapterName);
return result;
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
const char *currentAdapterName = [[device.device name] cStringUsingEncoding:NSASCIIStringEncoding];
jstring result = env->NewStringUTF(currentAdapterName);
return result;
}
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapterMemorySize(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
uint64_t totalMemory = [device.device recommendedMaxWorkingSetSize];
return (jlong)totalMemory;
@autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
uint64_t totalMemory = [device.device recommendedMaxWorkingSetSize];
return (jlong)totalMemory;
}
}
JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_isOccluded(
JNIEnv *env, jobject redrawer, jlong windowPtr) {
NSWindow* window = (NSWindow*)windowPtr;
return ([window occlusionState] & NSWindowOcclusionStateVisible) == 0;
@autoreleasepool {
NSWindow* window = (__bridge NSWindow*) (void *) windowPtr;
return ([window occlusionState] & NSWindowOcclusionStateVisible) == 0;
}
}
} // 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
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.util.ScreenshotTestRule
import org.jetbrains.skiko.util.swingTest
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assume.assumeTrue
import org.junit.Rule
......@@ -63,6 +64,42 @@ class SkiaWindowTest {
@get:Rule
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
fun `render single window`() = swingTest {
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