Unverified Commit 53f70be8 authored by Elijah Semyonov's avatar Elijah Semyonov Committed by GitHub

Revert "Fix for losing presented drawables on JVM Mac OS (#708)" (#710)

This reverts commit 85aef666.
parent 85aef666
...@@ -73,6 +73,8 @@ internal class MetalRedrawer( ...@@ -73,6 +73,8 @@ internal class MetalRedrawer(
override val renderInfo: String get() = contextHandler.rendererInfo() override val renderInfo: String get() = contextHandler.rendererInfo()
private val windowHandle = layer.windowHandle
private val frameDispatcher = FrameDispatcher(MainUIDispatcher) { private val frameDispatcher = FrameDispatcher(MainUIDispatcher) {
if (layer.isShowing) { if (layer.isShowing) {
update(System.nanoTime()) update(System.nanoTime())
...@@ -84,10 +86,6 @@ internal class MetalRedrawer( ...@@ -84,10 +86,6 @@ internal class MetalRedrawer(
onContextInit() onContextInit()
} }
fun drawSync() {
frameDispatcher.scheduleFrame()
}
override fun dispose() = synchronized(drawLock) { override fun dispose() = synchronized(drawLock) {
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose() contextHandler.dispose()
...@@ -135,13 +133,10 @@ internal class MetalRedrawer( ...@@ -135,13 +133,10 @@ internal class MetalRedrawer(
// 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.
if (isOccluded()) if (isOccluded(windowHandle))
delay(300) delay(300)
} }
/**
* Avoid calling this method on main thread, it will sleep before next vsync happens if invoked multiple times.
*/
private fun performDraw() = synchronized(drawLock) { private fun performDraw() = synchronized(drawLock) {
if (!isDisposed) { if (!isDisposed) {
val handle = startRendering() val handle = startRendering()
...@@ -171,8 +166,6 @@ internal class MetalRedrawer( ...@@ -171,8 +166,6 @@ internal class MetalRedrawer(
setLayerVisible(device.ptr, isVisible) setLayerVisible(device.ptr, isVisible)
} }
private fun isOccluded() = isOccluded(device.ptr)
private external fun chooseAdapter(adapterPriority: Int): Long private external fun chooseAdapter(adapterPriority: Int): Long
private external fun createMetalDevice(window:Long, transparency: Boolean, adapter: Long, platformInfo: Long): Long private external fun createMetalDevice(window:Long, transparency: Boolean, adapter: Long, platformInfo: Long): Long
private external fun disposeDevice(device: Long) private external fun disposeDevice(device: Long)
...@@ -180,7 +173,7 @@ internal class MetalRedrawer( ...@@ -180,7 +173,7 @@ internal class MetalRedrawer(
private external fun setLayerVisible(device: Long, isVisible: Boolean) private external fun setLayerVisible(device: Long, isVisible: Boolean)
private external fun setContentScale(device: Long, contentScale: Float) private external fun setContentScale(device: Long, contentScale: Float)
private external fun setVSyncEnabled(device: Long, enabled: Boolean) private external fun setVSyncEnabled(device: Long, enabled: Boolean)
private external fun isOccluded(device: Long): Boolean private external fun isOccluded(window: Long): Boolean
private external fun getAdapterName(adapter: Long): String private external fun getAdapterName(adapter: Long): String
private external fun getAdapterMemorySize(adapter: Long): Long private external fun getAdapterMemorySize(adapter: Long): Long
private external fun startRendering(): Long private external fun startRendering(): Long
......
#ifdef SK_METAL
#ifndef SK_AWT_METAL_LAYER_H
#define SK_AWT_METAL_LAYER_H
#import <QuartzCore/CAMetalLayer.h>
#import <jni.h>
@interface AWTMetalLayer : CAMetalLayer
@property jobject javaRef;
@end
#endif // SK_AWT_METAL_LAYER_H
#endif // SK_METAL
\ No newline at end of file
#import "AWTMetalLayer.h"
@implementation AWTMetalLayer
- (id)init {
self = [super init];
if (self) {
[self removeAllAnimations];
[self setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[self setNeedsDisplayOnBoundsChange: YES];
}
return self;
}
@end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#import <jawt.h> #import <jawt.h>
#import <jawt_md.h> #import <jawt_md.h>
#import <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.h>
#import <GrDirectContext.h> #import <GrDirectContext.h>
#import <mtl/GrMtlBackendContext.h> #import <mtl/GrMtlBackendContext.h>
#import <mtl/GrMtlTypes.h> #import <mtl/GrMtlTypes.h>
...@@ -11,7 +13,6 @@ ...@@ -11,7 +13,6 @@
extern "C" extern "C"
{ {
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_context_MetalContextHandler_makeMetalContext( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_context_MetalContextHandler_makeMetalContext(
JNIEnv* env, jobject contextHandler, jlong devicePtr) JNIEnv* env, jobject contextHandler, jlong devicePtr)
{ {
...@@ -29,15 +30,10 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_context_MetalContextHandler_mak ...@@ -29,15 +30,10 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_context_MetalContextHandler_mak
{ {
@autoreleasepool { @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr; MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr;
[device recreateDisplayLinkIfNeeded];
[device waitUntilVsync];
[device waitForQueueSlot];
GrBackendRenderTarget* renderTarget = NULL; GrBackendRenderTarget* renderTarget = NULL;
id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable]; id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable];
if (!currentDrawable) { if (!currentDrawable) {
[device freeQueueSlot];
return NULL; return NULL;
} }
device.drawableHandle = currentDrawable; device.drawableHandle = currentDrawable;
...@@ -60,9 +56,6 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_context_MetalContextHandler_fini ...@@ -60,9 +56,6 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_context_MetalContextHandler_fini
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer]; id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present"; commandBuffer.label = @"Present";
[commandBuffer presentDrawable:currentDrawable]; [commandBuffer presentDrawable:currentDrawable];
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
[device freeQueueSlot];
}];
[commandBuffer commit]; [commandBuffer commit];
device.drawableHandle = nil; device.drawableHandle = nil;
} }
......
#ifdef SK_METAL #ifdef SK_METAL
#ifndef SK_METAL_DEVICE_H @interface AWTMetalLayer : CAMetalLayer
#define SK_METAL_DEVICE_H
#import <QuartzCore/QuartzCore.h> @property jobject javaRef;
#import <AppKit/AppKit.h>
#import <Metal/Metal.h>
#import "AWTMetalLayer.h" @end
@interface MetalDevice : NSObject @interface MetalDevice : NSObject
@property (weak) CALayer *container; @property (weak) CALayer *container;
@property (strong) NSWindow *window; @property (retain, strong) AWTMetalLayer *layer;
@property (strong) AWTMetalLayer *layer; @property (retain, strong) id<MTLDevice> adapter;
@property (strong) id<MTLDevice> adapter; @property (retain, strong) id<MTLCommandQueue> queue;
@property (strong) id<MTLCommandQueue> queue; @property (retain, strong) id<CAMetalDrawable> drawableHandle;
@property (strong) id<CAMetalDrawable> drawableHandle;
- (instancetype)initWithContainer:(CALayer *)container adapter:(id<MTLDevice>)adapter window:(NSWindow *)window;
- (void)recreateDisplayLinkIfNeeded;
- (void)handleDisplayLinkFired;
- (void)waitUntilVsync;
- (void)waitForQueueSlot;
- (void)freeQueueSlot;
@end @end
#endif // SK_AWT_METAL_DEVICE_H #endif
\ No newline at end of file
#endif // SK_METAL
\ No newline at end of file
#import "MetalDevice.h"
#import <stdatomic.h>
static CVReturn MetalDeviceDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
MetalDevice *device = (__bridge MetalDevice *)displayLinkContext;
[device handleDisplayLinkFired];
return kCVReturnSuccess;
}
@implementation MetalDevice {
NSScreen *_displayLinkScreen;
CVDisplayLinkRef _displayLink;
dispatch_semaphore_t _presentingBuffersExhaustionSemaphore;
NSConditionLock *_vsyncConditionLock;
volatile atomic_bool _displayLinkOk;
}
- (instancetype)initWithContainer:(CALayer *)container adapter:(id<MTLDevice>)adapter window:(NSWindow *)window {
self = [super init];
if (self) {
self.container = container;
self.adapter = adapter;
self.queue = [adapter newCommandQueue];
self.window = window;
self.layer = [AWTMetalLayer new];
_displayLinkScreen = nil;
[container removeAllAnimations];
[container setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[container setNeedsDisplayOnBoundsChange: YES];
self.layer.device = adapter;
self.layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
self.layer.contentsGravity = kCAGravityTopLeft;
CGFloat transparent[] = { 0.0f, 0.0f, 0.0f, 0.0f };
self.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), transparent);
self.layer.opaque = NO;
[container addSublayer: self.layer];
_presentingBuffersExhaustionSemaphore = dispatch_semaphore_create(3);
_vsyncConditionLock = [[NSConditionLock alloc] initWithCondition: 1];
atomic_store(&_displayLinkOk, true);
}
return self;
}
- (void)dealloc {
[self invalidateDisplayLink];
}
- (void)handleDisplayLinkSetupFailure {
atomic_store(&_displayLinkOk, false);
/// Next line is here to tackle edge case where displayLink reconstruction failed
/// but someone is already waiting at `waitUntilVsync`
/// It's quite improbable scenario anyway
[self handleDisplayLinkFired];
}
- (void)recreateDisplayLinkIfNeeded {
if ([self.window.screen isEqualTo: _displayLinkScreen]) {
return;
}
[self invalidateDisplayLink];
_displayLinkScreen = self.window.screen;
NSDictionary* screenDescription = [_displayLinkScreen deviceDescription];
NSNumber* screenID = [screenDescription objectForKey:@"NSScreenNumber"];
/// TODO: create fallback for any possible failure
CVReturn result;
CVDisplayLinkRef displayLink;
result = CVDisplayLinkCreateWithCGDisplay([screenID unsignedIntValue], &displayLink);
if (result != kCVReturnSuccess) {
[self handleDisplayLinkSetupFailure];
return;
}
result = CVDisplayLinkSetOutputCallback(displayLink, &MetalDeviceDisplayLinkCallback, (__bridge void *)(self));
if (result != kCVReturnSuccess) {
[self handleDisplayLinkSetupFailure];
return;
}
_displayLink = displayLink;
result = CVDisplayLinkStart(displayLink);
if (result != kCVReturnSuccess) {
[self handleDisplayLinkSetupFailure];
return;
}
atomic_store(&_displayLinkOk, true);
NSLog(@"DisplayLink launched for screen with ID: %@", screenID);
}
- (void)handleDisplayLinkFired {
[_vsyncConditionLock lock];
[_vsyncConditionLock unlockWithCondition:1];
}
- (void)waitUntilVsync {
bool displayLinkOk = atomic_load(&_displayLinkOk);
/// If display link construction was corrupted, don't perform any waiting
if (!displayLinkOk) {
return;
}
[_vsyncConditionLock lockWhenCondition:1];
[_vsyncConditionLock unlockWithCondition:0];
}
- (void)waitForQueueSlot {
/// In case we receive more encoded command buffers, than gpu can handle (GPU bottleneck),
/// we need to throttle it down and start a new frame only when one currently run is finished
///
/// see call place of `freeQueueSlot`
dispatch_semaphore_wait(_presentingBuffersExhaustionSemaphore, DISPATCH_TIME_FOREVER);
}
- (void)freeQueueSlot {
dispatch_semaphore_signal(_presentingBuffersExhaustionSemaphore);
}
- (void)invalidateDisplayLink {
if (_displayLink) {
CVDisplayLinkStop(_displayLink);
CVDisplayLinkRelease(_displayLink);
_displayLink = nil;
}
}
@end
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
#import <jawt.h> #import <jawt.h>
#import <jawt_md.h> #import <jawt_md.h>
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#import <GrBackendSurface.h> #import <GrBackendSurface.h>
#import <GrDirectContext.h> #import <GrDirectContext.h>
#import <mtl/GrMtlBackendContext.h> #import <mtl/GrMtlBackendContext.h>
...@@ -18,11 +23,47 @@ ...@@ -18,11 +23,47 @@
#define AdpapterPriorityIntegrated 1 #define AdpapterPriorityIntegrated 1
#define AdpapterPriorityDiscrete 2 #define AdpapterPriorityDiscrete 2
@implementation AWTMetalLayer
- (id)init
{
self = [super init];
assert(self != NULL);
[self removeAllAnimations];
[self setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[self setNeedsDisplayOnBoundsChange: YES];
return self;
}
@end
@implementation MetalDevice
- (id) init
{
self = [super init];
if (self)
{
self.layer = nil;
self.adapter = nil;
self.queue = nil;
self.drawableHandle = nil;
}
return self;
}
@end
extern "C" extern "C"
{ {
void *objc_autoreleasePoolPush(void); extern "C" void* objc_autoreleasePoolPush(void);
void objc_autoreleasePoolPop(void *); extern "C" void objc_autoreleasePoolPop(void*);
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_startRendering( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_startRendering(
JNIEnv * env, jobject redrawer) JNIEnv * env, jobject redrawer)
...@@ -31,9 +72,9 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_startRen ...@@ -31,9 +72,9 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_startRen
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_endRendering( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_endRendering(
JNIEnv *env, jobject redrawer, jlong handle) JNIEnv * env, jobject redrawer, jlong handle)
{ {
objc_autoreleasePoolPop((void *)handle); objc_autoreleasePoolPop((void*)handle);
} }
BOOL isUsingIntegratedGPU() { BOOL isUsingIntegratedGPU() {
...@@ -107,17 +148,38 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMe ...@@ -107,17 +148,38 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMe
{ {
@autoreleasepool { @autoreleasepool {
id<MTLDevice> adapter = (__bridge_transfer id<MTLDevice>) (void *) adapterPtr; id<MTLDevice> adapter = (__bridge_transfer id<MTLDevice>) (void *) adapterPtr;
MetalDevice *device = [MetalDevice new];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) (void*) platformInfoPtr; NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) (void*) platformInfoPtr;
NSWindow *window = (__bridge NSWindow *) (void *) windowPtr;
CALayer *container = [dsi_mac windowLayer]; 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);
MetalDevice *device = [[MetalDevice alloc] initWithContainer:container adapter:adapter window:window]; id<MTLCommandQueue> fQueue = [adapter newCommandQueue];
device.layer.javaRef = env->NewGlobalRef(redrawer); device.container = container;
device.layer = layer;
device.adapter = adapter;
device.queue = fQueue;
device.layer.device = device.adapter;
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) if (transparency)
{ {
NSWindow* window = (__bridge NSWindow*) (void *) windowPtr;
window.hasShadow = NO; window.hasShadow = NO;
} }
...@@ -220,11 +282,10 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapt ...@@ -220,11 +282,10 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_getAdapt
} }
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 devicePtr) { JNIEnv *env, jobject redrawer, jlong windowPtr) {
@autoreleasepool { @autoreleasepool {
MetalDevice *device = (__bridge MetalDevice *)(void *)devicePtr; NSWindow* window = (__bridge NSWindow*) (void *) windowPtr;
return ([window occlusionState] & NSWindowOcclusionStateVisible) == 0;
return ([device.window occlusionState] & NSWindowOcclusionStateVisible) == 0;
} }
} }
......
...@@ -2,7 +2,6 @@ package org.jetbrains.skiko ...@@ -2,7 +2,6 @@ package org.jetbrains.skiko
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
import org.jetbrains.skia.Canvas import org.jetbrains.skia.Canvas
import org.jetbrains.skia.FontMgr import org.jetbrains.skia.FontMgr
...@@ -13,7 +12,6 @@ import org.jetbrains.skia.paragraph.ParagraphBuilder ...@@ -13,7 +12,6 @@ import org.jetbrains.skia.paragraph.ParagraphBuilder
import org.jetbrains.skia.paragraph.ParagraphStyle import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.TextStyle import org.jetbrains.skia.paragraph.TextStyle
import org.jetbrains.skiko.context.JvmContextHandler import org.jetbrains.skiko.context.JvmContextHandler
import org.jetbrains.skiko.redrawer.MetalRedrawer
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.UiTestScope import org.jetbrains.skiko.util.UiTestScope
...@@ -23,11 +21,12 @@ import org.junit.Assert.assertEquals ...@@ -23,11 +21,12 @@ import org.junit.Assert.assertEquals
import org.junit.Assume.assumeTrue import org.junit.Assume.assumeTrue
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.Ignore
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Color import java.awt.Color
import java.awt.Dimension import java.awt.Dimension
import java.awt.event.* import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.awt.event.WindowEvent
import javax.swing.JFrame import javax.swing.JFrame
import javax.swing.JLayeredPane import javax.swing.JLayeredPane
import javax.swing.JPanel import javax.swing.JPanel
...@@ -36,8 +35,6 @@ import javax.swing.WindowConstants ...@@ -36,8 +35,6 @@ import javax.swing.WindowConstants
import kotlin.random.Random import kotlin.random.Random
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@Suppress("BlockingMethodInNonBlockingContext", "SameParameterValue") @Suppress("BlockingMethodInNonBlockingContext", "SameParameterValue")
class SkiaLayerTest { class SkiaLayerTest {
...@@ -95,69 +92,6 @@ class SkiaLayerTest { ...@@ -95,69 +92,6 @@ class SkiaLayerTest {
) )
} }
@OptIn(ExperimentalTime::class)
@Ignore
@Test
fun `frame is rendered immediately`() = uiTest {
val window = UiTestWindow(
properties = SkiaLayerProperties(
isVsyncEnabled = true
)
)
val colors = arrayOf(
Color.RED,
Color.GREEN,
Color.BLUE,
Color.YELLOW
)
var counter1 = 0
var counter2 = 0
val paint = Paint()
try {
window.setLocation(200, 200)
window.setSize(400, 600)
window.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
window.layer.skikoView = object : SkikoView {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val c1 = counter1
val c2 = counter2
paint.color = colors[c1.mod(colors.size)].rgb
canvas.drawRect(Rect(0f, 0f, width.toFloat(), height / 2f), paint)
paint.color = colors[c2.mod(colors.size)].rgb
canvas.drawRect(Rect(0f, height / 2f, width.toFloat(), height.toFloat()), paint)
}
}
window.isVisible = true
window.addKeyListener(object : KeyAdapter() {
override fun keyTyped(e: KeyEvent?) {
launch {
val redrawer = window.layer.redrawer as MetalRedrawer
redrawer.drawSync()
counter1 += 1
redrawer.drawSync()
counter2 += 1
redrawer.drawSync()
}
}
});
window.addWindowListener(object : WindowAdapter() {
override fun windowActivated(e: WindowEvent?) {
window.requestFocus()
}
})
delay(Duration.INFINITE)
} finally {
window.close()
}
}
@Test @Test
fun `render single window`() = uiTest { fun `render single window`() = uiTest {
val window = UiTestWindow() val window = UiTestWindow()
......
...@@ -10,10 +10,9 @@ import javax.swing.JFrame ...@@ -10,10 +10,9 @@ import javax.swing.JFrame
internal fun uiTest(block: suspend UiTestScope.() -> Unit) { internal fun uiTest(block: suspend UiTestScope.() -> Unit) {
assumeFalse(GraphicsEnvironment.isHeadless()) assumeFalse(GraphicsEnvironment.isHeadless())
// assumeTrue(System.getProperty("skiko.test.ui.enabled", "false") == "true") assumeTrue(System.getProperty("skiko.test.ui.enabled", "false") == "true")
// val renderApi = System.getProperty("skiko.test.ui.renderApi", "all") val renderApi = System.getProperty("skiko.test.ui.renderApi", "all")
val renderApi = "metal"
runBlocking(MainUIDispatcher) { runBlocking(MainUIDispatcher) {
if (renderApi == "all") { if (renderApi == "all") {
......
...@@ -13,11 +13,11 @@ import kotlin.coroutines.CoroutineContext ...@@ -13,11 +13,11 @@ import kotlin.coroutines.CoroutineContext
*/ */
class FrameDispatcher( class FrameDispatcher(
scope: CoroutineScope, scope: CoroutineScope,
private val onFrame: suspend () -> Unit, private val onFrame: suspend () -> Unit
) { ) {
constructor( constructor(
context: CoroutineContext, context: CoroutineContext,
onFrame: suspend () -> Unit, onFrame: suspend () -> Unit
) : this( ) : this(
CoroutineScope(context), CoroutineScope(context),
onFrame onFrame
...@@ -28,9 +28,7 @@ class FrameDispatcher( ...@@ -28,9 +28,7 @@ class FrameDispatcher(
private val job = scope.launch { private val job = scope.launch {
while (true) { while (true) {
// Await for draw request
frameChannel.receive() frameChannel.receive()
frameScheduled = false frameScheduled = false
onFrame() onFrame()
// As per `yield()` documentation: // As per `yield()` documentation:
......
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