Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
skiko
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liuqiang
skiko
Commits
afcd9e54
Unverified
Commit
afcd9e54
authored
Oct 12, 2025
by
Alexander Maryanovsky
Committed by
GitHub
Oct 12, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix drawing issues and add API to request render-and-draw explicitly (#1104)
parent
2044a13b
Changes
25
Show whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
187 additions
and
127 deletions
+187
-127
Actuals.android.kt
...androidMain/kotlin/org/jetbrains/skiko/Actuals.android.kt
+2
-2
SkiaLayer.android.kt
...droidMain/kotlin/org/jetbrains/skiko/SkiaLayer.android.kt
+4
-2
SkiaLayer.awt.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
+26
-19
AbstractDirectSoftwareRedrawer.kt
...etbrains/skiko/redrawer/AbstractDirectSoftwareRedrawer.kt
+3
-5
AngleRedrawer.kt
...Main/kotlin/org/jetbrains/skiko/redrawer/AngleRedrawer.kt
+3
-5
Direct3DRedrawer.kt
...n/kotlin/org/jetbrains/skiko/redrawer/Direct3DRedrawer.kt
+3
-5
LinuxOpenGLRedrawer.kt
...otlin/org/jetbrains/skiko/redrawer/LinuxOpenGLRedrawer.kt
+3
-5
LinuxSoftwareRedrawer.kt
...lin/org/jetbrains/skiko/redrawer/LinuxSoftwareRedrawer.kt
+2
-2
MetalRedrawer.kt
...Main/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt
+12
-15
SoftwareRedrawer.kt
...n/kotlin/org/jetbrains/skiko/redrawer/SoftwareRedrawer.kt
+3
-5
WindowsOpenGLRedrawer.kt
...lin/org/jetbrains/skiko/redrawer/WindowsOpenGLRedrawer.kt
+3
-5
SkiaLayerPerformanceTest.kt
...st/kotlin/org/jetbrains/skiko/SkiaLayerPerformanceTest.kt
+1
-1
SkiaLayerTest.kt
...o/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
+85
-36
SkiaLayer.kt
skiko/src/commonMain/kotlin/org/jetbrains/skiko/SkiaLayer.kt
+7
-1
SkikoRenderDelegate.kt
...monMain/kotlin/org/jetbrains/skiko/SkikoRenderDelegate.kt
+1
-1
Redrawer.kt
...ommonMain/kotlin/org/jetbrains/skiko/redrawer/Redrawer.kt
+2
-2
SkikoProperties.kt
...src/jvmMain/kotlin/org/jetbrains/skiko/SkikoProperties.kt
+4
-0
SkiaLayer.linux.kt
...c/linuxMain/kotlin/org/jetbrains/skiko/SkiaLayer.linux.kt
+2
-1
SkiaLayer.macos.kt
...c/macosMain/kotlin/org/jetbrains/skiko/SkiaLayer.macos.kt
+7
-5
MetalRedrawer.macos.kt
...otlin/org/jetbrains/skiko/redrawer/MetalRedrawer.macos.kt
+3
-3
OpenGLRedrawer.macos.kt
...tlin/org/jetbrains/skiko/redrawer/OpenGLRedrawer.macos.kt
+3
-3
SkiaLayer.uikit.kt
...c/uikitMain/kotlin/org/jetbrains/skiko/SkiaLayer.uikit.kt
+3
-1
SkikoUIView.kt
...o/src/uikitMain/kotlin/org/jetbrains/skiko/SkikoUIView.kt
+1
-1
MetalRedrawer.uikit.kt
...otlin/org/jetbrains/skiko/redrawer/MetalRedrawer.uikit.kt
+1
-1
SkiaLayer.js.kt
skiko/src/webMain/kotlin/org/jetbrains/skiko/SkiaLayer.js.kt
+3
-1
No files found.
skiko/src/androidMain/kotlin/org/jetbrains/skiko/Actuals.android.kt
View file @
afcd9e54
...
@@ -15,8 +15,8 @@ internal class AndroidOpenGLRedrawer(
...
@@ -15,8 +15,8 @@ internal class AndroidOpenGLRedrawer(
private
val
properties
:
SkiaLayerProperties
private
val
properties
:
SkiaLayerProperties
)
:
Redrawer
{
)
:
Redrawer
{
override
fun
dispose
()
=
TODO
()
override
fun
dispose
()
=
TODO
()
override
fun
needRe
draw
(
canUpdateImmediately
:
Boolean
)
=
TODO
()
override
fun
needRe
nder
(
canUpdateImmediately
:
Boolean
)
=
TODO
()
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
TODO
()
override
fun
re
nderImmediately
(
)
=
TODO
()
override
fun
update
(
nanoTime
:
Long
)
=
TODO
()
override
fun
update
(
nanoTime
:
Long
)
=
TODO
()
override
val
renderInfo
:
String
get
()
=
"Android renderer"
override
val
renderInfo
:
String
get
()
=
"Android renderer"
...
...
skiko/src/androidMain/kotlin/org/jetbrains/skiko/SkiaLayer.android.kt
View file @
afcd9e54
...
@@ -47,7 +47,7 @@ actual open class SkiaLayer {
...
@@ -47,7 +47,7 @@ actual open class SkiaLayer {
view
.
setFocusableInTouchMode
(
true
)
view
.
setFocusableInTouchMode
(
true
)
needRe
draw
()
needRe
nder
()
}
}
actual
fun
detach
()
{
actual
fun
detach
()
{
...
@@ -57,12 +57,14 @@ actual open class SkiaLayer {
...
@@ -57,12 +57,14 @@ actual open class SkiaLayer {
}
}
}
}
actual
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
glView
?.
apply
{
glView
?.
apply
{
scheduleFrame
()
scheduleFrame
()
}
}
}
}
actual
fun
needRedraw
()
=
needRender
()
actual
val
pixelGeometry
:
PixelGeometry
actual
val
pixelGeometry
:
PixelGeometry
get
()
=
PixelGeometry
.
UNKNOWN
get
()
=
PixelGeometry
.
UNKNOWN
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
View file @
afcd9e54
...
@@ -113,8 +113,8 @@ actual open class SkiaLayer internal constructor(
...
@@ -113,8 +113,8 @@ actual open class SkiaLayer internal constructor(
// 2. HardwareLayer.paint is also not always called.
// 2. HardwareLayer.paint is also not always called.
// For example, on macOs when we resize window or change DPI
// For example, on macOs when we resize window or change DPI
//
//
// 3. to avoid double paint in one single frame, use needRe
draw instead of redraw
Immediately
// 3. to avoid double paint in one single frame, use needRe
nder instead of render
Immediately
redrawer
?.
needRe
draw
(
throttledToVsync
=
false
)
redrawer
?.
needRe
nder
(
throttledToVsync
=
false
)
}
}
@Suppress
(
"OVERRIDE_DEPRECATION"
)
@Suppress
(
"OVERRIDE_DEPRECATION"
)
...
@@ -124,7 +124,7 @@ actual open class SkiaLayer internal constructor(
...
@@ -124,7 +124,7 @@ actual open class SkiaLayer internal constructor(
super
.
reshape
(
x
,
y
,
width
,
height
)
super
.
reshape
(
x
,
y
,
width
,
height
)
redrawer
?.
syncBounds
()
redrawer
?.
syncBounds
()
redrawer
?.
needRe
draw
(
throttledToVsync
=
false
)
redrawer
?.
needRe
nder
(
throttledToVsync
=
false
)
}
}
override
fun
getInputMethodRequests
():
InputMethodRequests
?
{
override
fun
getInputMethodRequests
():
InputMethodRequests
?
{
...
@@ -384,7 +384,7 @@ actual open class SkiaLayer internal constructor(
...
@@ -384,7 +384,7 @@ actual open class SkiaLayer internal constructor(
@Suppress
(
"DEPRECATION"
)
@Suppress
(
"DEPRECATION"
)
super
.
reshape
(
x
,
y
,
w
,
h
)
super
.
reshape
(
x
,
y
,
w
,
h
)
// Calling re
draw
Immediately as early as possible improves the situation with
// Calling re
nder
Immediately as early as possible improves the situation with
// the visual glitch when the drawn content is scaled during window resize.
// the visual glitch when the drawn content is scaled during window resize.
// Note, however, that this actually causes the reverse glitch (content appears
// Note, however, that this actually causes the reverse glitch (content appears
// scaled in the other direction from the window size), but this seems to
// scaled in the other direction from the window size), but this seems to
...
@@ -394,7 +394,7 @@ actual open class SkiaLayer internal constructor(
...
@@ -394,7 +394,7 @@ actual open class SkiaLayer internal constructor(
// so apply this fix only for the Direct3D case.
// so apply this fix only for the Direct3D case.
if
(
renderApi
==
GraphicsApi
.
DIRECT3D
&&
isShowing
)
{
if
(
renderApi
==
GraphicsApi
.
DIRECT3D
&&
isShowing
)
{
redrawer
?.
syncBounds
()
redrawer
?.
syncBounds
()
redrawer
?.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
?.
re
nderImmediately
(
)
}
}
// Setting the bounds of children should be done only in the layout pass,
// Setting the bounds of children should be done only in the layout pass,
...
@@ -416,15 +416,14 @@ actual open class SkiaLayer internal constructor(
...
@@ -416,15 +416,14 @@ actual open class SkiaLayer internal constructor(
override
fun
paint
(
g
:
Graphics
)
{
override
fun
paint
(
g
:
Graphics
)
{
Logger
.
debug
{
"paint called on SkiaLayer $this"
}
Logger
.
debug
{
"paint called on SkiaLayer $this"
}
val
updateNeeded
=
checkContentScale
()
checkContentScale
()
redrawer
?.
redrawImmediately
(
updateNeeded
=
updateNeeded
)
redrawer
?.
needRender
(
throttledToVsync
=
false
)
}
}
// Workaround for JBR-5274 and JBR-5305
// Workaround for JBR-5274 and JBR-5305
fun
checkContentScale
()
:
Boolean
{
fun
checkContentScale
()
{
val
currentGraphicsContextScaleTransform
=
graphicsConfiguration
.
defaultTransform
val
currentGraphicsContextScaleTransform
=
graphicsConfiguration
.
defaultTransform
return
(
currentGraphicsContextScaleTransform
!=
latestReceivedGraphicsContextScaleTransform
).
also
{
if
(
currentGraphicsContextScaleTransform
!=
latestReceivedGraphicsContextScaleTransform
)
{
if
(
it
)
{
firePropertyChange
(
firePropertyChange
(
"graphicsContextScaleTransform"
,
"graphicsContextScaleTransform"
,
latestReceivedGraphicsContextScaleTransform
,
latestReceivedGraphicsContextScaleTransform
,
...
@@ -432,6 +431,12 @@ actual open class SkiaLayer internal constructor(
...
@@ -432,6 +431,12 @@ actual open class SkiaLayer internal constructor(
)
)
}
}
}
}
/**
* Updates the layer and redraws synchronously.
*/
fun
updateAndDrawImmediately
()
{
redrawer
?.
renderImmediately
()
}
}
// We need to delegate all event listeners to the Canvas (so and focus/input)
// We need to delegate all event listeners to the Canvas (so and focus/input)
...
@@ -546,12 +551,14 @@ actual open class SkiaLayer internal constructor(
...
@@ -546,12 +551,14 @@ actual open class SkiaLayer internal constructor(
/**
/**
* Redraw on the next animation Frame (on vsync signal if vsync is enabled).
* Redraw on the next animation Frame (on vsync signal if vsync is enabled).
*/
*/
actual
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
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"
}
redrawer
?.
needRe
draw
(
throttledToVsync
)
redrawer
?.
needRe
nder
(
throttledToVsync
)
}
}
actual
fun
needRedraw
()
=
needRender
()
internal
fun
update
(
nanoTime
:
Long
)
{
internal
fun
update
(
nanoTime
:
Long
)
{
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"
}
...
@@ -608,7 +615,7 @@ actual open class SkiaLayer internal constructor(
...
@@ -608,7 +615,7 @@ actual open class SkiaLayer internal constructor(
if
(!
isDisposed
)
{
if
(!
isDisposed
)
{
Logger
.
warn
(
e
)
{
"Exception in draw scope"
}
Logger
.
warn
(
e
)
{
"Exception in draw scope"
}
redrawerManager
.
findNextWorkingRenderApi
()
redrawerManager
.
findNextWorkingRenderApi
()
redrawer
?.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
?.
re
nderImmediately
(
)
}
}
}
}
}
}
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/AbstractDirectSoftwareRedrawer.kt
View file @
afcd9e54
...
@@ -29,16 +29,14 @@ internal abstract class AbstractDirectSoftwareRedrawer(
...
@@ -29,16 +29,14 @@ internal abstract class AbstractDirectSoftwareRedrawer(
protected
var
device
=
0L
protected
var
device
=
0L
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
protected
open
fun
draw
()
=
inDrawScope
(
contextHandler
::
draw
)
protected
open
fun
draw
()
=
inDrawScope
(
contextHandler
::
draw
)
override
fun
redrawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
renderImmediately
()
{
if
(
updateNeeded
)
{
update
()
update
()
}
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
draw
()
draw
()
}
}
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/AngleRedrawer.kt
View file @
afcd9e54
...
@@ -59,16 +59,14 @@ internal class AngleRedrawer(
...
@@ -59,16 +59,14 @@ internal class AngleRedrawer(
super
.
dispose
()
super
.
dispose
()
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
checkDisposed
()
checkDisposed
()
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
checkDisposed
()
checkDisposed
()
if
(
updateNeeded
)
{
update
()
update
()
}
inDrawScope
{
inDrawScope
{
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
drawAndSwap
(
withVsync
=
SkikoProperties
.
windowsWaitForVsyncOnRedrawImmediately
)
drawAndSwap
(
withVsync
=
SkikoProperties
.
windowsWaitForVsyncOnRedrawImmediately
)
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/Direct3DRedrawer.kt
View file @
afcd9e54
...
@@ -63,16 +63,14 @@ internal class Direct3DRedrawer(
...
@@ -63,16 +63,14 @@ internal class Direct3DRedrawer(
super
.
dispose
()
super
.
dispose
()
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
checkDisposed
()
checkDisposed
()
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
checkDisposed
()
checkDisposed
()
if
(
updateNeeded
)
{
update
()
update
()
}
inDrawScope
{
inDrawScope
{
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
drawAndSwap
(
withVsync
=
SkikoProperties
.
windowsWaitForVsyncOnRedrawImmediately
)
drawAndSwap
(
withVsync
=
SkikoProperties
.
windowsWaitForVsyncOnRedrawImmediately
)
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/LinuxOpenGLRedrawer.kt
View file @
afcd9e54
...
@@ -72,17 +72,15 @@ internal class LinuxOpenGLRedrawer(
...
@@ -72,17 +72,15 @@ internal class LinuxOpenGLRedrawer(
super
.
dispose
()
super
.
dispose
()
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
checkDisposed
()
checkDisposed
()
toRedraw
.
add
(
this
)
toRedraw
.
add
(
this
)
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
layer
.
backedLayer
.
lockLinuxDrawingSurface
{
override
fun
re
nderImmediately
(
)
=
layer
.
backedLayer
.
lockLinuxDrawingSurface
{
checkDisposed
()
checkDisposed
()
if
(
updateNeeded
)
{
update
()
update
()
}
inDrawScope
{
inDrawScope
{
it
.
makeCurrent
(
context
)
it
.
makeCurrent
(
context
)
contextHandler
.
draw
()
contextHandler
.
draw
()
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/LinuxSoftwareRedrawer.kt
View file @
afcd9e54
...
@@ -31,8 +31,8 @@ internal class LinuxSoftwareRedrawer(
...
@@ -31,8 +31,8 @@ internal class LinuxSoftwareRedrawer(
super
.
draw
()
super
.
draw
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
layer
.
backedLayer
.
lockLinuxDrawingSurface
{
override
fun
re
nderImmediately
(
)
=
layer
.
backedLayer
.
lockLinuxDrawingSurface
{
super
.
re
drawImmediately
(
updateNeeded
)
super
.
re
nderImmediately
(
)
}
}
override
fun
resize
(
width
:
Int
,
height
:
Int
)
=
layer
.
backedLayer
.
lockLinuxDrawingSurface
{
override
fun
resize
(
width
:
Int
,
height
:
Int
)
=
layer
.
backedLayer
.
lockLinuxDrawingSurface
{
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.kt
View file @
afcd9e54
...
@@ -94,26 +94,23 @@ internal class MetalRedrawer(
...
@@ -94,26 +94,23 @@ internal class MetalRedrawer(
super
.
dispose
()
super
.
dispose
()
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
checkDisposed
()
checkDisposed
()
frameDispatcher
.
scheduleFrame
(
needUpdate
=
true
,
throttledToVsync
=
throttledToVsync
)
frameDispatcher
.
scheduleFrame
(
needUpdate
=
true
,
throttledToVsync
=
throttledToVsync
)
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
checkDisposed
()
checkDisposed
()
if
(
updateNeeded
)
{
update
()
update
()
}
// Trying to draw immediately in Metal will result in lost (undrawn)
// frames if there are more than two between consecutive vsync events.
if
(
layer
.
isShowing
)
{
frameDispatcher
.
scheduleFrame
(
needUpdate
=
false
,
throttledToVsync
=
false
)
}
else
{
// But if the layer isn't showing yet, we want to draw immediately,
// so that if it shows before the next vsync, there is no background flash
inDrawScope
{
inDrawScope
{
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
performDraw
()
performDraw
()
// Trying to draw immediately in Metal will result in lost (undrawn)
// frames if there are more than two between consecutive vsync events.
if
(
SkikoProperties
.
macOSWaitForPreviousFrameVsyncOnRedrawImmediately
)
{
runBlocking
{
vSyncer
?.
waitForVSync
()
}
}
}
}
}
}
}
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/SoftwareRedrawer.kt
View file @
afcd9e54
...
@@ -42,15 +42,13 @@ internal class SoftwareRedrawer(
...
@@ -42,15 +42,13 @@ internal class SoftwareRedrawer(
super
.
dispose
()
super
.
dispose
()
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
checkDisposed
()
checkDisposed
()
if
(
updateNeeded
)
{
update
()
update
()
}
inDrawScope
{
inDrawScope
{
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
contextHandler
.
draw
()
contextHandler
.
draw
()
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/redrawer/WindowsOpenGLRedrawer.kt
View file @
afcd9e54
...
@@ -55,17 +55,15 @@ internal class WindowsOpenGLRedrawer(
...
@@ -55,17 +55,15 @@ internal class WindowsOpenGLRedrawer(
super
.
dispose
()
super
.
dispose
()
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
check
(!
isDisposed
)
{
"WindowsOpenGLRedrawer is disposed"
}
check
(!
isDisposed
)
{
"WindowsOpenGLRedrawer is disposed"
}
toRedraw
.
add
(
this
)
toRedraw
.
add
(
this
)
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
check
(!
isDisposed
)
{
"WindowsOpenGLRedrawer is disposed"
}
check
(!
isDisposed
)
{
"WindowsOpenGLRedrawer is disposed"
}
if
(
updateNeeded
)
{
update
()
update
()
}
inDrawScope
{
inDrawScope
{
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
makeCurrent
()
makeCurrent
()
...
...
skiko/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerPerformanceTest.kt
View file @
afcd9e54
...
@@ -59,7 +59,7 @@ class SkiaLayerPerformanceTest {
...
@@ -59,7 +59,7 @@ class SkiaLayerPerformanceTest {
if
(
canCollect
&&
frameTimes
.
size
<
frameCount
)
{
if
(
canCollect
&&
frameTimes
.
size
<
frameCount
)
{
frameTimes
.
add
(
System
.
nanoTime
())
// we check the real time, not the time provided by the argument
frameTimes
.
add
(
System
.
nanoTime
())
// we check the real time, not the time provided by the argument
}
}
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
}
}
}
}
window
.
isUndecorated
=
true
window
.
isUndecorated
=
true
...
...
skiko/src/awtTest/kotlin/org/jetbrains/skiko/SkiaLayerTest.kt
View file @
afcd9e54
...
@@ -106,11 +106,11 @@ class SkiaLayerTest {
...
@@ -106,11 +106,11 @@ class SkiaLayerTest {
override
fun
keyTyped
(
e
:
KeyEvent
?)
{
override
fun
keyTyped
(
e
:
KeyEvent
?)
{
launch
{
launch
{
val
redrawer
=
window
.
layer
.
redrawer
as
MetalRedrawer
val
redrawer
=
window
.
layer
.
redrawer
as
MetalRedrawer
redrawer
.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
.
re
nderImmediately
(
)
counter1
+=
1
counter1
+=
1
redrawer
.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
.
re
nderImmediately
(
)
counter2
+=
1
counter2
+=
1
redrawer
.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
.
re
nderImmediately
(
)
}
}
}
}
})
})
...
@@ -179,7 +179,7 @@ class SkiaLayerTest {
...
@@ -179,7 +179,7 @@ class SkiaLayerTest {
screenshots
.
assert
(
window
.
bounds
,
"frame1"
)
screenshots
.
assert
(
window
.
bounds
,
"frame1"
)
app
.
rectWidth
=
100
app
.
rectWidth
=
100
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
delay
(
1000
)
delay
(
1000
)
screenshots
.
assert
(
window
.
bounds
,
"frame2"
)
screenshots
.
assert
(
window
.
bounds
,
"frame2"
)
...
@@ -238,7 +238,7 @@ class SkiaLayerTest {
...
@@ -238,7 +238,7 @@ class SkiaLayerTest {
screenshots
.
assert
(
window
.
bounds
,
"frame1"
)
screenshots
.
assert
(
window
.
bounds
,
"frame1"
)
app
.
rectWidth
=
100
app
.
rectWidth
=
100
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
delay
(
1000
)
delay
(
1000
)
screenshots
.
assert
(
window
.
bounds
,
"frame2"
)
screenshots
.
assert
(
window
.
bounds
,
"frame2"
)
...
@@ -271,12 +271,12 @@ class SkiaLayerTest {
...
@@ -271,12 +271,12 @@ class SkiaLayerTest {
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
window
.
isUndecorated
=
true
window
.
isUndecorated
=
true
window
.
isVisible
=
true
window
.
isVisible
=
true
layer
.
needRe
draw
()
layer
.
needRe
nder
()
delay
(
1000
)
delay
(
1000
)
assertEquals
(
0
,
renderedWidth
)
assertEquals
(
0
,
renderedWidth
)
renderedWidth
=
-
1
renderedWidth
=
-
1
layer
.
needRe
draw
()
layer
.
needRe
nder
()
delay
(
1000
)
delay
(
1000
)
assertEquals
(
0
,
renderedWidth
)
assertEquals
(
0
,
renderedWidth
)
...
@@ -439,7 +439,7 @@ class SkiaLayerTest {
...
@@ -439,7 +439,7 @@ class SkiaLayerTest {
}
}
@Test
@Test
fun
`
should
call
onRender
after
init
,
after
resize
,
and
only
once
after
needRe
draw
`
()
=
uiTest
{
fun
`
should
call
onRender
after
init
,
after
resize
,
and
only
once
after
needRe
nder
`
()
=
uiTest
{
var
renderCount
=
0
var
renderCount
=
0
val
window
=
UiTestWindow
()
val
window
=
UiTestWindow
()
...
@@ -464,7 +464,7 @@ class SkiaLayerTest {
...
@@ -464,7 +464,7 @@ class SkiaLayerTest {
assertTrue
(
renderCount
>
0
)
assertTrue
(
renderCount
>
0
)
renderCount
=
0
renderCount
=
0
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
delay
(
1000
)
delay
(
1000
)
assertEquals
(
1
,
renderCount
)
assertEquals
(
1
,
renderCount
)
}
finally
{
}
finally
{
...
@@ -561,7 +561,7 @@ class SkiaLayerTest {
...
@@ -561,7 +561,7 @@ class SkiaLayerTest {
delay
(
100
)
delay
(
100
)
val
window
=
openWindow
()
val
window
=
openWindow
()
window
.
isVisible
=
true
window
.
isVisible
=
true
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
yield
()
yield
()
window
.
paint
(
window
.
graphics
)
window
.
paint
(
window
.
graphics
)
assertRenderApiFor
(
window
.
layer
)
assertRenderApiFor
(
window
.
layer
)
...
@@ -570,9 +570,12 @@ class SkiaLayerTest {
...
@@ -570,9 +570,12 @@ class SkiaLayerTest {
}
}
private
abstract
class
BaseTestRedrawer
(
val
layer
:
SkiaLayer
):
Redrawer
{
private
abstract
class
BaseTestRedrawer
(
val
layer
:
SkiaLayer
):
Redrawer
{
private
val
frameDispatcher
=
FrameDispatcher
(
MainUIDispatcher
)
{
renderImmediately
()
}
override
fun
dispose
()
=
Unit
override
fun
dispose
()
=
Unit
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
=
Unit
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
=
frameDispatcher
.
scheduleFrame
()
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
Unit
override
fun
re
nderImmediately
(
)
=
Unit
override
fun
update
(
nanoTime
:
Long
)
=
layer
.
update
(
nanoTime
)
override
fun
update
(
nanoTime
:
Long
)
=
layer
.
update
(
nanoTime
)
override
val
renderInfo
:
String
override
val
renderInfo
:
String
...
@@ -587,7 +590,7 @@ class SkiaLayerTest {
...
@@ -587,7 +590,7 @@ class SkiaLayerTest {
override
fun
initContext
()
=
false
override
fun
initContext
()
=
false
override
fun
initCanvas
()
=
Unit
override
fun
initCanvas
()
=
Unit
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
layer
.
inDrawScope
(
contextHandler
::
draw
)
override
fun
re
nderImmediately
(
)
=
layer
.
inDrawScope
(
contextHandler
::
draw
)
}
}
}
}
}
}
...
@@ -601,7 +604,7 @@ class SkiaLayerTest {
...
@@ -601,7 +604,7 @@ class SkiaLayerTest {
fun
`
fallback
to
software
renderer
,
fail
on
draw
`
()
=
uiTest
{
fun
`
fallback
to
software
renderer
,
fail
on
draw
`
()
=
uiTest
{
testFallbackToSoftware
{
layer
,
_
,
_
,
_
->
testFallbackToSoftware
{
layer
,
_
,
_
,
_
->
object
:
BaseTestRedrawer
(
layer
)
{
object
:
BaseTestRedrawer
(
layer
)
{
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
layer
.
inDrawScope
{
override
fun
re
nderImmediately
(
)
=
layer
.
inDrawScope
{
throw
RenderException
()
throw
RenderException
()
}
}
}
}
...
@@ -625,7 +628,7 @@ class SkiaLayerTest {
...
@@ -625,7 +628,7 @@ class SkiaLayerTest {
screenshots
.
assert
(
window
.
bounds
,
"frame1"
,
"testFallbackToSoftware"
)
screenshots
.
assert
(
window
.
bounds
,
"frame1"
,
"testFallbackToSoftware"
)
app
.
rectWidth
=
100
app
.
rectWidth
=
100
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
delay
(
1000
)
delay
(
1000
)
screenshots
.
assert
(
window
.
bounds
,
"frame2"
,
"testFallbackToSoftware"
)
screenshots
.
assert
(
window
.
bounds
,
"frame2"
,
"testFallbackToSoftware"
)
...
@@ -657,7 +660,7 @@ class SkiaLayerTest {
...
@@ -657,7 +660,7 @@ class SkiaLayerTest {
val
window
=
UiTestWindow
(
val
window
=
UiTestWindow
(
renderFactory
=
OverrideNonSoftwareRenderFactory
{
layer
,
_
,
_
,
_
->
renderFactory
=
OverrideNonSoftwareRenderFactory
{
layer
,
_
,
_
,
_
->
object
:
BaseTestRedrawer
(
layer
)
{
object
:
BaseTestRedrawer
(
layer
)
{
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
=
layer
.
inDrawScope
{
override
fun
re
nderImmediately
(
)
=
layer
.
inDrawScope
{
throw
RenderException
()
throw
RenderException
()
}
}
}
}
...
@@ -703,7 +706,7 @@ class SkiaLayerTest {
...
@@ -703,7 +706,7 @@ class SkiaLayerTest {
drawCount
++
drawCount
++
if
(
drawCount
<
targetDrawCount
)
{
if
(
drawCount
<
targetDrawCount
)
{
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
}
else
{
}
else
{
onDrawCompleted
.
complete
(
Unit
)
onDrawCompleted
.
complete
(
Unit
)
}
}
...
@@ -823,7 +826,7 @@ class SkiaLayerTest {
...
@@ -823,7 +826,7 @@ class SkiaLayerTest {
repeat
(
10
)
{
repeat
(
10
)
{
window
.
isVisible
=
true
window
.
isVisible
=
true
delay
(
16
)
delay
(
16
)
window
.
layer
.
needRe
draw
()
window
.
layer
.
needRe
nder
()
delay
(
500
)
delay
(
500
)
window
.
isVisible
=
false
window
.
isVisible
=
false
...
@@ -1145,7 +1148,7 @@ class SkiaLayerTest {
...
@@ -1145,7 +1148,7 @@ class SkiaLayerTest {
}
}
@Test
@Test
fun
`
temporary
change
is
not
visible
`
()
=
uiTest
{
fun
`
temporary
change
is
not
visible
with
needRender
(
throttledToVsync
=
false
)
`
()
=
uiTest
{
assumeTrue
(
hostOs
.
isMacOS
)
assumeTrue
(
hostOs
.
isMacOS
)
// The separation between update and draw is only implemented in MetalRedrawer at the moment
// The separation between update and draw is only implemented in MetalRedrawer at the moment
// Don't use assumeTrue, as uiTest iterates over multiple renderers,
// Don't use assumeTrue, as uiTest iterates over multiple renderers,
...
@@ -1181,18 +1184,19 @@ class SkiaLayerTest {
...
@@ -1181,18 +1184,19 @@ class SkiaLayerTest {
val
robot
=
Robot
()
val
robot
=
Robot
()
var
tempColorVisibleCount
=
0
var
tempColorVisibleCount
=
0
val
testCount
=
50
try
{
try
{
repeat
(
50
)
{
repeat
(
testCount
)
{
// Wait for just after the next vsync, so we have plenty of time until the one after it
// Wait for just after the next vsync, so we have plenty of time until the one after it
val
vSyncer
=
MetalVSyncer
(
window
.
layer
.
windowHandle
)
val
vSyncer
=
MetalVSyncer
(
window
.
layer
.
windowHandle
)
vSyncer
.
waitForVSync
()
vSyncer
.
waitForVSync
()
// Set the color to temp, then immediately back to normal
// Set the color to temp, then immediately back to normal
renderDelegate
.
color
=
tempColor
renderDelegate
.
color
=
tempColor
renderDelegate
.
layer
.
needRe
draw
(
throttledToVsync
=
false
)
renderDelegate
.
layer
.
needRe
nder
(
throttledToVsync
=
false
)
renderChannel
.
receive
()
// Wait until render is actually called
renderChannel
.
receive
()
// Wait until render is actually called
renderDelegate
.
color
=
color
renderDelegate
.
color
=
color
renderDelegate
.
layer
.
needRe
draw
(
throttledToVsync
=
false
)
renderDelegate
.
layer
.
needRe
nder
(
throttledToVsync
=
false
)
// Check whether the temp color was visible
// Check whether the temp color was visible
val
startTime
=
System
.
currentTimeMillis
()
val
startTime
=
System
.
currentTimeMillis
()
...
@@ -1208,15 +1212,15 @@ class SkiaLayerTest {
...
@@ -1208,15 +1212,15 @@ class SkiaLayerTest {
// color is reverted, we allow a small percentage of the tries to fail. This way the flakiness of the test
// color is reverted, we allow a small percentage of the tries to fail. This way the flakiness of the test
// is reduced.
// is reduced.
// Note that in practice, however, this test had never failed on an M1 Ultra machine with a 60Hz monitor.
// Note that in practice, however, this test had never failed on an M1 Ultra machine with a 60Hz monitor.
assertTrue
(
tempColorVisibleCount
<
5
)
assertTrue
(
tempColorVisibleCount
<
5
,
"Temp color was visible $tempColorVisibleCount/$testCount times"
)
}
finally
{
}
finally
{
window
.
dispose
()
window
.
dispose
()
}
}
}
}
@Test
@Test
fun
`
needRe
draw
throttled
and
regular
calls
render
and
draw
once
`
()
=
uiTest
{
fun
`
needRe
nder
throttled
and
regular
calls
render
and
draw
once
`
()
=
uiTest
{
// Check that calling both needRe
draw(true) and needRedraw
(false) causes only one render and one draw call
// Check that calling both needRe
nder(true) and needRender
(false) causes only one render and one draw call
var
renderCalls
=
0
var
renderCalls
=
0
val
renderChannel
=
Channel
<
Unit
>(
Channel
.
CONFLATED
)
val
renderChannel
=
Channel
<
Unit
>(
Channel
.
CONFLATED
)
...
@@ -1259,27 +1263,72 @@ class SkiaLayerTest {
...
@@ -1259,27 +1263,72 @@ class SkiaLayerTest {
renderCalls
=
0
renderCalls
=
0
drawCalls
=
0
drawCalls
=
0
withContext
(
MainUIDispatcher
)
{
withContext
(
MainUIDispatcher
)
{
window
.
layer
.
needRe
draw
(
true
)
window
.
layer
.
needRe
nder
(
true
)
window
.
layer
.
needRe
draw
(
false
)
window
.
layer
.
needRe
nder
(
false
)
}
}
delay
(
100
)
delay
(
100
)
assertEquals
(
"Render was called more than once on needRe
draw(true), needRedraw
(false)"
,
1
,
renderCalls
)
assertEquals
(
"Render was called more than once on needRe
nder(true), needRender
(false)"
,
1
,
renderCalls
)
assertEquals
(
"Draw was called more than once on needRe
draw(true), needRedraw
(false)"
,
1
,
drawCalls
)
assertEquals
(
"Draw was called more than once on needRe
nder(true), needRender
(false)"
,
1
,
drawCalls
)
renderCalls
=
0
renderCalls
=
0
drawCalls
=
0
drawCalls
=
0
withContext
(
MainUIDispatcher
)
{
withContext
(
MainUIDispatcher
)
{
window
.
layer
.
needRe
draw
(
false
)
window
.
layer
.
needRe
nder
(
false
)
window
.
layer
.
needRe
draw
(
true
)
window
.
layer
.
needRe
nder
(
true
)
}
}
delay
(
100
)
delay
(
100
)
assertEquals
(
"Render was called more than once on needRe
draw(false), needRedraw
(true)"
,
1
,
renderCalls
)
assertEquals
(
"Render was called more than once on needRe
nder(false), needRender
(true)"
,
1
,
renderCalls
)
assertEquals
(
"Draw was called more than once on needRe
draw(true), needRedraw
(true)"
,
1
,
drawCalls
)
assertEquals
(
"Draw was called more than once on needRe
nder(true), needRender
(true)"
,
1
,
drawCalls
)
}
finally
{
}
finally
{
window
.
dispose
()
window
.
dispose
()
}
}
}
}
@Test
fun
`
updateAndDrawImmediately
updates
and
draws
synchronously
`
()
=
uiTest
{
// Check that calling both needRender(true) and needRender(false) causes only one render and one draw call
var
renderCalls
=
0
val
renderChannel
=
Channel
<
Unit
>(
Channel
.
CONFLATED
)
var
drawCalls
=
0
val
deviceAnalytics
=
object
:
SkiaLayerAnalytics
.
DeviceAnalytics
{
override
fun
beforeFrameRender
()
{
drawCalls
++
}
}
val
analytics
=
object
:
SkiaLayerAnalytics
{
@ExperimentalSkikoApi
override
fun
device
(
skikoVersion
:
String
,
os
:
OS
,
api
:
GraphicsApi
,
deviceName
:
String
?
):
SkiaLayerAnalytics
.
DeviceAnalytics
{
return
deviceAnalytics
}
}
val
window
=
UiTestWindow
(
analytics
=
analytics
)
{
size
=
Dimension
(
600
,
600
)
location
=
Point
(
400
,
400
)
layer
.
renderDelegate
=
object
:
SkikoRenderDelegate
{
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
renderCalls
++
renderChannel
.
trySend
(
Unit
)
}
}
contentPane
.
add
(
layer
,
BorderLayout
.
CENTER
)
}
window
.
pack
()
val
initRenderCalls
=
renderCalls
val
initDrawCalls
=
drawCalls
window
.
layer
.
updateAndDrawImmediately
()
// Can't check renderCalls == initRenderCalls+1 because if drawing fails, render will be called again with
// the fallback renderer.
assertTrue
(
renderCalls
>
initRenderCalls
)
assertTrue
(
drawCalls
>
initDrawCalls
)
}
private
class
RectRenderer
(
private
class
RectRenderer
(
private
val
getContentScale
:
()
->
Float
,
private
val
getContentScale
:
()
->
Float
,
var
rectWidth
:
Int
,
var
rectWidth
:
Int
,
...
@@ -1341,7 +1390,7 @@ class SkiaLayerTest {
...
@@ -1341,7 +1390,7 @@ class SkiaLayerTest {
color
=
Color
.
RED
.
rgb
color
=
Color
.
RED
.
rgb
})
})
layer
.
needRe
draw
()
layer
.
needRe
nder
()
}
}
}
}
...
@@ -1354,7 +1403,7 @@ class SkiaLayerTest {
...
@@ -1354,7 +1403,7 @@ class SkiaLayerTest {
var
continuousRedraw
=
continuousRedraw
var
continuousRedraw
=
continuousRedraw
set
(
value
)
{
set
(
value
)
{
if
(
value
)
if
(
value
)
layer
.
needRe
draw
(
throttledToVsync
=
true
)
layer
.
needRe
nder
(
throttledToVsync
=
true
)
field
=
value
field
=
value
}
}
...
@@ -1370,7 +1419,7 @@ class SkiaLayerTest {
...
@@ -1370,7 +1419,7 @@ class SkiaLayerTest {
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
canvas
.
drawRect
(
Rect
(
0f
,
0f
,
width
.
toFloat
(),
height
.
toFloat
()),
paint
)
canvas
.
drawRect
(
Rect
(
0f
,
0f
,
width
.
toFloat
(),
height
.
toFloat
()),
paint
)
if
(
continuousRedraw
)
{
if
(
continuousRedraw
)
{
layer
.
needRe
draw
()
layer
.
needRe
nder
()
}
}
}
}
}
}
...
...
skiko/src/commonMain/kotlin/org/jetbrains/skiko/SkiaLayer.kt
View file @
afcd9e54
...
@@ -61,7 +61,13 @@ expect open class SkiaLayer {
...
@@ -61,7 +61,13 @@ expect open class SkiaLayer {
* @param throttledToVsync Whether to throttle calling [renderDelegate]'s [SkikoRenderDelegate.onRender] to at most
* @param throttledToVsync Whether to throttle calling [renderDelegate]'s [SkikoRenderDelegate.onRender] to at most
* once between vsync signals (if vsync is enabled).
* once between vsync signals (if vsync is enabled).
*/
*/
fun
needRedraw
(
throttledToVsync
:
Boolean
=
true
)
fun
needRender
(
throttledToVsync
:
Boolean
=
true
)
@Deprecated
(
"Use needRender(throttledToVsync) instead"
,
replaceWith
=
ReplaceWith
(
"needRender(throttledToVsync)"
)
)
fun
needRedraw
()
// TODO: Remove this sometime after 2026-07
/**
/**
* Drawing function.
* Drawing function.
...
...
skiko/src/commonMain/kotlin/org/jetbrains/skiko/SkikoRenderDelegate.kt
View file @
afcd9e54
...
@@ -15,6 +15,6 @@ open class SkiaLayerRenderDelegate(
...
@@ -15,6 +15,6 @@ open class SkiaLayerRenderDelegate(
canvas
.
scale
(
contentScale
,
contentScale
)
canvas
.
scale
(
contentScale
,
contentScale
)
renderDelegate
.
onRender
(
canvas
,
(
width
/
contentScale
).
toInt
(),
(
height
/
contentScale
).
toInt
(),
nanoTime
)
renderDelegate
.
onRender
(
canvas
,
(
width
/
contentScale
).
toInt
(),
(
height
/
contentScale
).
toInt
(),
nanoTime
)
// Request next frame immediately.
// Request next frame immediately.
layer
.
needRe
draw
()
layer
.
needRe
nder
()
}
}
}
}
skiko/src/commonMain/kotlin/org/jetbrains/skiko/redrawer/Redrawer.kt
View file @
afcd9e54
...
@@ -6,8 +6,8 @@ private val initialTime = TimeSource.Monotonic.markNow()
...
@@ -6,8 +6,8 @@ private val initialTime = TimeSource.Monotonic.markNow()
internal
interface
Redrawer
{
internal
interface
Redrawer
{
fun
dispose
()
fun
dispose
()
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
fun
re
nderImmediately
(
)
fun
syncBounds
()
=
Unit
fun
syncBounds
()
=
Unit
fun
update
(
nanoTime
:
Long
=
initialTime
.
elapsedNow
().
inWholeNanoseconds
)
fun
update
(
nanoTime
:
Long
=
initialTime
.
elapsedNow
().
inWholeNanoseconds
)
fun
setVisible
(
isVisible
:
Boolean
)
=
Unit
fun
setVisible
(
isVisible
:
Boolean
)
=
Unit
...
...
skiko/src/jvmMain/kotlin/org/jetbrains/skiko/SkikoProperties.kt
View file @
afcd9e54
...
@@ -42,6 +42,10 @@ object SkikoProperties {
...
@@ -42,6 +42,10 @@ object SkikoProperties {
}
}
}
}
val
macOSWaitForPreviousFrameVsyncOnRedrawImmediately
:
Boolean
get
()
{
return
getProperty
(
"skiko.rendering.macos.waitForPreviousFrameVsyncOnRedrawImmediately"
)
?.
toBoolean
()
?:
true
}
val
windowsWaitForVsyncOnRedrawImmediately
:
Boolean
get
()
{
val
windowsWaitForVsyncOnRedrawImmediately
:
Boolean
get
()
{
return
getProperty
(
"skiko.rendering.windows.waitForFrameVsyncOnRedrawImmediately"
)
?.
toBoolean
()
?:
false
return
getProperty
(
"skiko.rendering.windows.waitForFrameVsyncOnRedrawImmediately"
)
?.
toBoolean
()
?:
false
}
}
...
...
skiko/src/linuxMain/kotlin/org/jetbrains/skiko/SkiaLayer.linux.kt
View file @
afcd9e54
...
@@ -17,9 +17,10 @@ actual open class SkiaLayer {
...
@@ -17,9 +17,10 @@ actual open class SkiaLayer {
set
(
value
)
{}
set
(
value
)
{}
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
TODO
(
"Not yet implemented"
)
get
()
=
TODO
(
"Not yet implemented"
)
actual
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
TODO
(
"unimplemented"
)
TODO
(
"unimplemented"
)
}
}
actual
fun
needRedraw
()
=
needRender
()
actual
fun
attachTo
(
container
:
Any
)
{
actual
fun
attachTo
(
container
:
Any
)
{
TODO
(
"unimplemented"
)
TODO
(
"unimplemented"
)
}
}
...
...
skiko/src/macosMain/kotlin/org/jetbrains/skiko/SkiaLayer.macos.kt
View file @
afcd9e54
...
@@ -85,13 +85,13 @@ actual open class SkiaLayer {
...
@@ -85,13 +85,13 @@ actual open class SkiaLayer {
@ObjCAction
@ObjCAction
fun
frameDidChange
(
notification
:
NSNotification
)
{
fun
frameDidChange
(
notification
:
NSNotification
)
{
redrawer
?.
syncBounds
()
redrawer
?.
syncBounds
()
redrawer
?.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
?.
re
nderImmediately
(
)
}
}
@ObjCAction
@ObjCAction
fun
windowDidChangeBackingProperties
(
notification
:
NSNotification
)
{
fun
windowDidChangeBackingProperties
(
notification
:
NSNotification
)
{
redrawer
?.
syncBounds
()
redrawer
?.
syncBounds
()
redrawer
?.
re
drawImmediately
(
updateNeeded
=
true
)
redrawer
?.
re
nderImmediately
(
)
}
}
fun
addObserver
()
{
fun
addObserver
()
{
...
@@ -127,7 +127,7 @@ actual open class SkiaLayer {
...
@@ -127,7 +127,7 @@ actual open class SkiaLayer {
nsViewObserver
.
addObserver
()
nsViewObserver
.
addObserver
()
redrawer
=
createNativeRedrawer
(
this
,
renderApi
).
apply
{
redrawer
=
createNativeRedrawer
(
this
,
renderApi
).
apply
{
syncBounds
()
syncBounds
()
needRe
draw
()
needRe
nder
()
}
}
}
}
...
@@ -140,10 +140,12 @@ actual open class SkiaLayer {
...
@@ -140,10 +140,12 @@ actual open class SkiaLayer {
/**
/**
* Schedules a frame to an appropriate moment.
* Schedules a frame to an appropriate moment.
*/
*/
actual
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
redrawer
?.
needRe
draw
(
throttledToVsync
)
redrawer
?.
needRe
nder
(
throttledToVsync
)
}
}
actual
fun
needRedraw
()
=
needRender
()
/**
/**
* Updates the [picture] according to current [nanoTime]
* Updates the [picture] according to current [nanoTime]
*/
*/
...
...
skiko/src/macosMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.macos.kt
View file @
afcd9e54
...
@@ -137,7 +137,7 @@ internal class MacOsMetalRedrawer(
...
@@ -137,7 +137,7 @@ internal class MacOsMetalRedrawer(
/**
/**
* Schedules a frame [draw] to an appropriate moment.
* Schedules a frame [draw] to an appropriate moment.
*/
*/
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
checkDisposed
()
checkDisposed
()
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
...
@@ -150,10 +150,10 @@ internal class MacOsMetalRedrawer(
...
@@ -150,10 +150,10 @@ internal class MacOsMetalRedrawer(
/**
/**
* Invokes [draw] right away.
* Invokes [draw] right away.
*/
*/
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
checkDisposed
()
checkDisposed
()
autoreleasepool
{
autoreleasepool
{
if
(!
isDisposed
&&
updateNeeded
)
{
if
(!
isDisposed
)
{
update
()
update
()
}
}
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
if
(!
isDisposed
)
{
// Redrawer may be disposed in user code, during `update`
...
...
skiko/src/macosMain/kotlin/org/jetbrains/skiko/redrawer/OpenGLRedrawer.macos.kt
View file @
afcd9e54
...
@@ -38,7 +38,7 @@ internal class MacOsOpenGLRedrawer(
...
@@ -38,7 +38,7 @@ internal class MacOsOpenGLRedrawer(
}
}
private
val
frameDispatcher
=
FrameDispatcher
(
SkikoDispatchers
.
Main
)
{
private
val
frameDispatcher
=
FrameDispatcher
(
SkikoDispatchers
.
Main
)
{
re
drawImmediately
(
updateNeeded
=
true
)
re
nderImmediately
(
)
}
}
override
fun
dispose
()
{
override
fun
dispose
()
{
...
@@ -70,11 +70,11 @@ internal class MacOsOpenGLRedrawer(
...
@@ -70,11 +70,11 @@ internal class MacOsOpenGLRedrawer(
skiaLayer
.
update
(
nanoTime
)
skiaLayer
.
update
(
nanoTime
)
}
}
override
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
override
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
frameDispatcher
.
scheduleFrame
()
frameDispatcher
.
scheduleFrame
()
}
}
override
fun
re
drawImmediately
(
updateNeeded
:
Boolean
)
{
override
fun
re
nderImmediately
(
)
{
glLayer
.
setNeedsDisplay
()
glLayer
.
setNeedsDisplay
()
skiaLayer
.
nsView
.
setNeedsDisplay
(
true
)
skiaLayer
.
nsView
.
setNeedsDisplay
(
true
)
}
}
...
...
skiko/src/uikitMain/kotlin/org/jetbrains/skiko/SkiaLayer.uikit.kt
View file @
afcd9e54
...
@@ -23,10 +23,12 @@ actual open class SkiaLayer {
...
@@ -23,10 +23,12 @@ actual open class SkiaLayer {
get
()
=
false
get
()
=
false
set
(
_
)
{
throw
UnsupportedOperationException
()
}
set
(
_
)
{
throw
UnsupportedOperationException
()
}
actual
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
needRedrawCallback
.
invoke
()
needRedrawCallback
.
invoke
()
}
}
actual
fun
needRedraw
()
=
needRender
()
actual
val
component
:
Any
?
actual
val
component
:
Any
?
get
()
=
this
.
view
get
()
=
this
.
view
...
...
skiko/src/uikitMain/kotlin/org/jetbrains/skiko/SkikoUIView.kt
View file @
afcd9e54
...
@@ -65,7 +65,7 @@ class SkikoUIView : UIView {
...
@@ -65,7 +65,7 @@ class SkikoUIView : UIView {
}
}
)
)
skiaLayer
.
needRedrawCallback
=
_redrawer
::
needRe
draw
skiaLayer
.
needRedrawCallback
=
_redrawer
::
needRe
nder
skiaLayer
.
view
=
this
skiaLayer
.
view
=
this
}
}
...
...
skiko/src/uikitMain/kotlin/org/jetbrains/skiko/redrawer/MetalRedrawer.uikit.kt
View file @
afcd9e54
...
@@ -190,7 +190,7 @@ internal class MetalRedrawer(
...
@@ -190,7 +190,7 @@ internal class MetalRedrawer(
context
.
close
()
context
.
close
()
}
}
internal
fun
needRe
draw
()
{
internal
fun
needRe
nder
()
{
displayLinkConditions
.
needsRedrawOnNextVsync
=
true
displayLinkConditions
.
needsRedrawOnNextVsync
=
true
}
}
...
...
skiko/src/webMain/kotlin/org/jetbrains/skiko/SkiaLayer.js.kt
View file @
afcd9e54
...
@@ -48,10 +48,12 @@ actual open class SkiaLayer {
...
@@ -48,10 +48,12 @@ actual open class SkiaLayer {
/**
/**
* Schedules a drawFrame to the appropriate moment.
* Schedules a drawFrame to the appropriate moment.
*/
*/
actual
fun
needRe
draw
(
throttledToVsync
:
Boolean
)
{
actual
fun
needRe
nder
(
throttledToVsync
:
Boolean
)
{
state
?.
needRedraw
()
state
?.
needRedraw
()
}
}
actual
fun
needRedraw
()
=
needRender
()
/**
/**
* An implementation of [SkikoRenderDelegate] with content rendering and
* An implementation of [SkikoRenderDelegate] with content rendering and
* event processing logic.
* event processing logic.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment