You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
2.3 KiB
76 lines
2.3 KiB
import androidx.compose.runtime.Composable |
|
import androidx.compose.ui.ComposeScene |
|
import androidx.compose.ui.unit.Constraints |
|
import org.jetbrains.skia.DirectContext |
|
import org.jetbrains.skia.Surface |
|
import kotlin.time.Duration |
|
import kotlin.time.Duration.Companion.nanoseconds |
|
import kotlin.time.ExperimentalTime |
|
import kotlin.time.measureTime |
|
import kotlinx.coroutines.* |
|
|
|
const val nanosPerSecond = 1E9.toLong() |
|
const val millisPerSecond = 1e3.toLong() |
|
const val nanosPerMillisecond = nanosPerSecond / millisPerSecond |
|
|
|
interface GraphicsContext { |
|
fun surface(width: Int, height: Int): Surface |
|
|
|
suspend fun awaitGPUCompletion() |
|
} |
|
|
|
@OptIn(ExperimentalTime::class) |
|
fun measureComposable( |
|
frameCount: Int = 500, |
|
width: Int, |
|
height: Int, |
|
targetFps: Int, |
|
graphicsContext: GraphicsContext?, |
|
content: @Composable () -> Unit |
|
): BenchmarkResult = runBlocking { |
|
val scene = ComposeScene() |
|
try { |
|
val nanosPerFrame = (1.0 / targetFps.toDouble() * nanosPerSecond).toLong() |
|
scene.setContent(content) |
|
scene.constraints = Constraints.fixed(width, height) |
|
val surface = graphicsContext?.surface(width, height) ?: Surface.makeNull(width, height) |
|
|
|
val frames = MutableList(frameCount) { |
|
BenchmarkFrame(Duration.INFINITE, Duration.INFINITE) |
|
} |
|
|
|
var nanoTime = 0L |
|
|
|
repeat(frameCount) { |
|
val frameTime = measureTime { |
|
val cpuTime = measureTime { |
|
scene.render(surface.canvas, nanoTime) |
|
surface.flushAndSubmit(false) |
|
} |
|
|
|
val gpuTime = measureTime { |
|
graphicsContext?.awaitGPUCompletion() |
|
} |
|
|
|
frames[it] = BenchmarkFrame(cpuTime, gpuTime) |
|
} |
|
|
|
val actualNanosPerFrame = frameTime.inWholeNanoseconds |
|
val nanosUntilDeadline = nanosPerFrame - actualNanosPerFrame |
|
|
|
// Emulate waiting for next vsync |
|
if (nanosUntilDeadline > 0) { |
|
delay(nanosUntilDeadline / nanosPerMillisecond) |
|
} |
|
|
|
nanoTime += maxOf(actualNanosPerFrame, nanosPerFrame) |
|
} |
|
|
|
BenchmarkResult( |
|
nanosPerFrame.nanoseconds, |
|
frames |
|
) |
|
} finally { |
|
scene.close() |
|
} |
|
}
|
|
|