Unverified Commit 0f45ab4e authored by Igor Demin's avatar Igor Demin Committed by GitHub

Create ContextHandler inside Redrawer (#300)

parent e7c30e9e
...@@ -35,7 +35,7 @@ fun main() { ...@@ -35,7 +35,7 @@ fun main() {
} }
skiaLayer.renderer = GenericSkikoApp(skiaLayer, app) skiaLayer.renderer = GenericSkikoApp(skiaLayer, app)
skiaLayer.setCanvas(canvas) skiaLayer.setCanvas(canvas)
skiaLayer.draw() skiaLayer.needRedraw()
} }
} }
} }
package org.jetbrains.skiko
internal class RenderException(
message: String? = null,
cause: Throwable? = null
) : RuntimeException(message, cause)
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.context.*
import org.jetbrains.skiko.redrawer.* import org.jetbrains.skiko.redrawer.*
internal interface RenderFactory { internal interface RenderFactory {
fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): ContextHandler
fun createRedrawer(layer: SkiaLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer fun createRedrawer(layer: SkiaLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer
companion object { companion object {
......
...@@ -5,41 +5,33 @@ import org.jetbrains.skia.Canvas ...@@ -5,41 +5,33 @@ import org.jetbrains.skia.Canvas
import org.jetbrains.skia.DirectContext import org.jetbrains.skia.DirectContext
import org.jetbrains.skia.Picture import org.jetbrains.skia.Picture
import org.jetbrains.skia.Surface import org.jetbrains.skia.Surface
import org.jetbrains.skiko.GraphicsApi import org.jetbrains.skiko.*
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.RenderException
import org.jetbrains.skiko.hostArch
import org.jetbrains.skiko.hostOs internal abstract class ContextHandler(
import org.jetbrains.skiko.OS protected val layer: SkiaLayer,
private val drawContent: Canvas.() -> Unit
internal abstract class ContextHandler(val layer: SkiaLayer) { ) {
open val clearColor = if (layer.transparency || hostOs == OS.MacOS) 0 else -1 // TODO can we simplify clearColor logic? is there a reason why SoftwareContextHandler has opposite logic?
var context: DirectContext? = null protected open val clearColor = if (layer.transparency || hostOs == OS.MacOS) 0 else -1
var renderTarget: BackendRenderTarget? = null protected var context: DirectContext? = null
var surface: Surface? = null protected var renderTarget: BackendRenderTarget? = null
var canvas: Canvas? = null protected var surface: Surface? = null
protected var canvas: Canvas? = null
abstract fun initContext(): Boolean
abstract fun initCanvas() protected abstract fun initContext(): Boolean
protected abstract fun initCanvas()
fun clearCanvas() {
val color = if (layer.fullscreen && hostOs != OS.MacOS) -1 else clearColor protected open fun flush() {
canvas?.clear(color)
}
open fun drawOnCanvas(picture: Picture) {
canvas?.drawPicture(picture)
}
open fun flush() {
context?.flush() context?.flush()
} }
open fun dispose() { open fun dispose() {
disposeCanvas() disposeCanvas()
destroyContext() context?.close()
} }
open fun disposeCanvas() { protected open fun disposeCanvas() {
surface?.close() surface?.close()
renderTarget?.close() renderTarget?.close()
} }
...@@ -49,7 +41,16 @@ internal abstract class ContextHandler(val layer: SkiaLayer) { ...@@ -49,7 +41,16 @@ internal abstract class ContextHandler(val layer: SkiaLayer) {
"OS: ${hostOs.id} ${hostArch.id}\n" "OS: ${hostOs.id} ${hostArch.id}\n"
} }
protected open fun destroyContext() { // throws RenderException if initialization of graphic context was not successful
context?.close() fun draw() {
if (!initContext()) {
throw RenderException("Cannot init graphic context")
}
initCanvas()
canvas?.apply {
clear(if (layer.fullscreen && hostOs != OS.MacOS) -1 else clearColor)
drawContent()
}
flush()
} }
} }
...@@ -5,4 +5,5 @@ internal interface Redrawer { ...@@ -5,4 +5,5 @@ internal interface Redrawer {
fun needRedraw() fun needRedraw()
fun redrawImmediately() fun redrawImmediately()
fun syncSize() = Unit fun syncSize() = Unit
val renderInfo: String
} }
\ No newline at end of file
...@@ -2,6 +2,7 @@ package org.jetbrains.skiko ...@@ -2,6 +2,7 @@ package org.jetbrains.skiko
import kotlinx.cinterop.ObjCAction import kotlinx.cinterop.ObjCAction
import kotlinx.cinterop.useContents import kotlinx.cinterop.useContents
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.PictureRecorder import org.jetbrains.skia.PictureRecorder
import org.jetbrains.skia.Rect import org.jetbrains.skia.Rect
import org.jetbrains.skiko.context.MetalContextHandler import org.jetbrains.skiko.context.MetalContextHandler
...@@ -121,18 +122,10 @@ actual open class SkiaLayer( ...@@ -121,18 +122,10 @@ actual open class SkiaLayer(
this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt()) this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt())
} }
fun draw() { internal fun draw(canvas: Canvas) {
contextHandler?.apply { check(!isDisposed) { "SkiaLayer is disposed" }
if (!initContext()) { picture?.also {
error("initContext() failure") canvas.drawPicture(it.instance)
}
initCanvas()
clearCanvas()
val picture = picture
if (picture != null) {
drawOnCanvas(picture.instance)
}
flush()
} }
} }
} }
......
...@@ -8,7 +8,7 @@ import org.jetbrains.skia.SurfaceOrigin ...@@ -8,7 +8,7 @@ import org.jetbrains.skia.SurfaceOrigin
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.MetalRedrawer import org.jetbrains.skiko.redrawer.MetalRedrawer
internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer, layer::draw) {
val metalRedrawer: MetalRedrawer val metalRedrawer: MetalRedrawer
get() = layer.redrawer!! get() = layer.redrawer!!
......
...@@ -8,6 +8,8 @@ import kotlinx.cinterop.useContents ...@@ -8,6 +8,8 @@ import kotlinx.cinterop.useContents
import org.jetbrains.skia.BackendRenderTarget import org.jetbrains.skia.BackendRenderTarget
import org.jetbrains.skia.DirectContext import org.jetbrains.skia.DirectContext
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.MetalContextHandler
import platform.CoreGraphics.CGColorCreate import platform.CoreGraphics.CGColorCreate
import platform.CoreGraphics.CGColorSpaceCreateDeviceRGB import platform.CoreGraphics.CGColorSpaceCreateDeviceRGB
import platform.CoreGraphics.CGContextRef import platform.CoreGraphics.CGContextRef
...@@ -24,6 +26,9 @@ internal class MetalRedrawer( ...@@ -24,6 +26,9 @@ internal class MetalRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = MetalContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private var isDisposed = false private var isDisposed = false
internal val device = MTLCreateSystemDefaultDevice()!! internal val device = MTLCreateSystemDefaultDevice()!!
private val queue = device.newCommandQueue()!! private val queue = device.newCommandQueue()!!
...@@ -31,7 +36,7 @@ internal class MetalRedrawer( ...@@ -31,7 +36,7 @@ internal class MetalRedrawer(
private val metalLayer = MetalLayer() private val metalLayer = MetalLayer()
init { init {
metalLayer.init(this.layer, device) metalLayer.init(this.layer, contextHandler, device)
} }
private val frameDispatcher = FrameDispatcher(SkikoDispatchers.Main) { private val frameDispatcher = FrameDispatcher(SkikoDispatchers.Main) {
...@@ -51,6 +56,7 @@ internal class MetalRedrawer( ...@@ -51,6 +56,7 @@ internal class MetalRedrawer(
override fun dispose() { override fun dispose() {
if (!isDisposed) { if (!isDisposed) {
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose()
metalLayer.dispose() metalLayer.dispose()
isDisposed = true isDisposed = true
} }
...@@ -63,7 +69,7 @@ internal class MetalRedrawer( ...@@ -63,7 +69,7 @@ internal class MetalRedrawer(
size.width to size.height size.width to size.height
} }
metalLayer.frame = osView.frame metalLayer.frame = osView.frame
metalLayer.init(layer, device) metalLayer.init(layer, contextHandler, device)
metalLayer.drawableSize = CGSizeMake(w * metalLayer.contentsScale, h * metalLayer.contentsScale) metalLayer.drawableSize = CGSizeMake(w * metalLayer.contentsScale, h * metalLayer.contentsScale)
} }
...@@ -82,7 +88,7 @@ internal class MetalRedrawer( ...@@ -82,7 +88,7 @@ internal class MetalRedrawer(
// TODO: maybe make flush async as in JVM version. // TODO: maybe make flush async as in JVM version.
autoreleasepool { autoreleasepool {
if (!isDisposed) { if (!isDisposed) {
layer.draw() contextHandler.draw()
} }
} }
} }
...@@ -100,15 +106,22 @@ internal class MetalRedrawer( ...@@ -100,15 +106,22 @@ internal class MetalRedrawer(
} }
} }
class MetalLayer : CAMetalLayer { internal class MetalLayer : CAMetalLayer {
private lateinit var skiaLayer: SkiaLayer private lateinit var skiaLayer: SkiaLayer
private lateinit var contextHandler: ContextHandler
@OverrideInit @OverrideInit
constructor(): super() constructor(): super()
@OverrideInit @OverrideInit
constructor(layer: Any): super(layer) constructor(layer: Any): super(layer)
fun init(skiaLayer: SkiaLayer, theDevice: MTLDeviceProtocol) { fun init(
skiaLayer: SkiaLayer,
contextHandler: ContextHandler,
theDevice: MTLDeviceProtocol
) {
this.skiaLayer = skiaLayer
this.contextHandler = contextHandler
this.setNeedsDisplayOnBoundsChange(true) this.setNeedsDisplayOnBoundsChange(true)
this.removeAllAnimations() this.removeAllAnimations()
// TODO: looks like a bug in K/N interop. // TODO: looks like a bug in K/N interop.
...@@ -133,7 +146,7 @@ class MetalLayer : CAMetalLayer { ...@@ -133,7 +146,7 @@ class MetalLayer : CAMetalLayer {
override fun drawInContext(ctx: CGContextRef?) { override fun drawInContext(ctx: CGContextRef?) {
skiaLayer.update(getTimeNanos()) skiaLayer.update(getTimeNanos())
skiaLayer.draw() contextHandler.draw()
super.drawInContext(ctx) super.drawInContext(ctx)
} }
} }
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.context.*
import org.jetbrains.skiko.redrawer.* import org.jetbrains.skiko.redrawer.*
internal actual fun makeDefaultRenderFactory(): RenderFactory { internal actual fun makeDefaultRenderFactory(): RenderFactory {
return object : RenderFactory { return object : RenderFactory {
override fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): ContextHandler {
TODO()
}
override fun createRedrawer( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
......
...@@ -25,7 +25,7 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay ...@@ -25,7 +25,7 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay
} }
actual fun needRedraw() { actual fun needRedraw() {
draw() state?.draw()
} }
actual var skikoView: SkikoView? = null actual var skikoView: SkikoView? = null
...@@ -99,10 +99,6 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay ...@@ -99,10 +99,6 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay
)) ))
}) })
} }
fun draw() {
state?.draw()
}
} }
actual typealias SkikoPlatformInputEvent = InputEvent actual typealias SkikoPlatformInputEvent = InputEvent
......
package org.jetbrains.skiko package org.jetbrains.skiko
import java.io.File import java.io.File
import java.lang.RuntimeException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import kotlin.jvm.JvmStatic import kotlin.jvm.JvmStatic
internal class RenderException(
message: String? = null,
cause: Exception? = null
) : RuntimeException(message, cause)
internal class RenderExceptionsHandler { internal class RenderExceptionsHandler {
companion object { companion object {
private var output: File? = null private var output: File? = null
......
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.context.*
import org.jetbrains.skiko.redrawer.* import org.jetbrains.skiko.redrawer.*
internal actual fun makeDefaultRenderFactory(): RenderFactory { internal actual fun makeDefaultRenderFactory(): RenderFactory {
return object : RenderFactory { return object : RenderFactory {
override fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): ContextHandler {
return when (renderApi) {
GraphicsApi.DIRECT_SOFTWARE -> return when (hostOs) {
OS.Windows, OS.Linux -> DirectSoftwareContextHandler(layer)
else -> SoftwareContextHandler(layer)
}
GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
GraphicsApi.DIRECT3D -> Direct3DContextHandler(layer)
GraphicsApi.METAL -> MetalContextHandler(layer)
else -> TODO("Unsupported yet.")
}
}
override fun createRedrawer( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
......
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skia.Canvas import org.jetbrains.skia.*
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ColorInfo
import org.jetbrains.skia.ColorType
import org.jetbrains.skia.ColorSpace
import org.jetbrains.skia.ClipMode
import org.jetbrains.skia.ImageInfo
import org.jetbrains.skia.PictureRecorder
import org.jetbrains.skia.Rect
import org.jetbrains.skiko.context.ContextHandler import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import java.awt.Color import java.awt.Color
...@@ -209,7 +200,6 @@ actual open class SkiaLayer internal constructor( ...@@ -209,7 +200,6 @@ actual open class SkiaLayer internal constructor(
@Volatile @Volatile
private var isDisposed = false private var isDisposed = false
internal var redrawer: Redrawer? = null internal var redrawer: Redrawer? = null
internal var contextHandler: ContextHandler? = null
private val fallbackRenderApiQueue = SkikoProperties.fallbackRenderApiQueue.toMutableList() private val fallbackRenderApiQueue = SkikoProperties.fallbackRenderApiQueue.toMutableList()
private var renderApi_ = fallbackRenderApiQueue[0] private var renderApi_ = fallbackRenderApiQueue[0]
actual var renderApi: GraphicsApi actual var renderApi: GraphicsApi
...@@ -219,10 +209,10 @@ actual open class SkiaLayer internal constructor( ...@@ -219,10 +209,10 @@ actual open class SkiaLayer internal constructor(
notifyChange(PropertyKind.Renderer) notifyChange(PropertyKind.Renderer)
} }
val renderInfo: String val renderInfo: String
get() = if (contextHandler?.context == null) get() = if (redrawer == null)
"ContextHandler hasn't been initialized yet." "SkiaLayer isn't initialized yet"
else else
contextHandler!!.rendererInfo() redrawer!!.renderInfo
@Volatile @Volatile
private var picture: PictureHolder? = null private var picture: PictureHolder? = null
...@@ -235,9 +225,7 @@ actual open class SkiaLayer internal constructor( ...@@ -235,9 +225,7 @@ actual open class SkiaLayer internal constructor(
thrown = false thrown = false
try { try {
renderApi = fallbackRenderApiQueue.removeAt(0) renderApi = fallbackRenderApiQueue.removeAt(0)
contextHandler?.dispose()
redrawer?.dispose() redrawer?.dispose()
contextHandler = renderFactory.createContextHandler(this, renderApi)
redrawer = renderFactory.createRedrawer(this, renderApi, properties) redrawer = renderFactory.createRedrawer(this, renderApi, properties)
redrawer?.syncSize() redrawer?.syncSize()
} catch (e: RenderException) { } catch (e: RenderException) {
...@@ -251,7 +239,7 @@ actual open class SkiaLayer internal constructor( ...@@ -251,7 +239,7 @@ actual open class SkiaLayer internal constructor(
} }
} }
protected open fun init(recreation: Boolean = false) { private fun init(recreation: Boolean = false) {
isDisposed = false isDisposed = false
backedLayer.init() backedLayer.init()
pictureRecorder = PictureRecorder() pictureRecorder = PictureRecorder()
...@@ -281,8 +269,6 @@ actual open class SkiaLayer internal constructor( ...@@ -281,8 +269,6 @@ actual open class SkiaLayer internal constructor(
// we should dispose redrawer first (to cancel `draw` in rendering thread) // we should dispose redrawer first (to cancel `draw` in rendering thread)
redrawer?.dispose() redrawer?.dispose()
redrawer = null redrawer = null
contextHandler?.dispose()
contextHandler = null
picture?.instance?.close() picture?.instance?.close()
picture = null picture = null
pictureRecorder?.close() pictureRecorder?.close()
...@@ -413,6 +399,7 @@ actual open class SkiaLayer internal constructor( ...@@ -413,6 +399,7 @@ actual open class SkiaLayer internal constructor(
check(isEventDispatchThread()) { "Method should be called from AWT event dispatch thread" } check(isEventDispatchThread()) { "Method should be called from AWT event dispatch thread" }
check(!isDisposed) { "SkiaLayer is disposed" } check(!isDisposed) { "SkiaLayer is disposed" }
FrameWatcher.nextFrame()
fpsCounter?.tick() fpsCounter?.tick()
val pictureWidth = (width * contentScale).toInt().coerceAtLeast(0) val pictureWidth = (width * contentScale).toInt().coerceAtLeast(0)
...@@ -459,36 +446,28 @@ actual open class SkiaLayer internal constructor( ...@@ -459,36 +446,28 @@ actual open class SkiaLayer internal constructor(
} }
} }
// can be called from non-swing thread internal fun draw(canvas: Canvas) {
// throws exception if initialization of graphic context was not successful check(!isDisposed) { "SkiaLayer is disposed" }
internal fun draw() { lockPicture {
contextHandler?.apply { canvas.drawPicture(it.instance)
if (!initContext()) {
throw RenderException("Cannot init graphic context")
} }
initCanvas()
} }
check(!isDisposed) { "SkiaLayer is disposed" } private fun <T : Any> lockPicture(action: (PictureHolder) -> T): T? {
contextHandler?.apply { return synchronized(pictureLock) {
clearCanvas()
synchronized(pictureLock) {
val picture = picture val picture = picture
if (picture != null) { if (picture != null) {
drawOnCanvas(picture.instance) action(picture)
} } else {
null
} }
flush()
} }
FrameWatcher.nextFrame()
} }
// Captures current layer as bitmap. // Captures current layer as bitmap.
fun screenshot(): Bitmap? { fun screenshot(): Bitmap? {
return contextHandler?.let { check(!isDisposed) { "SkiaLayer is disposed" }
synchronized(pictureLock) { return lockPicture { picture ->
val picture = picture
if (picture != null) {
val store = Bitmap() val store = Bitmap()
val ci = ColorInfo( val ci = ColorInfo(
ColorType.BGRA_8888, ColorAlphaType.OPAQUE, ColorSpace.sRGB) ColorType.BGRA_8888, ColorAlphaType.OPAQUE, ColorSpace.sRGB)
...@@ -498,10 +477,6 @@ actual open class SkiaLayer internal constructor( ...@@ -498,10 +477,6 @@ actual open class SkiaLayer internal constructor(
canvas.drawPicture(picture.instance) canvas.drawPicture(picture.instance)
store.setImmutable() store.setImmutable()
store store
} else {
null
}
}
} }
} }
......
...@@ -8,7 +8,7 @@ import org.jetbrains.skiko.RenderException ...@@ -8,7 +8,7 @@ import org.jetbrains.skiko.RenderException
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.AngleRedrawer import org.jetbrains.skiko.redrawer.AngleRedrawer
internal class AngleContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class AngleContextHandler(layer: SkiaLayer) : JvmContextHandler(layer) {
val angleRedrawer: AngleRedrawer val angleRedrawer: AngleRedrawer
get() = layer.redrawer!! as AngleRedrawer get() = layer.redrawer!! as AngleRedrawer
......
...@@ -7,7 +7,7 @@ import org.jetbrains.skiko.SkiaLayer ...@@ -7,7 +7,7 @@ import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import java.lang.ref.Reference import java.lang.ref.Reference
internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class Direct3DContextHandler(layer: SkiaLayer) : JvmContextHandler(layer) {
private val bufferCount = 2 private val bufferCount = 2
private var surfaces: Array<Surface?> = arrayOfNulls(bufferCount) private var surfaces: Array<Surface?> = arrayOfNulls(bufferCount)
...@@ -85,10 +85,6 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer) ...@@ -85,10 +85,6 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
} }
} }
override fun destroyContext() {
context?.close()
}
override fun disposeCanvas() { override fun disposeCanvas() {
for (bufferIndex in 0 until bufferCount) { for (bufferIndex in 0 until bufferCount) {
surfaces[bufferIndex]?.close() surfaces[bufferIndex]?.close()
......
...@@ -5,9 +5,8 @@ import org.jetbrains.skia.impl.Native ...@@ -5,9 +5,8 @@ import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.getPtr import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.AbstractDirectSoftwareRedrawer import org.jetbrains.skiko.redrawer.AbstractDirectSoftwareRedrawer
import java.lang.ref.Reference
internal class DirectSoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class DirectSoftwareContextHandler(layer: SkiaLayer) : JvmContextHandler(layer) {
var isInited = false var isInited = false
val softwareRedrawer: AbstractDirectSoftwareRedrawer val softwareRedrawer: AbstractDirectSoftwareRedrawer
......
package org.jetbrains.skiko.context
import org.jetbrains.skiko.SkiaLayer
internal abstract class JvmContextHandler(layer: SkiaLayer) : ContextHandler(layer, layer::draw)
\ No newline at end of file
...@@ -7,7 +7,7 @@ import org.jetbrains.skia.SurfaceOrigin ...@@ -7,7 +7,7 @@ import org.jetbrains.skia.SurfaceOrigin
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.MetalRedrawer import org.jetbrains.skiko.redrawer.MetalRedrawer
internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class MetalContextHandler(layer: SkiaLayer) : JvmContextHandler(layer) {
val metalRedrawer: MetalRedrawer val metalRedrawer: MetalRedrawer
get() = layer.redrawer!! as MetalRedrawer get() = layer.redrawer!! as MetalRedrawer
......
...@@ -10,7 +10,7 @@ import org.jetbrains.skiko.SkiaLayer ...@@ -10,7 +10,7 @@ import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.makeGLContext import org.jetbrains.skiko.makeGLContext
import org.jetbrains.skiko.makeGLRenderTarget import org.jetbrains.skiko.makeGLRenderTarget
internal class OpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class OpenGLContextHandler(layer: SkiaLayer) : JvmContextHandler(layer) {
override fun initContext(): Boolean { override fun initContext(): Boolean {
try { try {
if (context == null) { if (context == null) {
...@@ -73,13 +73,4 @@ internal class OpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) { ...@@ -73,13 +73,4 @@ internal class OpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
"Model: ${gl.glGetString(gl.GL_RENDERER)}\n" + "Model: ${gl.glGetString(gl.GL_RENDERER)}\n" +
"Total VRAM: ${gl.glGetIntegerv(gl.GL_TOTAL_MEMORY) / 1024} MB\n" "Total VRAM: ${gl.glGetIntegerv(gl.GL_TOTAL_MEMORY) / 1024} MB\n"
} }
fun disposeInOpenGLContext() {
super.dispose()
}
// dispose should be called only in the OpenGL context.
// TODO remove in https://github.com/JetBrains/skiko/pull/300
override open fun dispose() {
}
} }
...@@ -18,7 +18,7 @@ import java.awt.image.DataBufferByte ...@@ -18,7 +18,7 @@ import java.awt.image.DataBufferByte
import java.awt.image.Raster import java.awt.image.Raster
import java.awt.image.WritableRaster import java.awt.image.WritableRaster
internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class SoftwareContextHandler(layer: SkiaLayer) : JvmContextHandler(layer) {
override val clearColor = if (layer.transparency && hostOs == OS.MacOS) 0 else -1 override val clearColor = if (layer.transparency && hostOs == OS.MacOS) 0 else -1
val colorModel = ComponentColorModel( val colorModel = ComponentColorModel(
...@@ -59,9 +59,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer) ...@@ -59,9 +59,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer)
canvas = Canvas(storage) canvas = Canvas(storage)
} }
override fun drawOnCanvas(picture: Picture) { override fun flush() {
super.drawOnCanvas(picture)
val scale = layer.contentScale val scale = layer.contentScale
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)
...@@ -87,8 +85,4 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer) ...@@ -87,8 +85,4 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer)
graphics?.drawImage(image!!, 0, 0, layer.width, layer.height, null) graphics?.drawImage(image!!, 0, 0, layer.width, layer.height, null)
} }
} }
override fun flush() {
// Raster does not need to flush canvas
}
} }
...@@ -8,11 +8,15 @@ import org.jetbrains.skiko.FrameLimiter ...@@ -8,11 +8,15 @@ import org.jetbrains.skiko.FrameLimiter
import org.jetbrains.skiko.RenderException import org.jetbrains.skiko.RenderException
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.context.DirectSoftwareContextHandler
internal abstract class AbstractDirectSoftwareRedrawer( internal abstract class AbstractDirectSoftwareRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = DirectSoftwareContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private val frameJob = Job() private val frameJob = Job()
private val frameLimiter = FrameLimiter(CoroutineScope(Dispatchers.IO + frameJob), layer.backedLayer) private val frameLimiter = FrameLimiter(CoroutineScope(Dispatchers.IO + frameJob), layer.backedLayer)
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
...@@ -22,7 +26,7 @@ internal abstract class AbstractDirectSoftwareRedrawer( ...@@ -22,7 +26,7 @@ internal abstract class AbstractDirectSoftwareRedrawer(
if (layer.isShowing) { if (layer.isShowing) {
layer.update(System.nanoTime()) layer.update(System.nanoTime())
layer.inDrawScope(layer::draw) layer.inDrawScope(contextHandler::draw)
} }
} }
...@@ -34,7 +38,7 @@ internal abstract class AbstractDirectSoftwareRedrawer( ...@@ -34,7 +38,7 @@ internal abstract class AbstractDirectSoftwareRedrawer(
override fun redrawImmediately() { override fun redrawImmediately() {
layer.update(System.nanoTime()) layer.update(System.nanoTime())
layer.inDrawScope(layer::draw) layer.inDrawScope(contextHandler::draw)
} }
open fun resize(width: Int, height: Int) = resize(device, width, height) open fun resize(width: Int, height: Int) = resize(device, width, height)
...@@ -47,8 +51,9 @@ internal abstract class AbstractDirectSoftwareRedrawer( ...@@ -47,8 +51,9 @@ internal abstract class AbstractDirectSoftwareRedrawer(
} }
open fun finishFrame() = finishFrame(device) open fun finishFrame() = finishFrame(device)
override fun dispose() { override fun dispose() {
disposeDevice(device)
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose()
disposeDevice(device)
runBlocking { runBlocking {
frameJob.cancelAndJoin() frameJob.cancelAndJoin()
} }
......
...@@ -7,11 +7,15 @@ import org.jetbrains.skia.DirectContext ...@@ -7,11 +7,15 @@ import org.jetbrains.skia.DirectContext
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.context.AngleContextHandler
import org.jetbrains.skiko.context.DirectSoftwareContextHandler
internal class AngleRedrawer( internal class AngleRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = AngleContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private var isDisposed = false private var isDisposed = false
private var device: Long = 0 private var device: Long = 0
...@@ -25,6 +29,7 @@ internal class AngleRedrawer( ...@@ -25,6 +29,7 @@ internal class AngleRedrawer(
override fun dispose() { override fun dispose() {
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose()
disposeDevice(device) disposeDevice(device)
isDisposed = true isDisposed = true
} }
...@@ -45,7 +50,7 @@ internal class AngleRedrawer( ...@@ -45,7 +50,7 @@ internal class AngleRedrawer(
} }
private fun draw() { private fun draw() {
layer.draw() contextHandler.draw()
} }
fun createDevice(): Long { fun createDevice(): Long {
......
...@@ -6,11 +6,15 @@ import kotlinx.coroutines.withContext ...@@ -6,11 +6,15 @@ import kotlinx.coroutines.withContext
import org.jetbrains.skia.DirectContext import org.jetbrains.skia.DirectContext
import org.jetbrains.skia.Surface import org.jetbrains.skia.Surface
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.AngleContextHandler
import org.jetbrains.skiko.context.Direct3DContextHandler
internal class Direct3DRedrawer( internal class Direct3DRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = Direct3DContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private var isDisposed = false private var isDisposed = false
private var drawLock = Any() private var drawLock = Any()
...@@ -29,8 +33,9 @@ internal class Direct3DRedrawer( ...@@ -29,8 +33,9 @@ internal class Direct3DRedrawer(
} }
override fun dispose() = synchronized(drawLock) { override fun dispose() = synchronized(drawLock) {
disposeDevice(device)
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose()
disposeDevice(device)
isDisposed = true isDisposed = true
} }
...@@ -61,7 +66,7 @@ internal class Direct3DRedrawer( ...@@ -61,7 +66,7 @@ internal class Direct3DRedrawer(
private fun drawAndSwap(withVsync: Boolean) = synchronized(drawLock) { private fun drawAndSwap(withVsync: Boolean) = synchronized(drawLock) {
if (!isDisposed) { if (!isDisposed) {
layer.draw() contextHandler.draw()
swap(device, withVsync) swap(device, withVsync)
} }
} }
......
...@@ -3,12 +3,16 @@ package org.jetbrains.skiko.redrawer ...@@ -3,12 +3,16 @@ package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.Direct3DContextHandler
import org.jetbrains.skiko.context.OpenGLContextHandler import org.jetbrains.skiko.context.OpenGLContextHandler
internal class LinuxOpenGLRedrawer( internal class LinuxOpenGLRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = OpenGLContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private var isDisposed = false private var isDisposed = false
private var context = 0L private var context = 0L
private val swapInterval = if (properties.isVsyncEnabled) 1 else 0 private val swapInterval = if (properties.isVsyncEnabled) 1 else 0
...@@ -52,8 +56,7 @@ internal class LinuxOpenGLRedrawer( ...@@ -52,8 +56,7 @@ internal class LinuxOpenGLRedrawer(
// makeCurrent is mandatory to destroy context, otherwise, OpenGL will destroy wrong context (from another window). // makeCurrent is mandatory to destroy context, otherwise, OpenGL will destroy wrong context (from another window).
// see the official example: https://www.khronos.org/opengl/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX) // see the official example: https://www.khronos.org/opengl/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
it.makeCurrent(context) it.makeCurrent(context)
// TODO remove in https://github.com/JetBrains/skiko/pull/300 contextHandler.dispose()
(layer.contextHandler as OpenGLContextHandler).disposeInOpenGLContext()
it.destroyContext(context) it.destroyContext(context)
} }
runBlocking { runBlocking {
...@@ -84,7 +87,7 @@ internal class LinuxOpenGLRedrawer( ...@@ -84,7 +87,7 @@ internal class LinuxOpenGLRedrawer(
} }
private fun draw() { private fun draw() {
layer.inDrawScope(layer::draw) layer.inDrawScope(contextHandler::draw)
} }
companion object { companion object {
......
...@@ -8,12 +8,17 @@ import kotlinx.coroutines.withContext ...@@ -8,12 +8,17 @@ import kotlinx.coroutines.withContext
import org.jetbrains.skia.BackendRenderTarget import org.jetbrains.skia.BackendRenderTarget
import org.jetbrains.skia.DirectContext import org.jetbrains.skia.DirectContext
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.MetalContextHandler
import org.jetbrains.skiko.context.OpenGLContextHandler
import javax.swing.SwingUtilities.* import javax.swing.SwingUtilities.*
internal class MetalRedrawer( internal class MetalRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = MetalContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
companion object { companion object {
init { init {
Library.load() Library.load()
...@@ -39,6 +44,7 @@ internal class MetalRedrawer( ...@@ -39,6 +44,7 @@ internal class MetalRedrawer(
override fun dispose() = synchronized(drawLock) { override fun dispose() = synchronized(drawLock) {
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose()
disposeDevice(device) disposeDevice(device)
isDisposed = true isDisposed = true
} }
...@@ -90,7 +96,7 @@ internal class MetalRedrawer( ...@@ -90,7 +96,7 @@ internal class MetalRedrawer(
if (!isDisposed) { if (!isDisposed) {
val handle = startRendering() val handle = startRendering()
try { try {
layer.draw() contextHandler.draw()
} finally { } finally {
endRendering(handle) endRendering(handle)
} }
......
...@@ -6,11 +6,16 @@ import org.jetbrains.skiko.FrameDispatcher ...@@ -6,11 +6,16 @@ import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.FrameLimiter import org.jetbrains.skiko.FrameLimiter
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.context.MetalContextHandler
import org.jetbrains.skiko.context.SoftwareContextHandler
internal class SoftwareRedrawer( internal class SoftwareRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = SoftwareContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private val frameJob = Job() private val frameJob = Job()
private val frameLimiter = FrameLimiter(CoroutineScope(Dispatchers.IO + frameJob), layer.backedLayer) private val frameLimiter = FrameLimiter(CoroutineScope(Dispatchers.IO + frameJob), layer.backedLayer)
...@@ -21,12 +26,13 @@ internal class SoftwareRedrawer( ...@@ -21,12 +26,13 @@ internal class SoftwareRedrawer(
if (layer.isShowing) { if (layer.isShowing) {
layer.update(System.nanoTime()) layer.update(System.nanoTime())
layer.inDrawScope(layer::draw) layer.inDrawScope(contextHandler::draw)
} }
} }
override fun dispose() { override fun dispose() {
frameDispatcher.cancel() frameDispatcher.cancel()
contextHandler.dispose()
runBlocking { runBlocking {
frameJob.cancelAndJoin() frameJob.cancelAndJoin()
} }
...@@ -38,6 +44,6 @@ internal class SoftwareRedrawer( ...@@ -38,6 +44,6 @@ internal class SoftwareRedrawer(
override fun redrawImmediately() { override fun redrawImmediately() {
layer.update(System.nanoTime()) layer.update(System.nanoTime())
layer.inDrawScope(layer::draw) layer.inDrawScope(contextHandler::draw)
} }
} }
\ No newline at end of file
...@@ -6,11 +6,15 @@ import kotlinx.coroutines.swing.Swing ...@@ -6,11 +6,15 @@ import kotlinx.coroutines.swing.Swing
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.OpenGLContextHandler import org.jetbrains.skiko.context.OpenGLContextHandler
import org.jetbrains.skiko.context.SoftwareContextHandler
internal class WindowsOpenGLRedrawer( internal class WindowsOpenGLRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = OpenGLContextHandler(layer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private val device = layer.backedLayer.useDrawingSurfacePlatformInfo(::getDevice) private val device = layer.backedLayer.useDrawingSurfacePlatformInfo(::getDevice)
private val context = createContext(device, layer.contentHandle, layer.transparency).also { private val context = createContext(device, layer.contentHandle, layer.transparency).also {
if (it == 0L) { if (it == 0L) {
...@@ -35,8 +39,7 @@ internal class WindowsOpenGLRedrawer( ...@@ -35,8 +39,7 @@ internal class WindowsOpenGLRedrawer(
override fun dispose() { override fun dispose() {
check(!isDisposed) { "WindowsOpenGLRedrawer is disposed" } check(!isDisposed) { "WindowsOpenGLRedrawer is disposed" }
makeCurrent() makeCurrent()
// TODO remove in https://github.com/JetBrains/skiko/pull/300 contextHandler.dispose()
(layer.contextHandler as OpenGLContextHandler).disposeInOpenGLContext()
deleteContext(context) deleteContext(context)
isDisposed = true isDisposed = true
} }
...@@ -61,7 +64,7 @@ internal class WindowsOpenGLRedrawer( ...@@ -61,7 +64,7 @@ internal class WindowsOpenGLRedrawer(
} }
private fun draw() { private fun draw() {
layer.inDrawScope(layer::draw) layer.inDrawScope(contextHandler::draw)
} }
private fun makeCurrent() = makeCurrent(device, context) private fun makeCurrent() = makeCurrent(device, context)
......
...@@ -11,11 +11,10 @@ import org.jetbrains.skia.paragraph.FontCollection ...@@ -11,11 +11,10 @@ import org.jetbrains.skia.paragraph.FontCollection
import org.jetbrains.skia.paragraph.ParagraphBuilder 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.ContextHandler import org.jetbrains.skiko.context.JvmContextHandler
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.swingTest import org.jetbrains.skiko.util.swingTest
import org.junit.Assert
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assume.assumeTrue import org.junit.Assume.assumeTrue
import org.junit.Rule import org.junit.Rule
...@@ -174,7 +173,7 @@ class SkiaWindowTest { ...@@ -174,7 +173,7 @@ class SkiaWindowTest {
@Test @Test
fun `render three windows`() = swingTest { fun `render three windows`() = swingTest {
fun window(color: Color) = SkiaWindow().apply { fun window(color: Color) = SkiaWindow().apply {
setLocation(200,200) setLocation(200, 200)
setSize(400, 200) setSize(400, 200)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.skikoView = RectRenderer(layer, 200, 100, color) layer.skikoView = RectRenderer(layer, 200, 100, color)
...@@ -244,7 +243,7 @@ class SkiaWindowTest { ...@@ -244,7 +243,7 @@ class SkiaWindowTest {
@Test(timeout = 60000) @Test(timeout = 60000)
fun `stress test - open multiple windows`() = swingTest { fun `stress test - open multiple windows`() = swingTest {
fun window(isAnimated: Boolean) = SkiaWindow().apply { fun window(isAnimated: Boolean) = SkiaWindow().apply {
setLocation(200,200) setLocation(200, 200)
setSize(40, 20) setSize(40, 20)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.skikoView = if (isAnimated) { layer.skikoView = if (isAnimated) {
...@@ -288,7 +287,7 @@ class SkiaWindowTest { ...@@ -288,7 +287,7 @@ class SkiaWindowTest {
fun openWindow() = SkiaWindow( fun openWindow() = SkiaWindow(
properties = SkiaLayerProperties(isVsyncEnabled = false, isVsyncFramelimitFallbackEnabled = true) properties = SkiaLayerProperties(isVsyncEnabled = false, isVsyncFramelimitFallbackEnabled = true)
).apply { ).apply {
setLocation(200,200) setLocation(200, 200)
setSize(400, 200) setSize(400, 200)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
layer.skikoView = AnimatedBoxRenderer(layer, pixelsPerSecond = 20.0, size = 20.0) layer.skikoView = AnimatedBoxRenderer(layer, pixelsPerSecond = 20.0, size = 20.0)
...@@ -311,7 +310,7 @@ class SkiaWindowTest { ...@@ -311,7 +310,7 @@ class SkiaWindowTest {
fun openWindow() = SkiaWindow( fun openWindow() = SkiaWindow(
properties = SkiaLayerProperties(isVsyncEnabled = false, isVsyncFramelimitFallbackEnabled = true) properties = SkiaLayerProperties(isVsyncEnabled = false, isVsyncFramelimitFallbackEnabled = true)
).apply { ).apply {
setLocation(200,200) setLocation(200, 200)
setSize(400, 200) setSize(400, 200)
preferredSize = Dimension(400, 200) preferredSize = Dimension(400, 200)
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
...@@ -336,22 +335,20 @@ class SkiaWindowTest { ...@@ -336,22 +335,20 @@ class SkiaWindowTest {
fun `fallback to software renderer, fail on init context`() = swingTest { fun `fallback to software renderer, fail on init context`() = swingTest {
testFallbackToSoftware( testFallbackToSoftware(
object : RenderFactory { object : RenderFactory {
override fun createContextHandler( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi renderApi: GraphicsApi,
) = object : ContextHandler(layer) { properties: SkiaLayerProperties
) = object : Redrawer {
private val contextHandler = object : JvmContextHandler(layer) {
override fun initContext() = false override fun initContext() = false
override fun initCanvas() = Unit override fun initCanvas() = Unit
} }
override fun createRedrawer(
layer: SkiaLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
): Redrawer = object : Redrawer {
override fun dispose() = Unit override fun dispose() = Unit
override fun needRedraw() = Unit override fun needRedraw() = Unit
override fun redrawImmediately() = layer.inDrawScope { layer.draw() } override fun redrawImmediately() = layer.inDrawScope(contextHandler::draw)
override val renderInfo = ""
} }
} }
) )
...@@ -361,14 +358,6 @@ class SkiaWindowTest { ...@@ -361,14 +358,6 @@ class SkiaWindowTest {
fun `fallback to software renderer, fail on create redrawer`() = swingTest { fun `fallback to software renderer, fail on create redrawer`() = swingTest {
testFallbackToSoftware( testFallbackToSoftware(
object : RenderFactory { object : RenderFactory {
override fun createContextHandler(
layer: SkiaLayer,
renderApi: GraphicsApi
) = object : ContextHandler(layer) {
override fun initContext() = true
override fun initCanvas() = Unit
}
override fun createRedrawer( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
...@@ -382,14 +371,6 @@ class SkiaWindowTest { ...@@ -382,14 +371,6 @@ class SkiaWindowTest {
fun `fallback to software renderer, fail on draw`() = swingTest { fun `fallback to software renderer, fail on draw`() = swingTest {
testFallbackToSoftware( testFallbackToSoftware(
object : RenderFactory { object : RenderFactory {
override fun createContextHandler(
layer: SkiaLayer,
renderApi: GraphicsApi
) = object : ContextHandler(layer) {
override fun initContext() = true
override fun initCanvas() = Unit
}
override fun createRedrawer( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
...@@ -400,6 +381,7 @@ class SkiaWindowTest { ...@@ -400,6 +381,7 @@ class SkiaWindowTest {
override fun redrawImmediately() = layer.inDrawScope { override fun redrawImmediately() = layer.inDrawScope {
throw RenderException() throw RenderException()
} }
override val renderInfo = ""
} }
} }
) )
...@@ -439,14 +421,6 @@ class SkiaWindowTest { ...@@ -439,14 +421,6 @@ class SkiaWindowTest {
private class OverrideNonSoftwareRenderFactory( private class OverrideNonSoftwareRenderFactory(
private val nonSoftwareRenderFactory: RenderFactory private val nonSoftwareRenderFactory: RenderFactory
) : RenderFactory { ) : RenderFactory {
override fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): ContextHandler {
return if (renderApi == GraphicsApi.SOFTWARE) {
RenderFactory.Default.createContextHandler(layer, renderApi)
} else {
nonSoftwareRenderFactory.createContextHandler(layer, renderApi)
}
}
override fun createRedrawer( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
......
...@@ -37,8 +37,6 @@ actual open class SkiaLayer( ...@@ -37,8 +37,6 @@ actual open class SkiaLayer(
actual var skikoView: SkikoView? = null actual var skikoView: SkikoView? = null
private var contextHandler = MacOSOpenGLContextHandler(this)
private var redrawer: Redrawer? = null private var redrawer: Redrawer? = null
private var picture: PictureHolder? = null private var picture: PictureHolder? = null
...@@ -131,13 +129,11 @@ actual open class SkiaLayer( ...@@ -131,13 +129,11 @@ actual open class SkiaLayer(
} }
nsView.frame = CGRectMake(0.0, 0.0, w, h) nsView.frame = CGRectMake(0.0, 0.0, w, h)
redrawer?.syncSize() redrawer?.syncSize()
initedCanvas = false
redrawer?.redrawImmediately() redrawer?.redrawImmediately()
} }
override fun windowDidChangeBackingProperties(notification: NSNotification) { override fun windowDidChangeBackingProperties(notification: NSNotification) {
redrawer?.syncSize() redrawer?.syncSize()
initedCanvas = false
redrawer?.redrawImmediately() redrawer?.redrawImmediately()
} }
} }
...@@ -151,14 +147,13 @@ actual open class SkiaLayer( ...@@ -151,14 +147,13 @@ actual open class SkiaLayer(
actual fun detach() { actual fun detach() {
redrawer?.dispose() redrawer?.dispose()
redrawer = null redrawer = null
initedCanvas = false
} }
actual fun needRedraw() { actual fun needRedraw() {
redrawer?.needRedraw() redrawer?.needRedraw()
} }
fun update(nanoTime: Long) { internal fun update(nanoTime: Long) {
val width = nsView.frame.useContents { size.width } val width = nsView.frame.useContents { size.width }
val height = nsView.frame.useContents { size.height } val height = nsView.frame.useContents { size.height }
...@@ -173,24 +168,9 @@ actual open class SkiaLayer( ...@@ -173,24 +168,9 @@ actual open class SkiaLayer(
this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt()) this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt())
} }
private var initedCanvas = false internal fun draw(canvas: Canvas) {
picture?.also {
fun draw() { canvas.drawPicture(it.instance)
contextHandler.apply {
if (!initedCanvas) {
if (!initContext()) {
error("initContext() failure")
return
}
initCanvas()
initedCanvas = true
}
clearCanvas()
val picture = picture
if (picture != null) {
drawOnCanvas(picture.instance)
}
flush()
} }
} }
} }
......
...@@ -7,7 +7,7 @@ import platform.OpenGL.GL_DRAW_FRAMEBUFFER_BINDING ...@@ -7,7 +7,7 @@ import platform.OpenGL.GL_DRAW_FRAMEBUFFER_BINDING
import platform.OpenGL.glGetIntegerv import platform.OpenGL.glGetIntegerv
import platform.OpenGLCommon.GLenum import platform.OpenGLCommon.GLenum
internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) { internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer, layer::draw) {
override fun initContext(): Boolean { override fun initContext(): Boolean {
try { try {
if (context == null) { if (context == null) {
...@@ -31,7 +31,10 @@ internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(laye ...@@ -31,7 +31,10 @@ internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(laye
return result return result
} }
var initedCanvas = false
override fun initCanvas() { override fun initCanvas() {
if (!initedCanvas) {
val scale = layer.contentScale val scale = layer.contentScale
val w = (layer.nsView.frame.useContents { size.width } * scale).toInt().coerceAtLeast(0) val w = (layer.nsView.frame.useContents { size.width } * scale).toInt().coerceAtLeast(0)
val h = (layer.nsView.frame.useContents { size.height } * scale).toInt().coerceAtLeast(0) val h = (layer.nsView.frame.useContents { size.height } * scale).toInt().coerceAtLeast(0)
...@@ -55,5 +58,8 @@ internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(laye ...@@ -55,5 +58,8 @@ internal class MacOSOpenGLContextHandler(layer: SkiaLayer) : ContextHandler(laye
canvas = surface?.canvas canvas = surface?.canvas
?: error("Could not obtain Canvas from Surface") ?: error("Could not obtain Canvas from Surface")
initedCanvas = true
}
} }
} }
...@@ -6,6 +6,8 @@ import org.jetbrains.skiko.FrameDispatcher ...@@ -6,6 +6,8 @@ import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.SkikoDispatchers import org.jetbrains.skiko.SkikoDispatchers
import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.MacOSOpenGLContextHandler
import platform.CoreFoundation.CFTimeInterval import platform.CoreFoundation.CFTimeInterval
import platform.CoreGraphics.CGRectMake import platform.CoreGraphics.CGRectMake
import platform.CoreVideo.CVTimeStamp import platform.CoreVideo.CVTimeStamp
...@@ -20,10 +22,13 @@ internal class MacOsOpenGLRedrawer( ...@@ -20,10 +22,13 @@ internal class MacOsOpenGLRedrawer(
private val skiaLayer: SkiaLayer, private val skiaLayer: SkiaLayer,
private val properties: SkiaLayerProperties private val properties: SkiaLayerProperties
) : Redrawer { ) : Redrawer {
private val contextHandler = MacOSOpenGLContextHandler(skiaLayer)
override val renderInfo: String get() = contextHandler.rendererInfo()
private val glLayer = MacosGLLayer() private val glLayer = MacosGLLayer()
init { init {
glLayer.init(skiaLayer) glLayer.init(skiaLayer, contextHandler)
} }
private val frameDispatcher = FrameDispatcher(SkikoDispatchers.Main) { private val frameDispatcher = FrameDispatcher(SkikoDispatchers.Main) {
...@@ -31,6 +36,7 @@ internal class MacOsOpenGLRedrawer( ...@@ -31,6 +36,7 @@ internal class MacOsOpenGLRedrawer(
} }
override fun dispose() { override fun dispose() {
contextHandler.dispose()
glLayer.dispose() glLayer.dispose()
} }
...@@ -44,6 +50,7 @@ internal class MacOsOpenGLRedrawer( ...@@ -44,6 +50,7 @@ internal class MacOsOpenGLRedrawer(
size.height.toInt().coerceAtLeast(0) size.height.toInt().coerceAtLeast(0)
) )
} }
contextHandler.initedCanvas = false
} }
private fun syncContentScale() { private fun syncContentScale() {
...@@ -66,13 +73,15 @@ internal class MacOsOpenGLRedrawer( ...@@ -66,13 +73,15 @@ internal class MacOsOpenGLRedrawer(
internal class MacosGLLayer : CAOpenGLLayer { internal class MacosGLLayer : CAOpenGLLayer {
private lateinit var layer: SkiaLayer private lateinit var layer: SkiaLayer
private lateinit var contextHandler: ContextHandler
@OverrideInit @OverrideInit
constructor(): super() constructor(): super()
@OverrideInit @OverrideInit
constructor(layer: Any): super(layer) constructor(layer: Any): super(layer)
fun init(layer: SkiaLayer) { fun init(layer: SkiaLayer, contextHandler: ContextHandler) {
this.layer = layer this.layer = layer
this.contextHandler = contextHandler
this.setNeedsDisplayOnBoundsChange(true) this.setNeedsDisplayOnBoundsChange(true)
this.removeAllAnimations() this.removeAllAnimations()
this.setAutoresizingMask(kCALayerWidthSizable or kCALayerHeightSizable ) this.setAutoresizingMask(kCALayerWidthSizable or kCALayerHeightSizable )
...@@ -112,7 +121,7 @@ internal class MacosGLLayer : CAOpenGLLayer { ...@@ -112,7 +121,7 @@ internal class MacosGLLayer : CAOpenGLLayer {
CGLSetCurrentContext(ctx); CGLSetCurrentContext(ctx);
try { try {
layer.update(getTimeNanos()) layer.update(getTimeNanos())
layer.draw() contextHandler.draw()
} catch (e: Throwable) { } catch (e: Throwable) {
e.printStackTrace() e.printStackTrace()
throw e throw e
......
...@@ -13,10 +13,6 @@ internal expect fun createNativeRedrawer( ...@@ -13,10 +13,6 @@ internal expect fun createNativeRedrawer(
internal actual fun makeDefaultRenderFactory(): RenderFactory { internal actual fun makeDefaultRenderFactory(): RenderFactory {
return object : RenderFactory { return object : RenderFactory {
override fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): ContextHandler {
return createNativeContextHandler(layer, renderApi)
}
override fun createRedrawer( override fun createRedrawer(
layer: SkiaLayer, layer: SkiaLayer,
renderApi: GraphicsApi, renderApi: GraphicsApi,
......
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