Unverified Commit 901d632a authored by ApoloApps's avatar ApoloApps Committed by GitHub

Hot Paths optimizations for less allocations (#1156)

Improve performance of critical paths by preventing allocating
intermediate objects like lists, arrays,.... (chunked, map,
to*Collection)

---------
Co-authored-by: 's avatarIvan Matkov <matkov.ivan@gmail.com>
Co-authored-by: 's avatarAlexander Maryanovsky <msasha@gmail.com>
parent 29582041
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.internal.fastForEachReversed
internal interface CloseScope { internal interface CloseScope {
fun <T : AutoCloseable> T.autoClose(): T fun <T : AutoCloseable> T.autoClose(): T
} }
...@@ -33,8 +35,7 @@ internal fun autoCloseScope(body: CloseScope.() -> Unit) { ...@@ -33,8 +35,7 @@ internal fun autoCloseScope(body: CloseScope.() -> Unit) {
try { try {
scope.body() scope.body()
} finally { } finally {
for (index in resources.indices.reversed()) { resources.fastForEachReversed { item ->
val item = resources[index]
item.close() item.close()
} }
} }
......
...@@ -5,6 +5,7 @@ import kotlinx.coroutines.GlobalScope ...@@ -5,6 +5,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skiko.internal.fastForEach
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.RedrawerManager import org.jetbrains.skiko.redrawer.RedrawerManager
import java.awt.Color import java.awt.Color
...@@ -384,8 +385,8 @@ actual open class SkiaLayer internal constructor( ...@@ -384,8 +385,8 @@ actual open class SkiaLayer internal constructor(
private fun notifyChange(kind: PropertyKind) { private fun notifyChange(kind: PropertyKind) {
stateChangeListeners[kind]?.let { handlers -> stateChangeListeners[kind]?.let { handlers ->
for (index in handlers.indices) { handlers.fastForEach { handler ->
handlers[index].invoke(this) handler.invoke(this)
} }
} }
} }
......
...@@ -9,7 +9,7 @@ import java.awt.GraphicsConfiguration ...@@ -9,7 +9,7 @@ import java.awt.GraphicsConfiguration
import javax.accessibility.AccessibleContext import javax.accessibility.AccessibleContext
import javax.swing.JPanel import javax.swing.JPanel
import javax.swing.SwingUtilities.isEventDispatchThread import javax.swing.SwingUtilities.isEventDispatchThread
import org.jetbrains.skiko.internal.fastForEach
/** /**
* Swing component that draws content provided by [renderDelegate] with GPU acceleration using Skia engine. * Swing component that draws content provided by [renderDelegate] with GPU acceleration using Skia engine.
* *
...@@ -44,8 +44,7 @@ open class SkiaSwingLayer( ...@@ -44,8 +44,7 @@ open class SkiaSwingLayer(
private val renderDelegateWithClipping = SkikoRenderDelegate { canvas, width, height, nanoTime -> private val renderDelegateWithClipping = SkikoRenderDelegate { canvas, width, height, nanoTime ->
val scale = graphicsConfiguration.defaultTransform.scaleX.toFloat() val scale = graphicsConfiguration.defaultTransform.scaleX.toFloat()
// clipping // clipping
for (index in clipComponents.indices) { clipComponents.fastForEach { component ->
val component = clipComponents[index]
canvas.cutoutFromClip(component, scale) canvas.cutoutFromClip(component, scale)
} }
renderDelegate.onRender(canvas, width, height, nanoTime) renderDelegate.onRender(canvas, width, height, nanoTime)
......
...@@ -2,6 +2,7 @@ package org.jetbrains.skia ...@@ -2,6 +2,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.InteropScope import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skiko.internal.unpackTo
class FontFeature(val _tag: Int, val value: Int, val start: UInt, val end: UInt) { class FontFeature(val _tag: Int, val value: Int, val start: UInt, val end: UInt) {
...@@ -141,12 +142,13 @@ class FontFeature(val _tag: Int, val value: Int, val start: UInt, val end: UInt) ...@@ -141,12 +142,13 @@ class FontFeature(val _tag: Int, val value: Int, val start: UInt, val end: UInt)
internal fun InteropScope.arrayOfFontFeaturesToInterop(fontFeatures: Array<FontFeature>?): InteropPointer { internal fun InteropScope.arrayOfFontFeaturesToInterop(fontFeatures: Array<FontFeature>?): InteropPointer {
val ints = IntArray(4 * (fontFeatures?.size ?: 0)) val ints = IntArray(4 * (fontFeatures?.size ?: 0))
fontFeatures?.forEachIndexed { ix, fontFeature -> fontFeatures?.unpackTo(ints) { feature , arr, index ->
val j = ix * 4 arr[index] = feature._tag
ints[j] = fontFeature._tag arr[index + 1] = feature.value
ints[j + 1] = fontFeature.value arr[index + 2] = feature.start.toInt()
ints[j + 2] = fontFeature.start.toInt() arr[index + 3] = feature.end.toInt()
ints[j + 3] = fontFeature.end.toInt() //Stepping 4 places for the next feature, because every feature is represented by 4 ints
index + 4
} }
return toInterop(ints) return toInterop(ints)
......
...@@ -113,9 +113,14 @@ open class Rect constructor(val left: Float, val top: Float, val right: Float, v ...@@ -113,9 +113,14 @@ open class Rect constructor(val left: Float, val top: Float, val right: Float, v
internal fun fromInteropPointer(size: Int, block: InteropScope.(InteropPointer) -> Unit): Array<Rect> { internal fun fromInteropPointer(size: Int, block: InteropScope.(InteropPointer) -> Unit): Array<Rect> {
val result = withResult(FloatArray(size), block) val result = withResult(FloatArray(size), block)
return result.toList().chunked(4).map { (left, top, right, bottom) -> return Array(size / 4) { i ->
Rect(left, right, top, bottom) val offset = i * 4
}.toTypedArray() val left = result[offset]
val top = result[offset + 1]
val right = result[offset + 2]
val bottom = result[offset + 3]
Rect(left = left, top = top, right = right, bottom = bottom)
}
} }
internal fun fromInteropPointerNullable(block: (InteropPointer) -> Boolean): Rect? { internal fun fromInteropPointerNullable(block: (InteropPointer) -> Boolean): Rect? {
......
...@@ -2,6 +2,7 @@ package org.jetbrains.skia ...@@ -2,6 +2,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.* import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skiko.internal.unpackTo
/** /**
* Describes a set of pixels as either one integer rectangle or a run-length encoded * Describes a set of pixels as either one integer rectangle or a run-length encoded
...@@ -168,11 +169,13 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -168,11 +169,13 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
fun setRects(rects: Array<IRect>): Boolean { fun setRects(rects: Array<IRect>): Boolean {
return try { return try {
val arr = IntArray(rects.size * 4) val arr = IntArray(rects.size * 4)
for (i in rects.indices) { rects.unpackTo(arr) { rect, destination, i ->
arr[i * 4] = rects[i].left destination[i] = rect.left
arr[i * 4 + 1] = rects[i].top destination[i + 1] = rect.top
arr[i * 4 + 2] = rects[i].right destination[i + 2] = rect.right
arr[i * 4 + 3] = rects[i].bottom destination[i + 3] = rect.bottom
//Stepping 4 places for the next rect
i + 4
} }
Stats.onNativeCall() Stats.onNativeCall()
interopScope { interopScope {
......
...@@ -2,6 +2,7 @@ package org.jetbrains.skia ...@@ -2,6 +2,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.* import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skiko.internal.unpackTo
class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) { class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) {
companion object { companion object {
...@@ -150,13 +151,18 @@ class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) { ...@@ -150,13 +151,18 @@ class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) {
*/ */
fun makeClone(variations: Array<FontVariation>, collectionIndex: Int = 0): Typeface { fun makeClone(variations: Array<FontVariation>, collectionIndex: Int = 0): Typeface {
return try { return try {
if (variations.size == 0) return this if (variations.isEmpty()) return this
Stats.onNativeCall() Stats.onNativeCall()
val variationsData = variations.asList().flatMap { listOf(it._tag, it.value.toRawBits()) }.toIntArray() val variationsCount = variations.size * 2
val ptr = val variationsData = variations.unpackTo(IntArray(variationsCount)) { variation, arr, idx ->
interopScope { _nMakeClone(_ptr, toInterop(variationsData), 2 * variations.size, collectionIndex) } arr[idx] = variation._tag
arr[idx + 1] = variation.value.toRawBits()
//Stepping 2 places for the next variation
idx + 2
}
val ptr = interopScope { _nMakeClone(_ptr, toInterop(variationsData), variationsCount, collectionIndex) }
require(ptr != NullPointer) { require(ptr != NullPointer) {
"Failed to clone Typeface $this with $variations" "Failed to clone Typeface $this with ${variations.contentToString()}"
} }
Typeface(ptr) Typeface(ptr)
} finally { } finally {
......
...@@ -55,9 +55,16 @@ class Shadow(val color: Int, val offsetX: Float, val offsetY: Float, val blurSig ...@@ -55,9 +55,16 @@ class Shadow(val color: Int, val offsetX: Float, val offsetY: Float, val blurSig
companion object companion object
} }
internal fun Shadow.Companion.fromInteropPointer(shadowsCount: Int, block: InteropScope.(InteropPointer) -> Unit): Array<Shadow> { internal inline fun Shadow.Companion.fromInteropPointer(shadowsCount: Int, block: InteropScope.(InteropPointer) -> Unit): Array<Shadow> {
return withResult(IntArray(shadowsCount * 5), block).toList().chunked(5).map { (color, offsetX, offsetY, blurSigmaA, blurSigmaB) -> val data = withResult(IntArray(shadowsCount * 5), block)
return Array(shadowsCount) { i ->
val offset = i * 5
val color = data[offset]
val offsetX = data[offset + 1]
val offsetY = data[offset + 2]
val blurSigmaA = data[offset + 3]
val blurSigmaB = data[offset + 4]
val blurSigma = (blurSigmaA.toLong() shl 32) or (blurSigmaB.toLong() and 0xFFFFFFFFL) val blurSigma = (blurSigmaA.toLong() shl 32) or (blurSigmaB.toLong() and 0xFFFFFFFFL)
Shadow(color, Float.fromBits(offsetX), Float.fromBits(offsetY), Double.fromBits(blurSigma)) Shadow(color, Float.fromBits(offsetX), Float.fromBits(offsetY), Double.fromBits(blurSigma))
}.toTypedArray() }
} }
\ No newline at end of file
package org.jetbrains.skiko.internal
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> Array<T>.unpackTo(destinationArray: R, unpackItem: (T, R, Int) -> Int): R {
contract {
callsInPlace(unpackItem)
}
var targetIndex = 0
for (i in indices) {
targetIndex = unpackItem(this[i], destinationArray, targetIndex)
}
return destinationArray
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
contract { callsInPlace(action) }
for (index in indices) {
val item = get(index)
action(item)
}
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEachReversed(action: (T) -> Unit) {
contract { callsInPlace(action) }
for (index in indices.reversed()) {
val item = get(index)
action(item)
}
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEachIndexed(action: (Int, T) -> Unit) {
contract { callsInPlace(action) }
for (index in indices) {
val item = get(index)
action(index, item)
}
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastAny(predicate: (T) -> Boolean): Boolean {
contract { callsInPlace(predicate) }
fastForEach { if (predicate(it)) return true }
return false
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastNone(predicate: (T) -> Boolean): Boolean {
contract { callsInPlace(predicate) }
fastForEach { if (predicate(it)) return false }
return true
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastAll(predicate: (T) -> Boolean): Boolean {
contract { callsInPlace(predicate) }
fastForEach { if (!predicate(it)) return false }
return true
}
\ No newline at end of file
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
package org.jetbrains.skia.impl package org.jetbrains.skia.impl
import org.jetbrains.skiko.internal.fastForEach
internal actual class InteropScope actual constructor() { internal actual class InteropScope actual constructor() {
private val elements = mutableListOf<NativePointer>() private val elements = mutableListOf<NativePointer>()
private var callbacksInitialized = false private var callbacksInitialized = false
...@@ -149,15 +151,12 @@ internal actual class InteropScope actual constructor() { ...@@ -149,15 +151,12 @@ internal actual class InteropScope actual constructor() {
} }
actual fun toInterop(stringArray: Array<String>?): InteropPointer { actual fun toInterop(stringArray: Array<String>?): InteropPointer {
return if (stringArray != null && stringArray.isNotEmpty()) { if (stringArray.isNullOrEmpty()) return 0
val ptrs = stringArray.map {
toInterop(it)
}.toIntArray()
toInterop(ptrs) val ptrs = IntArray(stringArray.size) { i ->
} else { toInterop(stringArray[i])
0
} }
return toInterop(ptrs)
} }
actual fun InteropPointer.fromInteropNativePointerArray(): NativePointerArray { actual fun InteropPointer.fromInteropNativePointerArray(): NativePointerArray {
...@@ -237,7 +236,7 @@ internal actual class InteropScope actual constructor() { ...@@ -237,7 +236,7 @@ internal actual class InteropScope actual constructor() {
} }
actual fun release() { actual fun release() {
elements.forEach { elements.fastForEach {
_free(it) _free(it)
} }
elements.clear() elements.clear()
......
...@@ -269,7 +269,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetBounds ...@@ -269,7 +269,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetBounds
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
SkRect b = bounds[i]; SkRect b = bounds[i];
float r[4] = {b.left(), b.right(), b.top(), b.bottom()}; float r[4] = {b.left(), b.top(), b.right(), b.bottom()};
env->SetFloatArrayRegion(res, 4*i, 4, r); env->SetFloatArrayRegion(res, 4*i, 4, r);
} }
} }
......
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlin.system.measureNanoTime import org.jetbrains.skiko.internal.fastNone
import kotlin.time.Duration
import kotlin.time.Duration.Companion.nanoseconds
internal data class NotSupportedAdapter( internal data class NotSupportedAdapter(
val os: OS, val os: OS,
val api: GraphicsApi, val api: GraphicsApi,
val pattern: Regex val pattern: String,
/** If true, the adapter name should match the pattern exactly. If false, the patters should be the start of the adapter name. */
val exactPattern: Boolean = true
) )
private val notSupportedAdapters: List<NotSupportedAdapter> by lazy { private val notSupportedAdapters: List<NotSupportedAdapter> by lazy {
listOf( listOf(
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("Intel(R) HD Graphics 520")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "Intel(R) HD Graphics 520"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("Intel(R) HD Graphics 530")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "Intel(R) HD Graphics 530"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("Intel(R) HD Graphics 4400")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "Intel(R) HD Graphics 4400"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("Intel(R) HD Graphics 4600")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "Intel(R) HD Graphics 4600"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("NVIDIA GeForce GTX 750 Ti")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "NVIDIA GeForce GTX 750 Ti"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("NVIDIA GeForce GTX 960M")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "NVIDIA GeForce GTX 960M"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = Regex("NVIDIA Quadro M2000M")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.DIRECT3D, pattern = "NVIDIA Quadro M2000M"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.OPENGL, pattern = Regex("Intel(R) HD Graphics 2000")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.OPENGL, pattern = "Intel(R) HD Graphics 2000"),
NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.OPENGL, pattern = Regex("Intel(R) HD Graphics 3000")), NotSupportedAdapter(os = OS.Windows, api = GraphicsApi.OPENGL, pattern = "Intel(R) HD Graphics 3000"),
NotSupportedAdapter(os = OS.Linux, api = GraphicsApi.OPENGL, pattern = Regex("llvmpipe.*")), NotSupportedAdapter(os = OS.Linux, api = GraphicsApi.OPENGL, pattern = "llvmpipe", exactPattern = false),
NotSupportedAdapter(os = OS.Linux, api = GraphicsApi.OPENGL, pattern = Regex("virgl.*")) NotSupportedAdapter(os = OS.Linux, api = GraphicsApi.OPENGL, pattern = "virgl", exactPattern = false)
) )
} }
internal fun isVideoCardSupported(api: GraphicsApi, hostOs: OS, name: String): Boolean { internal fun isVideoCardSupported(api: GraphicsApi, hostOs: OS, name: String): Boolean {
return notSupportedAdapters.any { entry -> return notSupportedAdapters.fastNone { adapter ->
entry.os == hostOs && entry.api == api && entry.pattern.matches(name) if ((adapter.os != hostOs) || (adapter.api != api)) return@fastNone false
}.not()
val matchesPattern = if (adapter.exactPattern) (adapter.pattern == name) else name.startsWith(adapter.pattern)
matchesPattern
}
} }
\ No newline at end of file
...@@ -259,8 +259,8 @@ SKIKO_EXPORT void org_jetbrains_skia_Font__1nGetBounds ...@@ -259,8 +259,8 @@ SKIKO_EXPORT void org_jetbrains_skia_Font__1nGetBounds
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
SkRect b = bounds[i]; SkRect b = bounds[i];
res[4*i] = b.left(); res[4*i] = b.left();
res[4*i + 1] = b.right(); res[4*i + 1] = b.top();
res[4*i + 2] = b.top(); res[4*i + 2] = b.right();
res[4*i + 3] = b.bottom(); res[4*i + 3] = b.bottom();
} }
} }
......
...@@ -2,6 +2,7 @@ package org.jetbrains.skia.impl ...@@ -2,6 +2,7 @@ package org.jetbrains.skia.impl
import kotlinx.cinterop.* import kotlinx.cinterop.*
import org.jetbrains.skia.ExternalSymbolName import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skiko.internal.fastForEach
import kotlin.native.internal.NativePtr import kotlin.native.internal.NativePtr
actual abstract class Native actual constructor(ptr: NativePointer) { actual abstract class Native actual constructor(ptr: NativePointer) {
...@@ -180,13 +181,11 @@ internal actual class InteropScope actual constructor() { ...@@ -180,13 +181,11 @@ internal actual class InteropScope actual constructor() {
actual fun InteropPointer.fromInterop(result: NativePointerArray) {} actual fun InteropPointer.fromInterop(result: NativePointerArray) {}
actual fun toInterop(stringArray: Array<String>?): InteropPointer { actual fun toInterop(stringArray: Array<String>?): InteropPointer {
if (stringArray == null || stringArray.isEmpty()) return NativePtr.NULL if (stringArray.isNullOrEmpty()) return NativePtr.NULL
val pins = stringArray.toList()
.map { convertToZeroTerminatedString(it).pin() }
val nativePointerArray = NativePointerArray(stringArray.size) val nativePointerArray = NativePointerArray(stringArray.size)
pins.forEachIndexed { index, pin -> stringArray.forEachIndexed { index, str ->
val pin = convertToZeroTerminatedString(str).pin()
elements.add(pin) elements.add(pin)
nativePointerArray[index] = pin.addressOf(0).rawValue nativePointerArray[index] = pin.addressOf(0).rawValue
} }
...@@ -207,7 +206,8 @@ internal actual class InteropScope actual constructor() { ...@@ -207,7 +206,8 @@ internal actual class InteropScope actual constructor() {
} }
actual fun toInteropForArraysOfPointers(interopPointers: Array<InteropPointer>): InteropPointer { actual fun toInteropForArraysOfPointers(interopPointers: Array<InteropPointer>): InteropPointer {
return toInterop(interopPointers.map { it.toLong() }.toLongArray()) if (interopPointers.isEmpty()) return NativePtr.NULL
return toInterop(LongArray(interopPointers.size) { interopPointers[it].toLong() })
} }
actual fun callback(callback: (() -> Unit)?) = callbackImpl(callback) actual fun callback(callback: (() -> Unit)?) = callbackImpl(callback)
...@@ -223,7 +223,7 @@ internal actual class InteropScope actual constructor() { ...@@ -223,7 +223,7 @@ internal actual class InteropScope actual constructor() {
actual fun virtualBoolean(method: () -> Boolean) = callbackImpl(method) actual fun virtualBoolean(method: () -> Boolean) = callbackImpl(method)
actual fun release() { actual fun release() {
elements.forEach { elements.fastForEach {
it.unpin() it.unpin()
} }
} }
......
...@@ -5,6 +5,7 @@ package org.jetbrains.skiko.redrawer ...@@ -5,6 +5,7 @@ package org.jetbrains.skiko.redrawer
import kotlinx.cinterop.* import kotlinx.cinterop.*
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skiko.Logger import org.jetbrains.skiko.Logger
import org.jetbrains.skiko.internal.fastForEach
import platform.Foundation.NSNotificationCenter import platform.Foundation.NSNotificationCenter
import platform.Foundation.NSRunLoop import platform.Foundation.NSRunLoop
import platform.Foundation.NSSelectorFromString import platform.Foundation.NSSelectorFromString
...@@ -154,9 +155,9 @@ internal class MetalRedrawer( ...@@ -154,9 +155,9 @@ internal class MetalRedrawer(
if (!isApplicationActive) { if (!isApplicationActive) {
// If application goes background, synchronously schedule all inflightCommandBuffers, as per // If application goes background, synchronously schedule all inflightCommandBuffers, as per
// https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/preparing_your_metal_app_to_run_in_the_background?language=objc // https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/preparing_your_metal_app_to_run_in_the_background?language=objc
inflightCommandBuffers.forEach { inflightCommandBuffers.fastForEach { commandBuffer ->
// Will immediately return for MTLCommandBuffer's which are not in `Commited` status // Will immediately return for MTLCommandBuffer's which are not in `Commited` status
it.waitUntilScheduled() commandBuffer.waitUntilCompleted()
} }
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
package org.jetbrains.skia.impl package org.jetbrains.skia.impl
import org.jetbrains.skiko.internal.fastForEach
internal actual class InteropScope actual constructor() { internal actual class InteropScope actual constructor() {
private val elements = mutableListOf<NativePointer>() private val elements = mutableListOf<NativePointer>()
private var callbacksInitialized = false private var callbacksInitialized = false
...@@ -149,15 +151,12 @@ internal actual class InteropScope actual constructor() { ...@@ -149,15 +151,12 @@ internal actual class InteropScope actual constructor() {
} }
actual fun toInterop(stringArray: Array<String>?): InteropPointer { actual fun toInterop(stringArray: Array<String>?): InteropPointer {
return if (stringArray != null && stringArray.isNotEmpty()) { if (stringArray.isNullOrEmpty()) return 0
val ptrs = stringArray.map {
toInterop(it)
}.toIntArray()
toInterop(ptrs) val ptrs = IntArray(stringArray.size) { i ->
} else { toInterop(stringArray[i])
0
} }
return toInterop(ptrs)
} }
actual fun InteropPointer.fromInteropNativePointerArray(): NativePointerArray { actual fun InteropPointer.fromInteropNativePointerArray(): NativePointerArray {
...@@ -237,7 +236,7 @@ internal actual class InteropScope actual constructor() { ...@@ -237,7 +236,7 @@ internal actual class InteropScope actual constructor() {
} }
actual fun release() { actual fun release() {
elements.forEach { elements.fastForEach {
_free(it) _free(it)
} }
elements.clear() elements.clear()
......
...@@ -28,7 +28,7 @@ actual class Matcher constructor(private val regex: Regex, private val input: Ch ...@@ -28,7 +28,7 @@ actual class Matcher constructor(private val regex: Regex, private val input: Ch
@Suppress("RedundantNullableReturnType") @Suppress("RedundantNullableReturnType")
val LANG: String by lazy { val LANG: String by lazy {
val lang: String? = window.navigator.language val lang: String? = window.navigator.language
if (lang == null || lang.isEmpty()) { "en-US" } else { lang } if (lang.isNullOrEmpty()) "en-US" else lang
} }
actual fun defaultLanguageTag(): String = LANG actual fun defaultLanguageTag(): String = LANG
......
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