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

TextField cursor animation uses a lot of CPU #4225

Closed
zoff99 opened this issue Feb 3, 2024 · 18 comments · Fixed by Zoxcore/trifa_material#165
Closed

TextField cursor animation uses a lot of CPU #4225

zoff99 opened this issue Feb 3, 2024 · 18 comments · Fixed by Zoxcore/trifa_material#165
Assignees

Comments

@zoff99
Copy link

zoff99 commented Feb 3, 2024

Describe the bug
i have a text input in my app, when i click on it to focus and enter text, cpu usage goes to 100%
as soon as i click somewhere else to shift focus, cpu goes back to normal again

Affected platforms

  • Desktop

Versions

  • Kotlin version*: 1.9.22
  • Compose Multiplatform version*: 1.5.11, 1.5.12
  • OS version(s)* (required for Desktop and iOS issues): ubuntu 22.04
  • OS architecture (x86 or arm64): x86 64bit
  • JDK (for desktop issues): 17, 21

To Reproduce
Steps and/or the code snippet to reproduce the behavior:

  1. Go to '...'
  2. Click on '...'
  3. Scroll down to '...'
  4. See error

Expected behavior
set focus on the textfield and not use 100% cpu

@zoff99 zoff99 added bug Something isn't working submitted labels Feb 3, 2024
@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

to reproduce:

git clone https://github.com/zoff99/trifa_material
cd trifa_material
git checkout cpu_burn
./gradlew run

then click inside the green textfield to focus it, and you see the blinking cursor.
what cpu usage with htop

@m-sasha
Copy link
Contributor

m-sasha commented Feb 3, 2024

Could you provide a smaller reproducer?

@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

whats the problem with this one?

@m-sasha
Copy link
Contributor

m-sasha commented Feb 3, 2024

A small/minimal reproducer demonstrates the bug.

A project

  1. Requires us to check out the code, potentially running malicious code in the gradle build file, or the code itself.
  2. If the code isn't minimal - requires to look through it to find the issue.
  3. Often the issue turns out to be user error. Reducing the reproducer to a minimal one lowers the chance of that.
  4. Having the code in the ticket makes it easier to discuss and for 3rd parties to look at. Your project could be gone a year from now, and someone looking at this ticket then wouldn't be able to understand the issue.

@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

i will remove as much as i can. but as you say its probably a combination of things.

@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

i removed as much as possible from that branch.
the issue is still there. and now its only Main.kt left with a few lines of code.

@m-sasha
Copy link
Contributor

m-sasha commented Feb 3, 2024

Can you post the code here then?

@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

you will still need some project and gradle files to run it.

@zoff99
Copy link
Author

zoff99 commented Feb 3, 2024

@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.MaterialTheme
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.zoffcc.applications.trifa_material.trifa_material.BuildConfig
import kotlinx.coroutines.DelicateCoroutinesApi


@OptIn(DelicateCoroutinesApi::class, ExperimentalFoundationApi::class)
@Composable
@Preview
fun App()
{
    MaterialTheme {
        Column(modifier = Modifier.fillMaxSize()) {
            Row(modifier = Modifier.fillMaxWidth().height(100.dp)
                .background(Color.Green)) {
                SendMessage() { text -> //
                }
            }
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SendMessage(sendMessage: (String) -> Unit)
{
    var inputText by remember { mutableStateOf("") }
    // var show_emoji_popup by remember { mutableStateOf(false) }
    TextField(
        modifier = Modifier.fillMaxWidth(),
        value = inputText,
        onValueChange = {
        }
    )
}

fun main() = application(exitProcessOnExit = true) {
    MainAppStart()
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun MainAppStart()
{
    val appIcon = painterResource("icon-linux.png")
    // ----------- main app screen -----------
    // ----------- main app screen -----------
    // ----------- main app screen -----------
    Window(
        onCloseRequest = { }, title = "TRIfA - " + BuildConfig.APP_VERSION,
        icon = appIcon,
        focusable = true,
    ) {
        App()
    }
}

@zoff99 zoff99 changed the title focusing on a Text files uses 100% cpu focusing on a Text field uses 100% cpu Feb 3, 2024
@m-sasha
Copy link
Contributor

m-sasha commented Feb 3, 2024

On my computer (M1 Ultra) this app goes from ~2% to ~20% CPU when focusing the text field.

fun main() = singleWindowApplication {
    TextField(
        modifier = Modifier.fillMaxWidth(),
        value = "",
        onValueChange = { }
    )
}

This doesn't seem reasonable, indeed.

@m-sasha m-sasha added performance and removed bug Something isn't working submitted labels Feb 3, 2024
@m-sasha m-sasha self-assigned this Feb 3, 2024
@m-sasha m-sasha changed the title focusing on a Text field uses 100% cpu TextField cursor animation uses a lot of CPU Feb 5, 2024
@m-sasha
Copy link
Contributor

m-sasha commented Feb 5, 2024

The proximal reason for this is that the cursor blinking is implemented as a regular animation, which causes frameClock.hasAwaiters to be true all the time, in turn causing render to run all the time.

The above shouldn't be spiking the CPU so high, however, and according to @igordmn, on Windows, it doesn't. So it seems we have a problem in Skiko on Linux and/or macOS. For example, this skiko code also causes an unreasonably high CPU utilization:

JFrame("SkikoIdlePerfTest").apply {
    size = Dimension(800, 600)
    val props = SkiaLayerProperties(
        renderApi = GraphicsApi.METAL,
        isVsyncEnabled = true,
    )

    add(SkiaLayer(properties = props).apply {
        skikoView = object : SkikoView {
            override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
                needRedraw()
            }
        }
    })
    isVisible = true
}

@zoff99
Copy link
Author

zoff99 commented Feb 14, 2024

any progress on the fix? is there an issue or PR to watch?

@m-sasha
Copy link
Contributor

m-sasha commented Feb 14, 2024

There will be a fix, but not in 1.6.0. I'll post an update here.

@zoff99
Copy link
Author

zoff99 commented Feb 21, 2024

There will be a fix, but not in 1.6.0. I'll post an update here.

after it is merged, how can i get it in my app?

@mgroth0
Copy link

mgroth0 commented Feb 21, 2024

A while ago I noticed a similar issue but I cannot remember if I ever reported it.

val MODIFIER = Modifier.pointerHoverIcon(PointerIcon(Cursor(Cursor.WAIT_CURSOR)))
fun main() {
    application {
        Window(visible = true, onCloseRequest = {
            exitApplication()
        }) {
            Column(
                modifier = MODIFIER
            ) {
                Text("Pointer Cached")
            }
        }
    }
}

The issue is that the spinning cursor stutters and sometimes stops for up to a second before spinning again.

I am on MacOS with the latest compose version

@m-sasha
Copy link
Contributor

m-sasha commented Feb 21, 2024

after it is merged, how can i get it in my app?

We have regular dev releases.

@m-sasha
Copy link
Contributor

m-sasha commented Feb 21, 2024

@m-sasha m-sasha closed this as completed Feb 21, 2024
zoff99 added a commit to zoff99/trifa_material that referenced this issue Feb 22, 2024
this fixes: JetBrains/compose-multiplatform#4225

TextField cursor animation uses a lot of CPU

and autofocus on text fields for entering a message again.
zoff99 added a commit to Zoxcore/trifa_material that referenced this issue Feb 22, 2024
this fixes: JetBrains/compose-multiplatform#4225

TextField cursor animation uses a lot of CPU

and autofocus on text fields for entering a message again.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment