Skip to content

Commit

Permalink
Merge pull request #56 from JetBrains/raster
Browse files Browse the repository at this point in the history
Raster
  • Loading branch information
Rsedaikin committed Feb 1, 2021
2 parents 58d59a8 + 0974209 commit 6ad0a4e
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ open class SkiaPanel: JLayeredPane {
}

override fun removeNotify() {
layer.dispose()
super.removeNotify()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package org.jetbrains.skiko

enum class GraphicsApi {
UNKNOWN, OPENGL, VULKAN, METAL
UNKNOWN, SOFTWARE, OPENGL, DIRECT3D, VULKAN, METAL
}
20 changes: 16 additions & 4 deletions skiko/src/jvmMain/kotlin/org/jetbrains/skiko/PlatformOperations.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.jetbrains.skiko

import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.context.renderApi
import org.jetbrains.skiko.redrawer.LinuxRedrawer
import org.jetbrains.skiko.redrawer.MacOsRedrawer
import org.jetbrains.skiko.redrawer.RasterRedrawer
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.WindowsRedrawer
import java.awt.Component
Expand All @@ -12,7 +15,7 @@ internal interface PlatformOperations {
fun isFullscreen(component: Component): Boolean
fun setFullscreen(component: Component, value: Boolean)
fun getDpiScale(component: Component): Float
fun createHardwareRedrawer(layer: HardwareLayer): Redrawer
fun createRedrawer(layer: HardwareLayer): Redrawer
}

internal val platformOperations: PlatformOperations by lazy {
Expand All @@ -30,7 +33,10 @@ internal val platformOperations: PlatformOperations by lazy {
return component.graphicsConfiguration.defaultTransform.scaleX.toFloat()
}

override fun createHardwareRedrawer(layer: HardwareLayer) = MacOsRedrawer(layer)
override fun createRedrawer(layer: HardwareLayer) = when(renderApi) {
GraphicsApi.SOFTWARE -> RasterRedrawer(layer)
else -> MacOsRedrawer(layer)
}
}
OS.Windows -> {
object: PlatformOperations {
Expand All @@ -50,7 +56,10 @@ internal val platformOperations: PlatformOperations by lazy {
return component.graphicsConfiguration.defaultTransform.scaleX.toFloat()
}

override fun createHardwareRedrawer(layer: HardwareLayer) = WindowsRedrawer(layer)
override fun createRedrawer(layer: HardwareLayer) = when(renderApi) {
GraphicsApi.SOFTWARE -> RasterRedrawer(layer)
else -> WindowsRedrawer(layer)
}
}
}
OS.Linux -> {
Expand Down Expand Up @@ -83,7 +92,10 @@ internal val platformOperations: PlatformOperations by lazy {
// return linuxGetDpiScaleNative(component)
}

override fun createHardwareRedrawer(layer: HardwareLayer) = LinuxRedrawer(layer)
override fun createRedrawer(layer: HardwareLayer) = when(renderApi) {
GraphicsApi.SOFTWARE -> RasterRedrawer(layer)
else -> LinuxRedrawer(layer)
}
}
}
}
Expand Down
95 changes: 19 additions & 76 deletions skiko/src/jvmMain/kotlin/org/jetbrains/skiko/SkiaLayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,23 @@ package org.jetbrains.skiko

import org.jetbrains.skija.*
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.RasterRedrawer
import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.context.SoftwareContextHandler
import java.awt.Graphics
import javax.swing.SwingUtilities.isEventDispatchThread

private class SkijaState {
val bleachConstant = if (hostOs == OS.MacOS) 0 else -1
var context: DirectContext? = null
var renderTarget: BackendRenderTarget? = null
var surface: Surface? = null
var canvas: Canvas? = null

fun clear() {
surface?.close()
renderTarget?.close()
}
}

interface SkiaRenderer {
suspend fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
}

private class PictureHolder(val instance: Picture, val width: Int, val height: Int)

open class SkiaLayer : HardwareLayer() {
open val api: GraphicsApi = GraphicsApi.OPENGL

var renderer: SkiaRenderer? = null
val clipComponents = mutableListOf<ClipRectangle>()

private val skijaState = SkijaState()
internal var skijaState = createContextHandler(this)

@Volatile
private var isDisposed = false
Expand All @@ -43,7 +31,7 @@ open class SkiaLayer : HardwareLayer() {

override fun init() {
super.init()
redrawer = platformOperations.createHardwareRedrawer(this)
redrawer = platformOperations.createRedrawer(this)
redrawer?.syncSize()
needRedraw()
}
Expand Down Expand Up @@ -114,26 +102,20 @@ open class SkiaLayer : HardwareLayer() {

override fun draw() {
check(!isDisposed)

if (skijaState.context == null) {
skijaState.context = when (api) {
GraphicsApi.OPENGL -> makeGLContext()
GraphicsApi.METAL -> makeMetalContext()
else -> TODO("Unsupported yet")
}
}

initSkija()

skijaState.apply {
canvas!!.clear(bleachConstant)
if (!initContext()) {
fallbackToRaster()
return
}
initCanvas()
clearCanvas()
synchronized(pictureLock) {
val picture = picture
if (picture != null) {
canvas!!.drawPicture(picture.instance)
drawOnCanvas(picture.instance)
}
}
context!!.flush()
flush()
}
}

Expand All @@ -151,50 +133,11 @@ open class SkiaLayer : HardwareLayer() {
)
}

private fun initSkija() {
initRenderTarget()
initSurface()
}

private fun initRenderTarget() {
skijaState.apply {
clear()
val dpi = contentScale
val width = (width * dpi).toInt().coerceAtLeast(0)
val height = (height * dpi).toInt().coerceAtLeast(0)
renderTarget = when (api) {
GraphicsApi.OPENGL -> {
val gl = OpenGLApi.instance
val fbId = gl.glGetIntegerv(gl.GL_DRAW_FRAMEBUFFER_BINDING)
makeGLRenderTarget(
width,
height,
0,
8,
fbId,
FramebufferFormat.GR_GL_RGBA8
)
}
GraphicsApi.METAL -> makeMetalRenderTarget(
width,
height,
0
)
else -> TODO("Unsupported yet")
}
}
}

private fun initSurface() {
skijaState.apply {
surface = Surface.makeFromBackendRenderTarget(
context!!,
renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.getSRGB()
)
canvas = surface!!.canvas
}
private fun fallbackToRaster() {
println("Falling back to software rendering...")
redrawer?.dispose()
skijaState = SoftwareContextHandler(this)
redrawer = RasterRedrawer(this)
needRedraw()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.jetbrains.skiko.context

import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.Canvas
import org.jetbrains.skija.DirectContext
import org.jetbrains.skija.Picture
import org.jetbrains.skija.Surface
import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.hostOs
import org.jetbrains.skiko.OS

internal val renderApi: GraphicsApi by lazy {
val environment = System.getenv("SKIKO_RENDER_API")
val property = System.getProperty("skiko.renderApi")
if (environment != null) {
parseRenderApi(environment)
} else {
parseRenderApi(property)
}
}

private fun parseRenderApi(text: String?): GraphicsApi {
when(text) {
"SOFTWARE" -> return GraphicsApi.SOFTWARE
"OPENGL" -> return GraphicsApi.OPENGL
else -> return GraphicsApi.OPENGL
}
}

internal fun createContextHandler(layer: HardwareLayer): ContextHandler {
return when (renderApi) {
GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
else -> TODO("Unsupported yet")
}
}

internal abstract class ContextHandler(val layer: HardwareLayer) {
open val bleachConstant = if (hostOs == OS.MacOS) 0 else -1
var context: DirectContext? = null
var renderTarget: BackendRenderTarget? = null
var surface: Surface? = null
var canvas: Canvas? = null

abstract fun initContext(): Boolean

abstract fun initCanvas()

fun clearCanvas() {
canvas?.clear(bleachConstant)
}

open fun drawOnCanvas(picture: Picture) {
canvas?.drawPicture(picture)
}

open fun flush() {
context?.flush()
}

fun dispose() {
surface?.close()
renderTarget?.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.jetbrains.skiko.context

import org.jetbrains.skija.ColorSpace
import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.makeMetalContext
import org.jetbrains.skiko.makeMetalRenderTarget

internal class MetalContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
override fun initContext(): Boolean {
try {
if (context == null) {
context = makeMetalContext()
}
} catch (e: Exception) {
println("Failed to create Skia Metal context!")
return false
}
return true
}

override fun initCanvas() {
dispose()

val scale = layer.contentScale
val w = (layer.width * scale).toInt().coerceAtLeast(0)
val h = (layer.height * scale).toInt().coerceAtLeast(0)

renderTarget = makeMetalRenderTarget(w, h, 0)

surface = Surface.makeFromBackendRenderTarget(
context!!,
renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.getSRGB()
)

canvas = surface!!.canvas
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.jetbrains.skiko.context

import org.jetbrains.skija.ColorSpace
import org.jetbrains.skija.FramebufferFormat
import org.jetbrains.skija.Picture
import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.makeGLContext
import org.jetbrains.skiko.makeGLRenderTarget
import org.jetbrains.skiko.OpenGLApi

internal class OpenGLContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
override fun initContext(): Boolean {
try {
if (context == null) {
context = makeGLContext()
}
} catch (e: Exception) {
println("Failed to create Skia OpenGL context!")
return false
}
return true
}

override fun initCanvas() {
dispose()

val scale = layer.contentScale
val w = (layer.width * scale).toInt().coerceAtLeast(0)
val h = (layer.height * scale).toInt().coerceAtLeast(0)

val gl = OpenGLApi.instance
val fbId = gl.glGetIntegerv(gl.GL_DRAW_FRAMEBUFFER_BINDING)
renderTarget = makeGLRenderTarget(
w,
h,
0,
8,
fbId,
FramebufferFormat.GR_GL_RGBA8
)

surface = Surface.makeFromBackendRenderTarget(
context!!,
renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.getSRGB()
)

canvas = surface!!.canvas
}
}

0 comments on commit 6ad0a4e

Please sign in to comment.