Manel Martos Roldan
11/06/2023, 9:23 PMtime
on the AGSL shader. I've been using graphicsLayer
modifier to update it, and for some reason, the new modifier system doesn't seem to handle it as smoothly as it used to before the update.
Any of you fine folks run into similar issues?
I've found some workarounds, like wrapping StrokedClock
with key(time)
, but it feels like overkill.
What's the best way to achieve the same results?
Thanks in advance for any help! πromainguy
11/07/2023, 12:15 AMManel Martos Roldan
11/07/2023, 6:40 AMgraphicsLayer
and call updateTime
from the outer scope issue persistManel Martos Roldan
11/07/2023, 7:25 AMtime
to bandShader
remember and bandShader
to shaderBrush
remember it works:
@Composable
fun StrokedClock(
clock: String,
time: Float,
modifier: Modifier,
) {
BoxWithConstraints(modifier = modifier) {
val bandShader = remember(time) {
PulseBandShader().apply {
updateResolution(
Size(
constraints.maxWidth.toFloat(),
constraints.maxHeight.toFloat()
)
)
updateTime(time)
}
}
val shaderBrush = remember(bandShader) { ShaderBrush(bandShader) }
Text(
text = clock,
color = MaterialTheme.colorScheme.onSurface,
style = TextStyle.Default.copy(
textAlign = TextAlign.Center,
fontSize = 72.sp,
fontWeight = FontWeight.Bold,
fontFamily = fontFamily,
drawStyle = Stroke(
miter = 10f,
width = 5f,
join = StrokeJoin.Round
),
brush = shaderBrush,
),
)
}
}
But I feel like this solution have a huge impact in memory consumption and gc too. Prev version didn't needed none of this tweaks. Is there any trick I can apply to redraw Text
composable with updated shaderBrush
and bandShader
without impacting in memory footprint?shikasd
11/07/2023, 10:04 PMManel Martos Roldan
11/09/2023, 6:33 AM@Composable
fun StrokedClock(
modifier: Modifier,
) {
BoxWithConstraints(modifier = modifier) {
var localDate by remember { mutableStateOf(LocalDateTime.now()) }
var time by remember { mutableFloatStateOf(0f) }
val bandShader = remember {
PulseBandShader().apply {
updateResolution(
Size(
constraints.maxWidth.toFloat(),
constraints.maxHeight.toFloat()
)
)
}
}
val shaderBrush = remember { ShaderBrush(bandShader) }
FrameEffect(Unit, initialValue = localDate.nano / 1_000_000_000f) {
time = it
localDate = LocalDateTime.now()
bandShader.updateTime(time)
}
val formatter = remember { DateTimeFormatter.ofPattern("HH:mm:ss") }
val formattedTime by remember { derivedStateOf { formatter.format(localDate) } }
Text(
text = formattedTime,
color = MaterialTheme.colorScheme.onSurface,
style = TextStyle.Default.copy(
textAlign = TextAlign.Center,
fontSize = 72.sp,
fontWeight = FontWeight.Bold,
fontFamily = fontFamily,
drawStyle = Stroke(
miter = 10f,
width = 5f,
join = StrokeJoin.Round
),
brush = shaderBrush,
),
)
}
}
shikasd
11/09/2023, 10:38 AMModifier.drawWithContent {
time.value // just read the state so Compose knows to redraw
drawContent()
}
Manel Martos Roldan
11/11/2023, 1:40 PMshikasd
11/12/2023, 1:58 AMManel Martos Roldan
11/12/2023, 8:06 AMbandShader.updateTime(time + 0.5f)
Since the band shader is designed to last 1 second and the drawing begins completely cleared, this might be why you're not seeing anything at all.shikasd
11/12/2023, 10:44 AMshikasd
11/12/2023, 1:12 PM@Composable
fun NoiseReveal(
modifier: Modifier = Modifier
) {
val shader = remember { NoiseRevealShader() }
val brush = remember { ShaderBrush(shader) }
Text(
...,
style = TextStyle(brush)
)
}
I would expect that adding something like this should be enough, as we read time
in draw pass, invalidating it:
Modifier.drawWithContent {
shader.update(/* size / time */)
drawContent()
}
But the only way I managed to make it work is recreating the brush whenever time changes:
var time by remember { mutableFloatStateOf(0f) }
val shader = remember { NoiseRevealShader() }
FrameEffect(Unit) {
time = it
}
// This causes recomposition (reading `time`) to recreate and re-set the brush
// If the brush is not recreated, the change is not applied.
shader.updateTime(time)
val brush = ShaderBrush(shader)
Text(
text = "Hello from Creative Lab!",
color = MaterialTheme.colorScheme.onSurface,
style = TextStyle(
textAlign = TextAlign.Center,
fontSize = 40.sp,
fontFamily = fontFamily,
fontWeight = FontWeight.Bold,
brush = brush
),
modifier = modifier
.onSizeChanged {
shader.updateResolution(it.toSize())
}
)
shikasd
11/12/2023, 1:15 PMShaderBrush
in this case and only invalidate draw for each animation tick?shikasd
11/12/2023, 1:17 PMHalil Ozercan
11/12/2023, 1:35 PMcreateShader
function. We solved this for Brush span in AnnotatedString
using derivedStateOf
to wrap createShader
call but I haven't got to fix it for brush parameter in TextStyle
yet.shikasd
11/12/2023, 1:38 PMcreateShader
function?Halil Ozercan
11/12/2023, 1:46 PMval shaderBrush = remember {
object : ShaderBrush() {
override fun createShader(size: Size): Shader {
bandShader.updateTime(time)
return bandShader
}
}
}
Halil Ozercan
11/12/2023, 1:46 PMshikasd
11/12/2023, 1:46 PMHalil Ozercan
11/12/2023, 1:47 PMAnnotatedString
. It's gonna be available in the next ui
module release hopefully.shikasd
11/12/2023, 1:48 PMManel Martos Roldan
11/12/2023, 7:20 PM