From 9b96d033844fc3321b6d45291aeec1d6c8dd3f36 Mon Sep 17 00:00:00 2001 From: Jeena Date: Tue, 20 Oct 2026 08:00:13 +0000 Subject: [PATCH] app: cap Coil's parallel fetch and decode work on TV hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A sched trace on the TV (release build) caught the real "2fps": not a slow frame, but a starved one. During navigation the render pipeline (app RenderThread, SurfaceFlinger/RenderEngine, the mali driver worker, the Dolby audio service) saturated all four cores together with Coil's image work — a burst of parallel ~30 ms bitmap decodes split across the whole IO pool descheduled the main thread hundreds of times and froze navigation for over a second. The image loader now caps the request dispatcher at 2 parallel ops and the decode/transformation pools at 2/1: images still fill in steadily while scrolling but can no longer eat every core at once. Combined with the release build on the TV (debug's JIT bursts produced 900 ms+ frames by themselves), the same navigation settles at p90 ~13 ms per frame with a worst frame of ~180 ms when a new row composes. --- app/src/main/java/net/jeena/jtubetv/JTubeApp.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/jeena/jtubetv/JTubeApp.kt b/app/src/main/java/net/jeena/jtubetv/JTubeApp.kt index fe13971..a9d3f43 100644 --- a/app/src/main/java/net/jeena/jtubetv/JTubeApp.kt +++ b/app/src/main/java/net/jeena/jtubetv/JTubeApp.kt @@ -1,6 +1,7 @@ package net.jeena.jtubetv import android.app.Application +import kotlinx.coroutines.Dispatchers import coil.ImageLoader import coil.ImageLoaderFactory import net.jeena.jtubetv.data.PeerTubeRepository @@ -33,9 +34,20 @@ class JTubeApp : Application(), coil.ImageLoaderFactory { * TVs are slow compositors: decode thumbnails as RGB_565 at capped size so * scrolling rows keep a smooth frame rate (user: "the animation has very low fps"). */ - override fun newImageLoader(): ImageLoader = - ImageLoader.Builder(this) + override fun newImageLoader(): ImageLoader { + // A sched trace on the TV (release build) showed the main thread being + // descheduled hundreds of times during navigation while the render + // pipeline (Mali/RenderThread/SF/Dolby) saturated all 4 cores together + // with Coil's image work — a burst of parallel 30 ms bitmap decodes + // starved the main thread for over a second (the felt "2fps"). Capping + // Coil's parallel work: images still fill in steadily while scrolling, + // but the decode/fetch pool can no longer eat every core at once. + return ImageLoader.Builder(this) .bitmapConfig(android.graphics.Bitmap.Config.RGB_565) .crossfade(false) + .dispatcher(Dispatchers.IO.limitedParallelism(2)) + .decoderDispatcher(Dispatchers.Default.limitedParallelism(2)) + .transformationDispatcher(Dispatchers.Default.limitedParallelism(1)) .build() + } } \ No newline at end of file