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
internal class MetalRedrawer(
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
properties: SkiaLayerProperties
) : Redrawer {
private var isDisposed = false
private var disposeLock = Any()
......@@ -58,38 +58,41 @@ internal class MetalRedrawer(
}
private suspend fun draw() {
if (layer.prepareDrawContext()) {
// 2,3 GHz 8-Core Intel Core i9
//
// Test1. 8 windows, multiple clocks, 800x600
//
// Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 20 FPS, 130% CPU
// Dispatchers.IO: 58 FPS, 460% CPU
//
// Test2. 60 windows, single clock, 800x600
//
// Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 50 FPS, 150% CPU
// Dispatchers.IO: 50 FPS, 200% CPU
withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) {
val handle = startRendering()
if (layer.prepareDrawContext()) {
// 2,3 GHz 8-Core Intel Core i9
//
// Test1. 8 windows, multiple clocks, 800x600
//
// Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 20 FPS, 130% CPU
// Dispatchers.IO: 58 FPS, 460% CPU
//
// Test2. 60 windows, single clock, 800x600
//
// Executors.newSingleThreadExecutor().asCoroutineDispatcher(): 50 FPS, 150% CPU
// Dispatchers.IO: 50 FPS, 200% CPU
synchronized(disposeLock) {
if (!isDisposed) {
layer.draw()
}
}
}
// 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)
endRendering(handle)
}
// 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() {
val rootPane = getRootPane(layer)
val globalPosition = convertPoint(layer, layer.x, layer.y, rootPane)
setContentScale(device, layer.contentScale)
resizeLayers(device,
resizeLayers(
device,
globalPosition.x,
rootPane.height - globalPosition.y - layer.height,
layer.width.coerceAtLeast(0),
......@@ -104,7 +107,7 @@ internal class MetalRedrawer(
fun makeRenderTarget(width: Int, height: Int) = BackendRenderTarget(
makeMetalRenderTarget(device, width, height)
)
fun finishFrame() = finishFrame(device)
fun getAdapterPriority(): Int {
......@@ -130,4 +133,6 @@ internal class MetalRedrawer(
private external fun isOccluded(window: Long): Boolean
private external fun getAdapterName(device: Long): String
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
MetalDevice *device = (MetalDevice *) devicePtr;
GrBackendRenderTarget* renderTarget = NULL;
@autoreleasepool {
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);
}
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;
}
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() {
kern_return_t kernResult = 0;
io_iterator_t iterator = IO_OBJECT_NULL;
......@@ -241,18 +254,13 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFra
id<CAMetalDrawable> currentDrawable = device.drawableHandle;
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present";
[currentDrawable addPresentedHandler:^(id<MTLDrawable> dr) {
CFRelease(currentDrawable);
}];
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
device.drawableHandle = nil;
currentDrawable = 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(
......
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