From 5ed970f08c3c6b6ff4fbbab74edd289fce16a54a Mon Sep 17 00:00:00 2001 From: Elijah Semyonov Date: Wed, 12 Jun 2024 11:03:02 +0200 Subject: [PATCH] Expose SkiaLayerProperties (#938) --- .../org/jetbrains/skiko/SkiaLayer.awt.kt | 13 +++++ .../jetbrains/skiko/redrawer/MetalRedrawer.kt | 12 ++++- .../awtMain/objectiveC/macos/MetalRedrawer.mm | 2 +- .../org/jetbrains/skia/BreakIteratorTests.kt | 4 +- .../kotlin/org/jetbrains/skia/FontTests.kt | 5 +- .../jetbrains/skiko/SkiaLayerProperties.kt | 54 ++++++++++++++++++- 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt b/skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt index 80bbf59ea..3e7fda6ec 100644 --- a/skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt +++ b/skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt @@ -71,6 +71,19 @@ actual open class SkiaLayer internal constructor( pixelGeometry ) + constructor( + externalAccessibleFactory: ((Component) -> Accessible)? = null, + properties: SkiaLayerProperties, + analytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty, + pixelGeometry: PixelGeometry = PixelGeometry.UNKNOWN, + ) : this( + externalAccessibleFactory, + properties, + RenderFactory.Default, + analytics, + pixelGeometry + ) + val canvas: java.awt.Canvas get() = backedLayer diff --git a/skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt b/skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt index 9812053f5..dc3806e3a 100644 --- a/skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt +++ b/skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt @@ -72,7 +72,7 @@ internal class MetalRedrawer( } _device = initDevice contextHandler = MetalContextHandler(layer, initDevice, adapter) - setVSyncEnabled(initDevice.ptr, properties.isVsyncEnabled) + setDisplaySyncEnabled(initDevice.ptr, properties.isVsyncEnabled) } override val renderInfo: String get() = contextHandler.rendererInfo() @@ -190,5 +190,13 @@ internal class MetalRedrawer( private external fun resizeLayers(device: Long, x: Int, y: Int, width: Int, height: Int) private external fun setLayerVisible(device: Long, isVisible: Boolean) private external fun setContentScale(device: Long, contentScale: Float) - private external fun setVSyncEnabled(device: Long, enabled: Boolean) + + /** + * Set this value to true to synchronize the presentation of the layer’s contents with the display’s refresh, + * also known as vsync or vertical sync. If false, the layer presents new content more quickly, + * but possibly with brief visual artifacts (screen tearing). + * + * @note see https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled + */ + private external fun setDisplaySyncEnabled(device: Long, enabled: Boolean) } diff --git a/skiko/src/awtMain/objectiveC/macos/MetalRedrawer.mm b/skiko/src/awtMain/objectiveC/macos/MetalRedrawer.mm index 998bb615b..805fe44b7 100644 --- a/skiko/src/awtMain/objectiveC/macos/MetalRedrawer.mm +++ b/skiko/src/awtMain/objectiveC/macos/MetalRedrawer.mm @@ -231,7 +231,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setConten } } -JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setVSyncEnabled(JNIEnv *env, jobject obj, jlong devicePtr, jboolean enabled) +JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setDisplaySyncEnabled(JNIEnv *env, jobject obj, jlong devicePtr, jboolean enabled) { @autoreleasepool { MetalDevice *device = (__bridge MetalDevice *) (void *) devicePtr; diff --git a/skiko/src/commonTest/kotlin/org/jetbrains/skia/BreakIteratorTests.kt b/skiko/src/commonTest/kotlin/org/jetbrains/skia/BreakIteratorTests.kt index 1e2194723..88b7b41fa 100644 --- a/skiko/src/commonTest/kotlin/org/jetbrains/skia/BreakIteratorTests.kt +++ b/skiko/src/commonTest/kotlin/org/jetbrains/skia/BreakIteratorTests.kt @@ -37,9 +37,9 @@ class BreakIteratorTests { @SkipJsTarget @SkipWasmTarget fun breakIteratorCloneTest() { - // Wasm and iOS builds of Skia do not include required data to implement those iterators, + // Wasm, iOS, and tvOS builds of Skia do not include required data to implement those iterators, // see `third_party/externals/icu/flutter/README.md`. - if (hostOs == OS.Ios) + if (hostOs == OS.Ios || hostOs == OS.Tvos) return if (isDebugModeOnJvm) diff --git a/skiko/src/commonTest/kotlin/org/jetbrains/skia/FontTests.kt b/skiko/src/commonTest/kotlin/org/jetbrains/skia/FontTests.kt index 98df3bd4a..9edc7949b 100644 --- a/skiko/src/commonTest/kotlin/org/jetbrains/skia/FontTests.kt +++ b/skiko/src/commonTest/kotlin/org/jetbrains/skia/FontTests.kt @@ -15,6 +15,7 @@ private fun isMac() = (hostOs == OS.MacOS) private fun isIos() = (hostOs == OS.Ios) private fun isLinux() = (hostOs == OS.Linux) private fun isWindows() = (hostOs == OS.Windows) +private fun isTvos() = (hostOs == OS.Tvos) private fun isJs() = (kotlinBackend == KotlinBackend.JS) private val COARSE_EPSILON = 2.4f private const val jbMonoPath = "./fonts/JetBrainsMono-Regular.ttf" @@ -91,6 +92,8 @@ class FontTests { assertEquals(if (isLinux() || isJs()) 26 else 24, firstGlyphPath.pointsCount) + //FontMetrics(_top=-11.64, _ascent=-11.64, _descent=3.24, _bottom=3.24, _leading=0.0, _avgCharWidth=29.46, _maxCharWidth=29.46, _xMin=-20.88, _xMax=8.58, _xHeight=6.6, _capHeight=8.64, _underlineThickness=0.54, _underlinePosition=1.44, _strikeoutThickness=0.54, _strikeoutPosition=-3.9), eps=0.01 + //FontMetrics(_top=-11.64, _ascent=-11.64, _descent=3.2400002, _bottom=3.2400002, _leading=0.0, _avgCharWidth=7.2, _maxCharWidth=29.460001, _xMin=-20.880001, _xMax=8.58, _xHeight=6.6000004, _capHeight=8.64, _underlineThickness=0.54, _underlinePosition=1.4399999, _strikeoutThickness=0.54, _strikeoutPosition=-3.8999999) assertCloseEnough(FontMetrics( top = -11.64f, ascent = -11.64f, @@ -99,7 +102,7 @@ class FontTests { leading = 0f, avgCharWidth = when { isJs() -> 7.2f - isIos() || isMac() -> 29.460001f + isIos() || isMac() || isTvos() -> 29.460001f isWindows() -> 0f else -> 7.2f }, diff --git a/skiko/src/jvmMain/kotlin/org/jetbrains/skiko/SkiaLayerProperties.kt b/skiko/src/jvmMain/kotlin/org/jetbrains/skiko/SkiaLayerProperties.kt index 2de5e781c..b3c1d0867 100644 --- a/skiko/src/jvmMain/kotlin/org/jetbrains/skiko/SkiaLayerProperties.kt +++ b/skiko/src/jvmMain/kotlin/org/jetbrains/skiko/SkiaLayerProperties.kt @@ -1,8 +1,58 @@ package org.jetbrains.skiko -internal data class SkiaLayerProperties( + +/** + * SkiaLayerProperties is a class that represents the rendering configuration for a SkiaLayer. + * + * @property isVsyncEnabled Specifies whether vertical synchronization (VSync) is enabled. + * Default value is [SkikoProperties.vsyncEnabled]. Setting this to true is a hint toward underlying implementation + * to synchronize the rendering with the display presentation. It guarantees that the frame is presented without + * visual artifacts like tearing in exchange for a possible latency increase. + * @property isVsyncFramelimitFallbackEnabled Specifies whether framelimit fallback is enabled (software renderer). + * Default value is [SkikoProperties.vsyncFramelimitFallbackEnabled]. + * @property renderApi Specifies the graphics API used for rendering. + * Default value is [SkikoProperties.renderApi]. + * @property adapterPriority Specifies the GPU that will be selected for rendering. + * Default value is [SkikoProperties.gpuPriority]. + */ +class SkiaLayerProperties( val isVsyncEnabled: Boolean = SkikoProperties.vsyncEnabled, val isVsyncFramelimitFallbackEnabled: Boolean = SkikoProperties.vsyncFramelimitFallbackEnabled, val renderApi: GraphicsApi = SkikoProperties.renderApi, val adapterPriority: GpuPriority = SkikoProperties.gpuPriority, -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + + val rhs = other as? SkiaLayerProperties ?: return false + + if (isVsyncEnabled != rhs.isVsyncEnabled) return false + if (isVsyncFramelimitFallbackEnabled != rhs.isVsyncFramelimitFallbackEnabled) return false + if (renderApi != rhs.renderApi) return false + if (adapterPriority != rhs.adapterPriority) return false + + return true + } + + fun copy( + isVsyncEnabled: Boolean = this.isVsyncEnabled, + isVsyncFramelimitFallbackEnabled: Boolean = this.isVsyncFramelimitFallbackEnabled, + renderApi: GraphicsApi = this.renderApi, + adapterPriority: GpuPriority = this.adapterPriority, + ): SkiaLayerProperties { + return SkiaLayerProperties( + isVsyncEnabled = isVsyncEnabled, + isVsyncFramelimitFallbackEnabled = isVsyncFramelimitFallbackEnabled, + renderApi = renderApi, + adapterPriority = adapterPriority, + ) + } + + override fun hashCode(): Int { + var result = isVsyncEnabled.hashCode() + result = 31 * result + isVsyncFramelimitFallbackEnabled.hashCode() + result = 31 * result + renderApi.hashCode() + result = 31 * result + adapterPriority.hashCode() + return result + } +}