Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MetalRedrawer fails to draw 3rd frame between vsyncs #703

Closed
m-sasha opened this issue May 9, 2023 · 2 comments
Closed

MetalRedrawer fails to draw 3rd frame between vsyncs #703

m-sasha opened this issue May 9, 2023 · 2 comments
Assignees

Comments

@m-sasha
Copy link
Contributor

m-sasha commented May 9, 2023

Because MetalRedrawer sends frames to Metal faster than the refresh rate, Metal appears to drop the 3rd frame sent between two vsyncs.

To reproduce, add this to MetalRedrawer.kt:

    fun drawSync(){
        layer.update(System.nanoTime())
        performDraw()
    }

In uiTest remove

assumeTrue(System.getProperty("skiko.test.ui.enabled", "false") == "true")

and replace

val renderApi = System.getProperty("skiko.test.ui.renderApi", "all")

with

    val renderApi = "metal"

then add and run this test inSkiaLayerTest.kt:

    @OptIn(ExperimentalTime::class)
    @Test
    fun `frame is rendered immediately`() = uiTest {
        val window = UiTestWindow(
            properties = SkiaLayerProperties(
                isVsyncEnabled = true
            )
        )
        val colors = arrayOf(
            Color.RED,
            Color.GREEN,
            Color.BLUE,
            Color.YELLOW
        )

        var counter1 = 0
        var counter2 = 0
        val paint = Paint()

        try{
            window.setLocation(200, 200)
            window.setSize(400, 600)
            window.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
            window.layer.skikoView = object : SkikoView {
                override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
                    val c1 = counter1
                    val c2 = counter2

                    paint.color = colors[c1.mod(colors.size)].rgb
                    canvas.drawRect(Rect(0f, 0f, width.toFloat(), height/2f), paint)

                    paint.color = colors[c2.mod(colors.size)].rgb
                    canvas.drawRect(Rect(0f, height/2f, width.toFloat(), height.toFloat()), paint)
                }
            }
            window.isVisible = true

            window.addKeyListener(object: KeyAdapter(){
                override fun keyTyped(e: KeyEvent?) {
                    launch {
                        val redrawer = window.layer.redrawer as MetalRedrawer
                        redrawer.drawSync()
                        counter1 += 1
                        redrawer.drawSync()
                        counter2 += 1
                        redrawer.drawSync()
                    }
                }
            });

            window.addWindowListener(object: WindowAdapter(){
                override fun windowActivated(e: WindowEvent?) {
                    window.requestFocus()
                }
            })

            delay(Duration.INFINITE)
        } finally {
            window.close()
        }
    }

The window will show fully red. Then press SPACE and the window should switch to fully green. However, it switches to half-red half-green.

@elijah-semyonov
Copy link
Contributor

elijah-semyonov commented May 12, 2023

@m-sasha
master...elijah.semyonov/3rd-frame-lost-fix contains a repro and fix for that behavior.

Tell me if you want to add additional behavior (like waiting until the draw scheduled by drawSync is finished and presented to the surface, which is implied by method name, but not implemented currently).

@m-sasha
Copy link
Contributor Author

m-sasha commented Jul 12, 2023

Fixed by #753

@m-sasha m-sasha closed this as completed Jul 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants