Unverified Commit 64946688 authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

Fix memory leak. (#140)

parent b3fb7a60
...@@ -16,7 +16,7 @@ import javax.swing.SwingUtilities.getRootPane ...@@ -16,7 +16,7 @@ import javax.swing.SwingUtilities.getRootPane
internal class MetalRedrawer( internal class MetalRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private var isDisposed = false private var isDisposed = false
private var disposeLock = Any() private var disposeLock = Any()
...@@ -58,38 +58,41 @@ internal class MetalRedrawer( ...@@ -58,38 +58,41 @@ internal class MetalRedrawer(
} }
private suspend fun draw() { private suspend fun draw() {
if (layer.prepareDrawContext()) { withContext(Dispatchers.IO) {
// 2,3 GHz 8-Core Intel Core i9 val handle = startRendering()
// if (layer.prepareDrawContext()) {
// Test1. 8 windows, multiple clocks, 800x600 // 2,3 GHz 8-Core Intel Core i9
// //
// Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 20 FPS, 130% CPU // Test1. 8 windows, multiple clocks, 800x600
// Dispatchers.IO: 58 FPS, 460% CPU //
// // Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 20 FPS, 130% CPU
// Test2. 60 windows, single clock, 800x600 // Dispatchers.IO: 58 FPS, 460% CPU
// //
// Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 50 FPS, 150% CPU // Test2. 60 windows, single clock, 800x600
// Dispatchers.IO: 50 FPS, 200% CPU //
withContext(Dispatchers.IO) { // Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 50 FPS, 150% CPU
// Dispatchers.IO: 50 FPS, 200% CPU
synchronized(disposeLock) { synchronized(disposeLock) {
if (!isDisposed) { if (!isDisposed) {
layer.draw() layer.draw()
} }
} }
} }
// When window is not visible - it doesn't make sense to redraw fast to avoid battery drain. endRendering(handle)
// In theory, we could be more precise, and just suspend rendering in
// `NSWindowDidChangeOcclusionStateNotification`, but current approach seems to work as well in practise.
if (isOccluded(windowHandle))
delay(300)
} }
// 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.
if (isOccluded(windowHandle))
delay(300)
} }
override fun syncSize() { override fun syncSize() {
val rootPane = getRootPane(layer) val rootPane = getRootPane(layer)
val globalPosition = convertPoint(layer, layer.x, layer.y, rootPane) val globalPosition = convertPoint(layer, layer.x, layer.y, rootPane)
setContentScale(device, layer.contentScale) setContentScale(device, layer.contentScale)
resizeLayers(device, resizeLayers(
device,
globalPosition.x, globalPosition.x,
rootPane.height - globalPosition.y - layer.height, rootPane.height - globalPosition.y - layer.height,
layer.width.coerceAtLeast(0), layer.width.coerceAtLeast(0),
...@@ -104,7 +107,7 @@ internal class MetalRedrawer( ...@@ -104,7 +107,7 @@ internal class MetalRedrawer(
fun makeRenderTarget(width: Int, height: Int) = BackendRenderTarget( fun makeRenderTarget(width: Int, height: Int) = BackendRenderTarget(
makeMetalRenderTarget(device, width, height) makeMetalRenderTarget(device, width, height)
) )
fun finishFrame() = finishFrame(device) fun finishFrame() = finishFrame(device)
fun getAdapterPriority(): Int { fun getAdapterPriority(): Int {
...@@ -130,4 +133,6 @@ internal class MetalRedrawer( ...@@ -130,4 +133,6 @@ internal class MetalRedrawer(
private external fun isOccluded(window: Long): Boolean private external fun isOccluded(window: Long): Boolean
private external fun getAdapterName(device: Long): String private external fun getAdapterName(device: Long): String
private external fun getAdapterMemorySize(device: Long): Long private external fun getAdapterMemorySize(device: Long): Long
private external fun startRendering(): Long
private external fun endRendering(handle: Long)
} }
...@@ -101,17 +101,30 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMeta ...@@ -101,17 +101,30 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMeta
MetalDevice *device = (MetalDevice *) devicePtr; MetalDevice *device = (MetalDevice *) devicePtr;
GrBackendRenderTarget* renderTarget = NULL; GrBackendRenderTarget* renderTarget = NULL;
@autoreleasepool { 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(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_autoreleasePoolPop(void*);
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_startRendering(
JNIEnv * env, jobject redrawer)
{
return (jlong)objc_autoreleasePoolPush();
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_endRendering(
JNIEnv * env, jobject redrawer, jlong handle)
{
objc_autoreleasePoolPop((void*)handle);
}
BOOL isUsingIntegratedGPU() { BOOL isUsingIntegratedGPU() {
kern_return_t kernResult = 0; kern_return_t kernResult = 0;
io_iterator_t iterator = IO_OBJECT_NULL; io_iterator_t iterator = IO_OBJECT_NULL;
...@@ -241,18 +254,13 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFra ...@@ -241,18 +254,13 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFra
id<CAMetalDrawable> currentDrawable = device.drawableHandle; id<CAMetalDrawable> currentDrawable = device.drawableHandle;
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer]; if (currentDrawable) {
commandBuffer.label = @"Present"; id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present";
[currentDrawable addPresentedHandler:^(id<MTLDrawable> dr) { [commandBuffer presentDrawable:currentDrawable];
CFRelease(currentDrawable); [commandBuffer commit];
}]; device.drawableHandle = nil;
[commandBuffer presentDrawable:currentDrawable]; }
[commandBuffer commit];
device.drawableHandle = nil;
currentDrawable = nil;
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice(
......
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