Unverified Commit 3158b8d7 authored by Roman Sedaikin's avatar Roman Sedaikin Committed by GitHub

Merge pull request #70 from JetBrains/metal

Metal API prototype.
parents 1f13701d 427dbacf
build build
.gradle .gradle
.project
local.properties local.properties
.DS_Store .DS_Store
run.sh run.sh
......
...@@ -288,15 +288,21 @@ tasks.withType(CppCompile::class.java).configureEach { ...@@ -288,15 +288,21 @@ tasks.withType(CppCompile::class.java).configureEach {
project.tasks.register<Exec>("objcCompile") { project.tasks.register<Exec>("objcCompile") {
val inputDir = "$projectDir/src/jvmMain/objectiveC/${targetOs.id}" val inputDir = "$projectDir/src/jvmMain/objectiveC/${targetOs.id}"
val outDir = "$buildDir/objc/$target" val outDir = "$buildDir/objc/$target"
val names = File(inputDir).listFiles()!!.map { it.name.removeSuffix(".m") } val names = File(inputDir).listFiles()!!.map { it.name.removeSuffix(".mm") }
val srcs = names.map { "$inputDir/$it.m" }.toTypedArray() val srcs = names.map { "$inputDir/$it.mm" }.toTypedArray()
val outs = names.map { "$outDir/$it.o" }.toTypedArray() val outs = names.map { "$outDir/$it.o" }.toTypedArray()
workingDir = File(outDir) workingDir = File(outDir)
val skiaDir = skiaDir.get().absolutePath
commandLine = listOf( commandLine = listOf(
"clang", "clang",
"-mmacosx-version-min=10.13", "-mmacosx-version-min=10.13",
"-I$jdkHome/include", "-I$jdkHome/include",
"-I$jdkHome/include/darwin", "-I$jdkHome/include/darwin",
"-I$skiaDir",
"-I$skiaDir/include",
"-I$skiaDir/include/gpu",
"-DSK_METAL",
"-std=c++17",
"-c", "-c",
*srcs *srcs
) )
......
...@@ -13,62 +13,32 @@ abstract class HardwareLayer : Canvas() { ...@@ -13,62 +13,32 @@ abstract class HardwareLayer : Canvas() {
// getDpiScale is expensive operation on some platforms, so we cache it // getDpiScale is expensive operation on some platforms, so we cache it
private var _contentScale: Float? = null private var _contentScale: Float? = null
private var isInit = false
init { internal fun defineContentScale() {
@Suppress("LeakingThis")
addHierarchyListener {
if (it.changeFlags and HierarchyEvent.SHOWING_CHANGED.toLong() != 0L) {
checkIsShowing()
}
}
}
private fun checkIsShowing() {
if (!isInit && isShowing) {
_contentScale = getDpiScale() _contentScale = getDpiScale()
init()
isInit = true
}
} }
override fun paint(g: Graphics) {}
protected open fun init() { internal open fun init() {
useDrawingSurfacePlatformInfo(::nativeInit) useDrawingSurfacePlatformInfo(::nativeInit)
onInit()
} }
fun dispose() { internal fun dispose() {
if (isInit) {
onDispose()
nativeDispose() nativeDispose()
} }
}
protected open fun onInit() = Unit
protected open fun onDispose() = Unit
private external fun nativeInit(platformInfo: Long) private external fun nativeInit(platformInfo: Long)
private external fun nativeDispose() private external fun nativeDispose()
protected open fun contentScaleChanged() = Unit
override fun setBounds(x: Int, y: Int, width: Int, height: Int) {
super.setBounds(x, y, width, height)
checkContentScale()
}
override fun paint(g: Graphics) {
checkContentScale()
}
// TODO checkContentScale is called before init. it is ok, but when we fix getDpiScale on Linux we should check [isInit] // TODO checkContentScale is called before init. it is ok, but when we fix getDpiScale on Linux we should check [isInit]
private fun checkContentScale() { internal fun checkContentScale(): Boolean {
val contentScale = getDpiScale() val contentScale = getDpiScale()
if (contentScale != _contentScale) { if (contentScale != _contentScale) {
_contentScale = contentScale _contentScale = contentScale
contentScaleChanged() return true
} }
return false
} }
private fun getDpiScale(): Float { private fun getDpiScale(): Float {
...@@ -77,12 +47,6 @@ abstract class HardwareLayer : Canvas() { ...@@ -77,12 +47,6 @@ abstract class HardwareLayer : Canvas() {
return scale return scale
} }
// Should be called in Swing thread
internal abstract fun update(nanoTime: Long)
// Should be called in the OpenGL thread, and only once after update
internal abstract fun draw()
val windowHandle: Long val windowHandle: Long
get() = useDrawingSurfacePlatformInfo(::getWindowHandle) get() = useDrawingSurfacePlatformInfo(::getWindowHandle)
......
...@@ -6,6 +6,7 @@ import org.jetbrains.skiko.redrawer.SoftwareRedrawer ...@@ -6,6 +6,7 @@ import org.jetbrains.skiko.redrawer.SoftwareRedrawer
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.WindowsOpenGLRedrawer import org.jetbrains.skiko.redrawer.WindowsOpenGLRedrawer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import org.jetbrains.skiko.redrawer.MetalRedrawer
import java.awt.Component import java.awt.Component
import java.awt.Window import java.awt.Window
import javax.swing.SwingUtilities import javax.swing.SwingUtilities
...@@ -14,7 +15,7 @@ internal interface PlatformOperations { ...@@ -14,7 +15,7 @@ internal interface PlatformOperations {
fun isFullscreen(component: Component): Boolean fun isFullscreen(component: Component): Boolean
fun setFullscreen(component: Component, value: Boolean) fun setFullscreen(component: Component, value: Boolean)
fun getDpiScale(component: Component): Float fun getDpiScale(component: Component): Float
fun createRedrawer(layer: HardwareLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer fun createRedrawer(layer: SkiaLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer
} }
internal val platformOperations: PlatformOperations by lazy { internal val platformOperations: PlatformOperations by lazy {
...@@ -33,11 +34,12 @@ internal val platformOperations: PlatformOperations by lazy { ...@@ -33,11 +34,12 @@ internal val platformOperations: PlatformOperations by lazy {
} }
override fun createRedrawer( override fun createRedrawer(
layer: HardwareLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
properties: SkiaLayerProperties properties: SkiaLayerProperties
) = when(renderApi) { ) = when(renderApi) {
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer) GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
GraphicsApi.METAL -> MetalRedrawer(layer, properties)
else -> MacOsOpenGLRedrawer(layer, properties) else -> MacOsOpenGLRedrawer(layer, properties)
} }
} }
...@@ -60,7 +62,7 @@ internal val platformOperations: PlatformOperations by lazy { ...@@ -60,7 +62,7 @@ internal val platformOperations: PlatformOperations by lazy {
} }
override fun createRedrawer( override fun createRedrawer(
layer: HardwareLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
properties: SkiaLayerProperties properties: SkiaLayerProperties
) = when(renderApi) { ) = when(renderApi) {
...@@ -101,7 +103,7 @@ internal val platformOperations: PlatformOperations by lazy { ...@@ -101,7 +103,7 @@ internal val platformOperations: PlatformOperations by lazy {
} }
override fun createRedrawer( override fun createRedrawer(
layer: HardwareLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
properties: SkiaLayerProperties properties: SkiaLayerProperties
) = when(renderApi) { ) = when(renderApi) {
......
...@@ -8,7 +8,14 @@ import org.jetbrains.skija.Rect ...@@ -8,7 +8,14 @@ import org.jetbrains.skija.Rect
import org.jetbrains.skiko.context.ContextHandler import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.createContextHandler import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import java.awt.event.InputMethodListener
import java.awt.event.KeyListener
import java.awt.event.MouseListener
import java.awt.event.MouseMotionListener
import java.awt.event.MouseWheelListener
import java.awt.Graphics import java.awt.Graphics
import java.awt.event.HierarchyEvent
import javax.swing.JPanel
import javax.swing.SwingUtilities.invokeLater import javax.swing.SwingUtilities.invokeLater
import javax.swing.SwingUtilities.isEventDispatchThread import javax.swing.SwingUtilities.isEventDispatchThread
...@@ -20,11 +27,48 @@ private class PictureHolder(val instance: Picture, val width: Int, val height: I ...@@ -20,11 +27,48 @@ private class PictureHolder(val instance: Picture, val width: Int, val height: I
open class SkiaLayer( open class SkiaLayer(
private val properties: SkiaLayerProperties = SkiaLayerProperties() private val properties: SkiaLayerProperties = SkiaLayerProperties()
) : HardwareLayer() { ) : JPanel() {
val backedLayer : HardwareLayer
init {
setOpaque(false)
layout = null
backedLayer = object : HardwareLayer() { }
add(backedLayer)
@Suppress("LeakingThis")
backedLayer.addHierarchyListener {
if (it.changeFlags and HierarchyEvent.SHOWING_CHANGED.toLong() != 0L) {
checkIsShowing()
}
}
}
private var isInited = false
private fun checkIsShowing() {
if (!isInited && isShowing) {
backedLayer.defineContentScale()
init()
isInited = true
}
}
val contentScale: Float
get() = backedLayer.contentScale
val windowHandle: Long
get() = backedLayer.windowHandle
var fullscreen: Boolean
get() = backedLayer.fullscreen
set(value) { backedLayer.fullscreen = value }
protected open fun contentScaleChanged() = Unit
var renderer: SkiaRenderer? = null var renderer: SkiaRenderer? = null
val clipComponents = mutableListOf<ClipRectangle>() val clipComponents = mutableListOf<ClipRectangle>()
@Volatile @Volatile
private var isDisposed = false private var isDisposed = false
internal var redrawer: Redrawer? = null internal var redrawer: Redrawer? = null
...@@ -36,8 +80,8 @@ open class SkiaLayer( ...@@ -36,8 +80,8 @@ open class SkiaLayer(
private val pictureRecorder = PictureRecorder() private val pictureRecorder = PictureRecorder()
private val pictureLock = Any() private val pictureLock = Any()
override fun onInit() { open fun init() {
super.onInit() backedLayer.init()
val initialRenderApi = fallbackRenderApiQueue.removeAt(0) val initialRenderApi = fallbackRenderApiQueue.removeAt(0)
contextHandler = createContextHandler(this, initialRenderApi) contextHandler = createContextHandler(this, initialRenderApi)
redrawer = platformOperations.createRedrawer(this, initialRenderApi, properties) redrawer = platformOperations.createRedrawer(this, initialRenderApi, properties)
...@@ -45,7 +89,7 @@ open class SkiaLayer( ...@@ -45,7 +89,7 @@ open class SkiaLayer(
redraw() redraw()
} }
override fun onDispose() { open fun dispose() {
check(!isDisposed) check(!isDisposed)
check(isEventDispatchThread()) check(isEventDispatchThread())
redrawer?.dispose() // we should dispose redrawer first (to cancel `draw` in rendering thread) redrawer?.dispose() // we should dispose redrawer first (to cancel `draw` in rendering thread)
...@@ -53,21 +97,51 @@ open class SkiaLayer( ...@@ -53,21 +97,51 @@ open class SkiaLayer(
picture?.instance?.close() picture?.instance?.close()
pictureRecorder.close() pictureRecorder.close()
isDisposed = true isDisposed = true
super.onDispose() if (isInited) {
backedLayer.dispose()
}
} }
override fun setBounds(x: Int, y: Int, width: Int, height: Int) { override fun setBounds(x: Int, y: Int, width: Int, height: Int) {
super.setBounds(x, y, width, height) super.setBounds(x, y, width, height)
backedLayer.setSize(width, height)
if (backedLayer.checkContentScale()) {
contentScaleChanged()
}
redrawer?.syncSize() redrawer?.syncSize()
redraw() redraw()
revalidate()
} }
override fun paint(g: Graphics) { override fun paint(g: Graphics) {
super.paint(g) super.paint(g)
if (backedLayer.checkContentScale()) {
contentScaleChanged()
}
redrawer?.syncSize() redrawer?.syncSize()
redrawer?.redrawImmediately() redrawer?.redrawImmediately()
} }
override fun addInputMethodListener(l: InputMethodListener) {
backedLayer.addInputMethodListener(l)
}
override fun addMouseListener(l: MouseListener) {
backedLayer.addMouseListener(l)
}
override fun addMouseMotionListener(l: MouseMotionListener) {
backedLayer.addMouseMotionListener(l)
}
override fun addMouseWheelListener(l: MouseWheelListener) {
backedLayer.addMouseWheelListener(l)
}
override fun addKeyListener(l: KeyListener) {
backedLayer.addKeyListener(l)
}
private var redrawScheduled = false private var redrawScheduled = false
/** /**
...@@ -97,7 +171,7 @@ open class SkiaLayer( ...@@ -97,7 +171,7 @@ open class SkiaLayer(
@Suppress("LeakingThis") @Suppress("LeakingThis")
private val fpsCounter = defaultFPSCounter(this) private val fpsCounter = defaultFPSCounter(this)
override fun update(nanoTime: Long) { open fun update(nanoTime: Long) {
check(!isDisposed) check(!isDisposed)
check(isEventDispatchThread()) check(isEventDispatchThread())
...@@ -126,7 +200,7 @@ open class SkiaLayer( ...@@ -126,7 +200,7 @@ open class SkiaLayer(
} }
} }
override fun draw() { open fun draw() {
check(!isDisposed) check(!isDisposed)
contextHandler?.apply { contextHandler?.apply {
if (!initContext()) { if (!initContext()) {
......
...@@ -43,7 +43,7 @@ internal object SkikoProperties { ...@@ -43,7 +43,7 @@ internal object SkikoProperties {
private fun bestRenderApiForCurrentOS(): GraphicsApi { private fun bestRenderApiForCurrentOS(): GraphicsApi {
when(hostOs) { when(hostOs) {
OS.MacOS -> return GraphicsApi.OPENGL OS.MacOS -> return GraphicsApi.METAL
OS.Linux -> return GraphicsApi.OPENGL OS.Linux -> return GraphicsApi.OPENGL
OS.Windows -> return GraphicsApi.DIRECT3D OS.Windows -> return GraphicsApi.DIRECT3D
} }
...@@ -54,8 +54,8 @@ internal object SkikoProperties { ...@@ -54,8 +54,8 @@ internal object SkikoProperties {
var renderApiList = mutableListOf<GraphicsApi>() var renderApiList = mutableListOf<GraphicsApi>()
when (hostOs) { when (hostOs) {
OS.MacOS -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.Linux -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE) OS.Linux -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.MacOS -> renderApiList = mutableListOf(GraphicsApi.METAL, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.Windows -> renderApiList = mutableListOf(GraphicsApi.DIRECT3D, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE) OS.Windows -> renderApiList = mutableListOf(GraphicsApi.DIRECT3D, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
} }
renderApiList.remove(head) renderApiList.remove(head)
......
...@@ -17,6 +17,7 @@ internal fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): Con ...@@ -17,6 +17,7 @@ internal fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): Con
GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer) GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
GraphicsApi.OPENGL -> OpenGLContextHandler(layer) GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
GraphicsApi.DIRECT3D -> Direct3DContextHandler(layer) GraphicsApi.DIRECT3D -> Direct3DContextHandler(layer)
GraphicsApi.METAL -> MetalContextHandler(layer)
else -> TODO("Unsupported yet.") else -> TODO("Unsupported yet.")
} }
} }
......
...@@ -4,20 +4,20 @@ import org.jetbrains.skija.ColorSpace ...@@ -4,20 +4,20 @@ import org.jetbrains.skija.ColorSpace
import org.jetbrains.skija.Surface import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.makeMetalContext import org.jetbrains.skiko.redrawer.MetalRedrawer
import org.jetbrains.skiko.makeMetalRenderTarget
import org.jetbrains.skiko.redrawer.Redrawer
internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
val metalRedrawer: MetalRedrawer
get() = layer.redrawer!! as MetalRedrawer
override fun initContext(): Boolean { override fun initContext(): Boolean {
try { try {
if (context == null) { if (context == null) {
context = makeMetalContext() context = metalRedrawer.makeContext()
} }
} catch (e: Exception) { } catch (e: Exception) {
println("Failed to create Skia Metal context!") println("${e.message}\nFailed to create Skia Metal context!")
return false return false
} }
return true return true
...@@ -30,16 +30,22 @@ internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) { ...@@ -30,16 +30,22 @@ internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
val w = (layer.width * scale).toInt().coerceAtLeast(0) val w = (layer.width * scale).toInt().coerceAtLeast(0)
val h = (layer.height * scale).toInt().coerceAtLeast(0) val h = (layer.height * scale).toInt().coerceAtLeast(0)
renderTarget = makeMetalRenderTarget(w, h, 0) renderTarget = metalRedrawer.makeRenderTarget(w, h)
surface = Surface.makeFromBackendRenderTarget( surface = Surface.makeFromBackendRenderTarget(
context!!, context!!,
renderTarget!!, renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT, SurfaceOrigin.TOP_LEFT,
SurfaceColorFormat.RGBA_8888, SurfaceColorFormat.BGRA_8888,
ColorSpace.getSRGB() ColorSpace.getSRGB()
) )
canvas = surface!!.canvas canvas = surface!!.canvas
} }
override fun flush() {
super.flush()
surface!!.flushAndSubmit()
metalRedrawer.finishFrame()
}
} }
...@@ -73,7 +73,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer) ...@@ -73,7 +73,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer)
null null
) )
image = BufferedImage(colorModel, raster!!, false, null) image = BufferedImage(colorModel, raster!!, false, null)
layer.getGraphics()?.drawImage(image!!, 0, 0, layer.width, layer.height, null) layer.backedLayer.getGraphics()?.drawImage(image!!, 0, 0, layer.width, layer.height, null)
} }
} }
......
...@@ -5,11 +5,11 @@ import kotlinx.coroutines.swing.Swing ...@@ -5,11 +5,11 @@ import kotlinx.coroutines.swing.Swing
import org.jetbrains.skija.BackendRenderTarget import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.DirectContext import org.jetbrains.skija.DirectContext
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
internal class Direct3DRedrawer( internal class Direct3DRedrawer(
private val layer: HardwareLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
......
...@@ -5,23 +5,24 @@ import kotlinx.coroutines.Dispatchers ...@@ -5,23 +5,24 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.DrawingSurface import org.jetbrains.skiko.DrawingSurface
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.OpenGLApi import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.getDrawingSurface import org.jetbrains.skiko.getDrawingSurface
internal class LinuxOpenGLRedrawer( internal class LinuxOpenGLRedrawer(
private val layer: HardwareLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val context = layer.lockDrawingSurface { private val context = layer.backedLayer.lockDrawingSurface {
it.createContext() it.createContext()
} }
private var isDisposed = false private var isDisposed = false
override fun dispose() { override fun dispose() {
check(!isDisposed) check(!isDisposed)
layer.lockDrawingSurface { layer.backedLayer.lockDrawingSurface {
it.destroyContext(context) it.destroyContext(context)
} }
isDisposed = true isDisposed = true
...@@ -33,7 +34,7 @@ internal class LinuxOpenGLRedrawer( ...@@ -33,7 +34,7 @@ internal class LinuxOpenGLRedrawer(
frameDispatcher.scheduleFrame() frameDispatcher.scheduleFrame()
} }
override fun redrawImmediately() = layer.lockDrawingSurface { override fun redrawImmediately() = layer.backedLayer.lockDrawingSurface {
check(!isDisposed) check(!isDisposed)
update(System.nanoTime()) update(System.nanoTime())
it.makeCurrent(context) it.makeCurrent(context)
...@@ -73,7 +74,7 @@ internal class LinuxOpenGLRedrawer( ...@@ -73,7 +74,7 @@ internal class LinuxOpenGLRedrawer(
val isVsyncEnabled = toRedrawAlive.all { it.properties.isVsyncEnabled } val isVsyncEnabled = toRedrawAlive.all { it.properties.isVsyncEnabled }
val drawingSurfaces = toRedrawAlive.map { lockDrawingSurface(it.layer) }.toList() val drawingSurfaces = toRedrawAlive.map { lockDrawingSurface(it.layer.backedLayer) }.toList()
try { try {
toRedrawAlive.forEachIndexed { index, redrawer -> toRedrawAlive.forEachIndexed { index, redrawer ->
drawingSurfaces[index].makeCurrent(redrawer.context) drawingSurfaces[index].makeCurrent(redrawer.context)
......
...@@ -4,7 +4,7 @@ import kotlinx.coroutines.Dispatchers ...@@ -4,7 +4,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.OpenGLApi import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.Task import org.jetbrains.skiko.Task
...@@ -21,10 +21,10 @@ import kotlin.system.measureNanoTime ...@@ -21,10 +21,10 @@ import kotlin.system.measureNanoTime
// P.S. MacOsOpenGLRedrawer will not be used by default in the future, because we will support Metal. // P.S. MacOsOpenGLRedrawer will not be used by default in the future, because we will support Metal.
internal class MacOsOpenGLRedrawer( internal class MacOsOpenGLRedrawer(
private val layer: HardwareLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val containerLayerPtr = layer.useDrawingSurfacePlatformInfo(::initContainer) private val containerLayerPtr = layer.backedLayer.useDrawingSurfacePlatformInfo(::initContainer)
private val drawLock = Any() private val drawLock = Any()
private var isDisposed = false private var isDisposed = false
......
package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.DirectContext
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.useDrawingSurfacePlatformInfo
import javax.swing.SwingUtilities.convertPoint
import javax.swing.SwingUtilities.getRootPane
internal class MetalRedrawer(
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private var isDisposed = false
private val device = layer.backedLayer.useDrawingSurfacePlatformInfo(::createMetalDevice)
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
update(System.nanoTime())
draw()
}
override fun dispose() {
frameDispatcher.cancel()
disposeDevice(device)
isDisposed = true
}
override fun needRedraw() {
check(!isDisposed)
frameDispatcher.scheduleFrame()
}
override fun redrawImmediately() {
check(!isDisposed)
update(System.nanoTime())
draw()
}
private fun update(nanoTime: Long) {
layer.update(nanoTime)
}
private fun draw() {
layer.draw()
}
override fun syncSize() {
val globalPosition = convertPoint(layer, layer.x, layer.y, getRootPane(layer))
setContentScale(device, layer.contentScale)
resizeLayers(device,
globalPosition.x,
globalPosition.y,
layer.width.coerceAtLeast(0),
layer.height.coerceAtLeast(0)
)
}
fun makeContext() = DirectContext(
makeMetalContext(device)
)
fun makeRenderTarget(width: Int, height: Int) = BackendRenderTarget(
makeMetalRenderTarget(device, width, height)
)
fun finishFrame() = finishFrame(device)
private external fun createMetalDevice(platformInfo: Long): Long
private external fun makeMetalContext(device: Long): Long
private external fun makeMetalRenderTarget(device: Long, width: Int, height: Int): Long
private external fun disposeDevice(device: Long)
private external fun finishFrame(device: Long)
private external fun resizeLayers(device: Long, x: Int, y: Int, width: Int, height: Int)
private external fun setContentScale(device: Long, contentScale: Float)
}
...@@ -3,10 +3,10 @@ package org.jetbrains.skiko.redrawer ...@@ -3,10 +3,10 @@ package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
internal class SoftwareRedrawer( internal class SoftwareRedrawer(
private val layer: HardwareLayer private val layer: SkiaLayer
) : Redrawer { ) : Redrawer {
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
......
...@@ -5,16 +5,16 @@ import kotlinx.coroutines.Dispatchers ...@@ -5,16 +5,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.OpenGLApi import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.useDrawingSurfacePlatformInfo import org.jetbrains.skiko.useDrawingSurfacePlatformInfo
internal class WindowsOpenGLRedrawer( internal class WindowsOpenGLRedrawer(
private val layer: HardwareLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val device = layer.useDrawingSurfacePlatformInfo(::getDevice) private val device = layer.backedLayer.useDrawingSurfacePlatformInfo(::getDevice)
private val context = createContext(device) private val context = createContext(device)
private var isDisposed = false private var isDisposed = false
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
- (void) disposeLayer:(JNIEnv *) env - (void) disposeLayer:(JNIEnv *) env
{ {
(*env)->DeleteGlobalRef(env, self.canvasGlobalRef); env->DeleteGlobalRef(self.canvasGlobalRef);
self.canvasGlobalRef = NULL; self.canvasGlobalRef = NULL;
self.container = NULL; self.container = NULL;
self.window = NULL; self.window = NULL;
...@@ -71,7 +71,7 @@ LayerHandler * findByObject(JNIEnv *env, jobject object) ...@@ -71,7 +71,7 @@ LayerHandler * findByObject(JNIEnv *env, jobject object)
} }
for (LayerHandler* layer in layerStorage) for (LayerHandler* layer in layerStorage)
{ {
if ((*env)->IsSameObject(env, object, layer.canvasGlobalRef) == JNI_TRUE) if (env->IsSameObject(object, layer.canvasGlobalRef) == JNI_TRUE)
{ {
return layer; return layer;
} }
...@@ -79,6 +79,9 @@ LayerHandler * findByObject(JNIEnv *env, jobject object) ...@@ -79,6 +79,9 @@ LayerHandler * findByObject(JNIEnv *env, jobject object)
return NULL; return NULL;
} }
extern "C"
{
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv *env, jobject canvas, jlong platformInfoPtr) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv *env, jobject canvas, jlong platformInfoPtr)
{ {
if (layerStorage == nil) if (layerStorage == nil)
...@@ -91,7 +94,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv ...@@ -91,7 +94,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv
LayerHandler *layersSet = [[LayerHandler alloc] init]; LayerHandler *layersSet = [[LayerHandler alloc] init];
layersSet.container = [dsi_mac windowLayer]; layersSet.container = [dsi_mac windowLayer];
jobject canvasGlobalRef = (*env)->NewGlobalRef(env, canvas); jobject canvasGlobalRef = env->NewGlobalRef(canvas);
[layersSet setCanvasGlobalRef: canvasGlobalRef]; [layersSet setCanvasGlobalRef: canvasGlobalRef];
NSMutableArray<NSWindow *> *windows = [NSMutableArray arrayWithArray: [[NSApplication sharedApplication] windows]]; NSMutableArray<NSWindow *> *windows = [NSMutableArray arrayWithArray: [[NSApplication sharedApplication] windows]];
...@@ -153,3 +156,5 @@ void getMetalDeviceAndQueue(void** device, void** queue) ...@@ -153,3 +156,5 @@ void getMetalDeviceAndQueue(void** device, void** queue)
*device = (__bridge void*)fDevice; *device = (__bridge void*)fDevice;
*queue = (__bridge void*)fQueue; *queue = (__bridge void*)fQueue;
} }
} // extern C
\ No newline at end of file
#ifdef SK_METAL
#import <jawt.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 <GrDirectContext.h>
#import <mtl/GrMtlBackendContext.h>
#import <mtl/GrMtlTypes.h>
@interface AWTMetalLayer : CAMetalLayer
@property jobject javaRef;
@end
@interface MetalDevice : NSObject
@property (retain, strong) CALayer *container;
@property (retain, strong) AWTMetalLayer *layer;
@property (retain, strong) id<MTLDevice> device;
@property (retain, strong) id<MTLCommandQueue> queue;
@property (retain, strong) id<CAMetalDrawable> drawableHandle;
@end
@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.device = nil;
self.queue = nil;
self.drawableHandle = nil;
}
return self;
}
@end
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalContext(
JNIEnv* env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
GrMtlBackendContext backendContext = {};
backendContext.fDevice.retain((GrMTLHandle) device.device);
backendContext.fQueue.retain((GrMTLHandle) device.queue);
return (jlong) GrDirectContext::MakeMetal(backendContext).release();
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalRenderTarget(
JNIEnv * env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
MetalDevice *device = (MetalDevice *) devicePtr;
device.layer.drawableSize = CGRectMake(0, 0, width, height).size;
id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable];
GrMtlTextureInfo info;
info.fTexture.retain(currentDrawable.texture);
GrBackendRenderTarget* renderTarget = new GrBackendRenderTarget(width, height, 0, info);
device.drawableHandle = currentDrawable;
return (jlong) renderTarget;
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMetalDevice(
JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{
MetalDevice *device = [MetalDevice new];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr;
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);
id<MTLDevice> fDevice = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> fQueue = [fDevice newCommandQueue];
device.container = container;
device.layer = layer;
device.device = fDevice;
device.queue = fQueue;
device.layer.device = device.device;
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;
return (jlong) device;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLayers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint x, jint y, jint width, jint height)
{
MetalDevice *device = (MetalDevice *) devicePtr;
y = (int)device.container.frame.size.height - y - height;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
device.layer.frame = CGRectMake(x, y, width, height);
[CATransaction commit];
[CATransaction flush];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setContentScale(JNIEnv *env, jobject obj, jlong devicePtr, jfloat contentScale)
{
MetalDevice *device = (MetalDevice *) devicePtr;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
assert(contentScale != 0);
device.layer.contentsScale = contentScale;
[CATransaction commit];
[CATransaction flush];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFrame(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
id<CAMetalDrawable> currentDrawable = device.drawableHandle;
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present";
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
[device.layer removeFromSuperlayer];
env->DeleteGlobalRef(device.layer.javaRef);
[device.layer release];
[device release];
}
} // extern C
#endif
\ No newline at end of file
...@@ -36,13 +36,13 @@ JavaVM *jvm = NULL; ...@@ -36,13 +36,13 @@ JavaVM *jvm = NULL;
{ {
assert(jvm != NULL); assert(jvm != NULL);
JNIEnv *env; JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL); jvm->AttachCurrentThread((void **)&env, NULL);
static jclass cls = NULL; static jclass cls = NULL;
static jmethodID method = NULL; static jmethodID method = NULL;
if (!cls) cls = (*env)->GetObjectClass(env, self.javaRef); if (!cls) cls = env->GetObjectClass(self.javaRef);
if (!method) method = (*env)->GetMethodID(env, cls, "canDraw", "()Z"); if (!method) method = env->GetMethodID(cls, "canDraw", "()Z");
return (*env)->CallBooleanMethod(env, self.javaRef, method); return env->CallBooleanMethod(self.javaRef, method);
} }
-(void)drawInCGLContext:(CGLContextObj)ctx -(void)drawInCGLContext:(CGLContextObj)ctx
...@@ -54,22 +54,25 @@ JavaVM *jvm = NULL; ...@@ -54,22 +54,25 @@ JavaVM *jvm = NULL;
assert(jvm != NULL); assert(jvm != NULL);
JNIEnv *env; JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL); jvm->AttachCurrentThread((void **)&env, NULL);
static jclass cls = NULL; static jclass cls = NULL;
static jmethodID method = NULL; static jmethodID method = NULL;
if (!cls) cls = (*env)->GetObjectClass(env, self.javaRef); if (!cls) cls = env->GetObjectClass(self.javaRef);
if (!method) method = (*env)->GetMethodID(env, cls, "performDraw", "()V"); if (!method) method = env->GetMethodID(cls, "performDraw", "()V");
(*env)->CallVoidMethod(env, self.javaRef, method); env->CallVoidMethod(self.javaRef, method);
[super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts]; [super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts];
} }
@end @end
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_initContainer(JNIEnv *env, jobject redrawer, jlong platformInfoPtr) JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_initContainer(JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{ {
(*env)->GetJavaVM(env, &jvm); env->GetJavaVM(&jvm);
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr; NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr;
...@@ -93,7 +96,7 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_ ...@@ -93,7 +96,7 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_
CALayer *container = (CALayer *) containerPtr; CALayer *container = (CALayer *) containerPtr;
AWTGLLayer *glLayer = [AWTGLLayer new]; AWTGLLayer *glLayer = [AWTGLLayer new];
glLayer.javaRef = (*env)->NewGlobalRef(env, layer); glLayer.javaRef = env->NewGlobalRef(layer);
[glLayer setNeedsDisplayOnBoundsChange: setNeedsDisplayOnBoundsChange]; [glLayer setNeedsDisplayOnBoundsChange: setNeedsDisplayOnBoundsChange];
[container addSublayer: glLayer]; [container addSublayer: glLayer];
...@@ -104,7 +107,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_d ...@@ -104,7 +107,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_d
{ {
AWTGLLayer *glLayer = (AWTGLLayer *) ptr; AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
[glLayer removeFromSuperlayer]; [glLayer removeFromSuperlayer];
(*env)->DeleteGlobalRef(env, glLayer.javaRef); env->DeleteGlobalRef(glLayer.javaRef);
[glLayer release]; [glLayer release];
} }
...@@ -139,3 +142,5 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_setFrame(JNI ...@@ -139,3 +142,5 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_setFrame(JNI
glLayer.frame = CGRectMake(x, y, width, height); glLayer.frame = CGRectMake(x, y, width, height);
[CATransaction commit]; [CATransaction commit];
} }
} // extern C
\ No newline at end of file
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