Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
skiko
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liuqiang
skiko
Commits
333d444e
Unverified
Commit
333d444e
authored
Nov 02, 2021
by
Igor Demin
Committed by
GitHub
Nov 02, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix memory leaks on macOs, enable Arc compilation (#340)
parent
ee8e4cc9
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
240 additions
and
306 deletions
+240
-306
build.gradle.kts
skiko/build.gradle.kts
+1
-0
NativeApplication.kt
...c/jvmMain/kotlin/org/jetbrains/skiko/NativeApplication.kt
+3
-0
MetalRedrawer.kt
...Main/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt
+10
-7
Drawlayer.mm
skiko/src/jvmMain/objectiveC/macos/Drawlayer.mm
+62
-55
MetalRedrawer.mm
skiko/src/jvmMain/objectiveC/macos/MetalRedrawer.mm
+111
-98
NativeApplication.mm
skiko/src/jvmMain/objectiveC/macos/NativeApplication.mm
+16
-0
OpenGLRedrawer.mm
skiko/src/jvmMain/objectiveC/macos/OpenGLRedrawer.mm
+0
-146
SkiaWindowTest.kt
.../src/jvmTest/kotlin/org/jetbrains/skiko/SkiaWindowTest.kt
+37
-0
No files found.
skiko/build.gradle.kts
View file @
333d444e
...
...
@@ -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"
,
...
...
skiko/src/jvmMain/kotlin/org/jetbrains/skiko/NativeApplication.kt
0 → 100644
View file @
333d444e
package
org.jetbrains.skiko
internal
external
fun
getApplicationWindowCount
():
Int
skiko/src/jvmMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt
View file @
333d444e
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
)
}
}
}
...
...
skiko/src/jvmMain/objectiveC/macos/Drawlayer.mm
View file @
333d444e
...
...
@@ -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 c
anvas
, jlong platformInfoPtr)
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_HardwareLayer_getWindowHandle(JNIEnv *env, jobject c
omponent
, 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 c
anvas
, jlong platformInfoPtr)
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_HardwareLayer_getContentHandle(JNIEnv *env, jobject c
omponent
, 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
skiko/src/jvmMain/objectiveC/macos/MetalRedrawer.mm
View file @
333d444e
...
...
@@ -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
...
...
skiko/src/jvmMain/objectiveC/macos/NativeApplication.mm
0 → 100644
View file @
333d444e
#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];
}
}
}
skiko/src/jvmMain/objectiveC/macos/OpenGLRedrawer.mm
deleted
100644 → 0
View file @
ee8e4cc9
#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
skiko/src/jvmTest/kotlin/org/jetbrains/skiko/SkiaWindowTest.kt
View file @
333d444e
...
...
@@ -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
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment