Unverified Commit 4abc90f9 authored by Igor Demin's avatar Igor Demin Committed by GitHub

Don't draw if window is invisible (JFrame.isVisible = false) (#149)

Partially fix https://github.com/JetBrains/skiko/issues/137
parent 57f3f868
...@@ -30,13 +30,13 @@ open class SkiaLayer( ...@@ -30,13 +30,13 @@ open class SkiaLayer(
ContentScale, ContentScale,
} }
internal val backedLayer : HardwareLayer internal val backedLayer: HardwareLayer
val canvas: java.awt.Canvas val canvas: java.awt.Canvas
get() = backedLayer get() = backedLayer
init { init {
setOpaque(false) isOpaque = false
layout = null layout = null
backedLayer = object : HardwareLayer() { backedLayer = object : HardwareLayer() {
override fun paint(g: Graphics) { override fun paint(g: Graphics) {
...@@ -54,9 +54,12 @@ open class SkiaLayer( ...@@ -54,9 +54,12 @@ open class SkiaLayer(
return this@SkiaLayer.inputMethodRequests return this@SkiaLayer.inputMethodRequests
} }
} }
add(backedLayer)
@Suppress("LeakingThis") @Suppress("LeakingThis")
add(backedLayer)
backedLayer.addHierarchyListener { backedLayer.addHierarchyListener {
if (it.changeFlags and HierarchyEvent.SHOWING_CHANGED.toLong() != 0L) {
checkShowing()
}
if (it.changeFlags and HierarchyEvent.DISPLAYABILITY_CHANGED.toLong() != 0L) { if (it.changeFlags and HierarchyEvent.DISPLAYABILITY_CHANGED.toLong() != 0L) {
checkInit() checkInit()
} }
...@@ -69,10 +72,24 @@ open class SkiaLayer( ...@@ -69,10 +72,24 @@ open class SkiaLayer(
private fun checkInit() { private fun checkInit() {
if (!isInited && isDisplayable) { if (!isInited && isDisplayable) {
backedLayer.defineContentScale() backedLayer.defineContentScale()
checkShowing()
init() init()
} }
} }
private fun checkShowing() {
isShowingCached = super.isShowing()
if (isShowing) {
repaint()
}
}
private var isShowingCached = false
override fun isShowing(): Boolean {
return isShowingCached
}
val contentScale: Float val contentScale: Float
get() = backedLayer.contentScale get() = backedLayer.contentScale
...@@ -84,7 +101,9 @@ open class SkiaLayer( ...@@ -84,7 +101,9 @@ open class SkiaLayer(
var fullscreen: Boolean var fullscreen: Boolean
get() = backedLayer.fullscreen get() = backedLayer.fullscreen
set(value) { backedLayer.fullscreen = value } set(value) {
backedLayer.fullscreen = value
}
var renderer: SkiaRenderer? = null var renderer: SkiaRenderer? = null
val clipComponents = mutableListOf<ClipRectangle>() val clipComponents = mutableListOf<ClipRectangle>()
...@@ -117,7 +136,7 @@ open class SkiaLayer( ...@@ -117,7 +136,7 @@ open class SkiaLayer(
do { do {
thrown = false thrown = false
try { try {
renderApi = fallbackRenderApiQueue.removeAt(0) renderApi = fallbackRenderApiQueue.removeAt(0)
contextHandler?.dispose() contextHandler?.dispose()
redrawer?.dispose() redrawer?.dispose()
contextHandler = createContextHandler(this, renderApi) contextHandler = createContextHandler(this, renderApi)
...@@ -137,10 +156,10 @@ open class SkiaLayer( ...@@ -137,10 +156,10 @@ open class SkiaLayer(
} }
private val stateHandlers = private val stateHandlers =
mutableMapOf<PropertyKind, MutableList<(SkiaLayer) -> Unit>>() mutableMapOf<PropertyKind, MutableList<(SkiaLayer) -> Unit>>()
fun onStateChanged(kind: PropertyKind, handler: (SkiaLayer) -> Unit) { fun onStateChanged(kind: PropertyKind, handler: (SkiaLayer) -> Unit) {
stateHandlers.getOrPut( kind, { mutableListOf() }) += handler stateHandlers.getOrPut(kind, { mutableListOf() }) += handler
} }
private fun notifyChange(kind: PropertyKind) { private fun notifyChange(kind: PropertyKind) {
...@@ -386,5 +405,5 @@ open class SkiaLayer( ...@@ -386,5 +405,5 @@ open class SkiaLayer(
rounded = value.toFloat() rounded = value.toFloat()
} }
return rounded.toInt() return rounded.toInt()
} }
} }
...@@ -17,8 +17,10 @@ internal class AngleRedrawer( ...@@ -17,8 +17,10 @@ internal class AngleRedrawer(
private var device: Long = 0 private var device: Long = 0
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
update(System.nanoTime()) if (layer.isShowing) {
draw() update(System.nanoTime())
draw()
}
} }
override fun dispose() { override fun dispose() {
......
...@@ -25,8 +25,10 @@ internal class Direct3DRedrawer( ...@@ -25,8 +25,10 @@ internal class Direct3DRedrawer(
} }
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
update(System.nanoTime()) if (layer.isShowing) {
draw() update(System.nanoTime())
draw()
}
} }
override fun dispose() = synchronized(drawLock) { override fun dispose() = synchronized(drawLock) {
......
...@@ -83,11 +83,14 @@ internal class LinuxOpenGLRedrawer( ...@@ -83,11 +83,14 @@ internal class LinuxOpenGLRedrawer(
companion object { companion object {
private val toRedraw = mutableSetOf<LinuxOpenGLRedrawer>() private val toRedraw = mutableSetOf<LinuxOpenGLRedrawer>()
private val toRedrawCopy = mutableSetOf<LinuxOpenGLRedrawer>() private val toRedrawCopy = mutableSetOf<LinuxOpenGLRedrawer>()
private val toRedrawAlive = toRedrawCopy.asSequence().filterNot(LinuxOpenGLRedrawer::isDisposed) private val toRedrawVisible = toRedrawCopy
.asSequence()
.filterNot(LinuxOpenGLRedrawer::isDisposed)
.filter { it.layer.isShowing }
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
// we should wait for the window with the maximum frame limit to avoid bottleneck when there is a window on a slower monitor // we should wait for the window with the maximum frame limit to avoid bottleneck when there is a window on a slower monitor
toRedrawAlive.maxByOrNull { it.frameLimit }?.limitFramesIfNeeded() toRedrawVisible.maxByOrNull { it.frameLimit }?.limitFramesIfNeeded()
toRedrawCopy.clear() toRedrawCopy.clear()
toRedrawCopy.addAll(toRedraw) toRedrawCopy.addAll(toRedraw)
...@@ -95,7 +98,7 @@ internal class LinuxOpenGLRedrawer( ...@@ -95,7 +98,7 @@ internal class LinuxOpenGLRedrawer(
val nanoTime = System.nanoTime() val nanoTime = System.nanoTime()
for (redrawer in toRedrawAlive) { for (redrawer in toRedrawVisible) {
try { try {
redrawer.update(nanoTime) redrawer.update(nanoTime)
} catch (e: CancellationException) { } catch (e: CancellationException) {
...@@ -103,9 +106,9 @@ internal class LinuxOpenGLRedrawer( ...@@ -103,9 +106,9 @@ internal class LinuxOpenGLRedrawer(
} }
} }
val drawingSurfaces = toRedrawAlive.associateWith { lockLinuxDrawingSurface(it.layer.backedLayer) } val drawingSurfaces = toRedrawVisible.associateWith { lockLinuxDrawingSurface(it.layer.backedLayer) }
try { try {
toRedrawAlive.forEach { redrawer -> toRedrawVisible.forEach { redrawer ->
drawingSurfaces[redrawer]!!.makeCurrent(redrawer.context) drawingSurfaces[redrawer]!!.makeCurrent(redrawer.context)
redrawer.draw() redrawer.draw()
} }
...@@ -113,11 +116,11 @@ internal class LinuxOpenGLRedrawer( ...@@ -113,11 +116,11 @@ internal class LinuxOpenGLRedrawer(
// TODO(demin) it seems now vsync doesn't work as expected with two windows (we have fps = refreshRate / windowCount) // TODO(demin) it seems now vsync doesn't work as expected with two windows (we have fps = refreshRate / windowCount)
// perhaps we should create frameDispatcher for each display. // perhaps we should create frameDispatcher for each display.
// Don't know what happened, but on 620547a commit everything was okay. maybe something changed in the code, maybe my system changed // Don't know what happened, but on 620547a commit everything was okay. maybe something changed in the code, maybe my system changed
toRedrawAlive.forEach { redrawer -> toRedrawVisible.forEach { redrawer ->
drawingSurfaces[redrawer]!!.swapBuffers() drawingSurfaces[redrawer]!!.swapBuffers()
} }
toRedrawAlive.forEach { redrawer -> toRedrawVisible.forEach { redrawer ->
drawingSurfaces[redrawer]!!.makeCurrent(redrawer.context) drawingSurfaces[redrawer]!!.makeCurrent(redrawer.context)
OpenGLApi.instance.glFinish() OpenGLApi.instance.glFinish()
} }
......
...@@ -9,7 +9,6 @@ import org.jetbrains.skia.DirectContext ...@@ -9,7 +9,6 @@ import org.jetbrains.skia.DirectContext
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import javax.swing.SwingUtilities.convertPoint import javax.swing.SwingUtilities.convertPoint
import javax.swing.SwingUtilities.getRootPane import javax.swing.SwingUtilities.getRootPane
import kotlin.time.ExperimentalTime
internal class MetalRedrawer( internal class MetalRedrawer(
private val layer: SkiaLayer, private val layer: SkiaLayer,
...@@ -32,8 +31,10 @@ internal class MetalRedrawer( ...@@ -32,8 +31,10 @@ internal class MetalRedrawer(
} }
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
update(System.nanoTime()) if (layer.isShowing) {
draw() update(System.nanoTime())
draw()
}
} }
override fun dispose() = synchronized(drawLock) { override fun dispose() = synchronized(drawLock) {
......
...@@ -19,9 +19,11 @@ internal class SoftwareRedrawer( ...@@ -19,9 +19,11 @@ internal class SoftwareRedrawer(
frameLimiter.awaitNextFrame() frameLimiter.awaitNextFrame()
} }
layer.update(System.nanoTime()) if (layer.isShowing) {
if (layer.prepareDrawContext()) { layer.update(System.nanoTime())
layer.draw() if (layer.prepareDrawContext()) {
layer.draw()
}
} }
} }
......
...@@ -65,7 +65,10 @@ internal class WindowsOpenGLRedrawer( ...@@ -65,7 +65,10 @@ internal class WindowsOpenGLRedrawer(
companion object { companion object {
private val toRedraw = mutableSetOf<WindowsOpenGLRedrawer>() private val toRedraw = mutableSetOf<WindowsOpenGLRedrawer>()
private val toRedrawCopy = mutableSetOf<WindowsOpenGLRedrawer>() private val toRedrawCopy = mutableSetOf<WindowsOpenGLRedrawer>()
private val toRedrawAlive = toRedrawCopy.asSequence().filterNot(WindowsOpenGLRedrawer::isDisposed) private val toRedrawVisible = toRedrawCopy
.asSequence()
.filterNot(WindowsOpenGLRedrawer::isDisposed)
.filter { it.layer.isShowing }
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) { private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
toRedrawCopy.clear() toRedrawCopy.clear()
...@@ -74,7 +77,7 @@ internal class WindowsOpenGLRedrawer( ...@@ -74,7 +77,7 @@ internal class WindowsOpenGLRedrawer(
val nanoTime = System.nanoTime() val nanoTime = System.nanoTime()
for (redrawer in toRedrawAlive) { for (redrawer in toRedrawVisible) {
try { try {
redrawer.update(nanoTime) redrawer.update(nanoTime)
} catch (e: CancellationException) { } catch (e: CancellationException) {
...@@ -82,21 +85,21 @@ internal class WindowsOpenGLRedrawer( ...@@ -82,21 +85,21 @@ internal class WindowsOpenGLRedrawer(
} }
} }
for (redrawer in toRedrawAlive) { for (redrawer in toRedrawVisible) {
redrawer.makeCurrent() redrawer.makeCurrent()
redrawer.draw() redrawer.draw()
} }
for (redrawer in toRedrawAlive) { for (redrawer in toRedrawVisible) {
redrawer.swapBuffers() redrawer.swapBuffers()
} }
for (redrawer in toRedrawAlive) { for (redrawer in toRedrawVisible) {
redrawer.makeCurrent() redrawer.makeCurrent()
OpenGLApi.instance.glFinish() OpenGLApi.instance.glFinish()
} }
val isVsyncEnabled = toRedrawAlive.all { it.properties.isVsyncEnabled } val isVsyncEnabled = toRedrawVisible.all { it.properties.isVsyncEnabled }
if (isVsyncEnabled) { if (isVsyncEnabled) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
dwmFlush() // wait for vsync dwmFlush() // wait for vsync
......
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