This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-skiko-hardware-canvas-frame-budget-guardrail

KMP Skiko Hardware Canvas Direct Frame Budget and Thread Deadlock Guardrail

Enforces strict sub-millisecond draw allocations, lock-free render pipelines, and asynchronous GPU texture synchronization across iOS (Metal) and Android (Vulkan/GLES) targets using Skiko and Compose Multiplatform. Prevents main-thread starvation, display-link deadlocks, and missed 120Hz VSYNC frame budgets during direct hardware canvas interop.

Prohibited Anti-Pattern: Allocating Skia native objects and acquiring synchronous locks inside direct Skiko hardware canvas draw passes.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP Skiko Hardware Canvas Direct Frame Budget and Thread Deadlock Guardrail - Enforces strict sub-millisecond draw allocations, lock-free render pipelines, and asynchronous GPU texture synchronization across iOS (Metal) and Android (Vulkan/GLES) targets using Skiko and Compose Multiplatform. Prevents main-thread starvation, display-link deadlocks, and missed 120Hz VSYNC frame budgets during direct hardware canvas interop.
globs: **/commonMain/**/*.kt, **/iosMain/**/*.kt, **/androidMain/**/*.kt, **/*Canvas*.kt, **/*Renderer*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-skiko-hardware-canvas-frame-budget-guardrail"
---

# KMP Skiko Hardware Canvas Direct Frame Budget and Thread Deadlock Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Allocating Skia native objects and acquiring synchronous locks inside direct Skiko hardware canvas draw passes.

## System Constraints
You are a Principal Mobile Systems Architect specializing in Kotlin Multiplatform and Skiko GPU graphics pipelines. Your mandate is to enforce zero-allocation hardware canvas loops, deterministic 8.33ms (120Hz) frame budgets, and complete immunity against native platform dispatch deadlocks between iOS CADisplayLink/Metal command queues and Android Choreographer/Vulkan renderers.

## Strict Guidelines
- Engineers MUST ALWAYS pre-allocate and reuse native Skiko/Skia primitives (`org.jetbrains.skia.Paint`, `Path`, `Shader`, `ColorFilter`) outside the composable drawing phase or cache them via keyed remember/render lifecycle states.
- Engineers MUST NOT invoke `runBlocking`, blocking Mutex locks, or synchronous thread barriers within `drawWithContent`, `Canvas`, or Skiko `SkiaLayer.render` callbacks.
- Code MUST isolate mutable graphics state transfers onto double-buffered, lock-free ring buffers or atomic state holders before dispatching to the native VSYNC draw pass.
- Engineers MUST NOT invoke blocking image decodes or native file/network I/O inside the direct canvas frame lifecycle; all pixel buffers must be uploaded to GPU textures ahead of time.
- Platform interop bridges (Metal Layer on iOS and SurfaceView on Android) MUST yield the GPU context back within a strict 4.0ms rasterization window to maintain the 8.33ms hardware display budget.
- Components NEVER bridge coroutine dispatchers using `Dispatchers.Main` inside a raw Skiko frame render cycle, as iOS Metal presentation loops can deadlock the UI thread during touch event dispatch.

## Bad vs Good Implementations
### Skiko Direct Canvas Rendering: Allocation & Deadlock Elimination

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Instantiating native Skia objects (Paint, Path) on every frame triggers continuous JNI/Kotlin-Native memory bridging and forces GC pressure, blowing past the 8.33ms (120Hz) frame budget. Calling `runBlocking` inside the frame loop starves the native display pump (CADisplayLink/Choreographer), causing severe jank and rendering deadlocks when GPU resources are acquired.
```kotlin
import androidx.compose.foundation.Canvas
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import kotlinx.coroutines.runBlocking
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Path

@Composable
fun RadarScanView(state: RadarTelemetryState, modifier: Modifier = Modifier) {
    Canvas(modifier = modifier) {
        // CRITICAL BUG 1: Allocating Skia C++ native wrapper objects inside the hot draw path
        val nativePaint = Paint().apply {
            color = 0xFF00FF00.toInt()
            strokeWidth = 4f
        }
        val path = Path()

        // CRITICAL BUG 2: Synchronous blocking lock on the UI/Render thread causing frame drops & deadlocks
        val points = runBlocking {
            state.fetchCurrentPointsSync()
        }

        drawIntoCanvas { canvas ->
            points.forEachIndexed { i, pt ->
                if (i == 0) path.moveTo(pt.x, pt.y) else path.lineTo(pt.x, pt.y)
            }
            canvas.nativeCanvas.drawPath(path, nativePaint)
        }
        // Leaking native Skia C++ peer memory every frame
    }
}
```

#### Production Standard (Correct)
> **Standard**: Skia native primitives are allocated once and tied to the composable lifecycle with explicit deterministic native cleanup (`close()`). State is accessed via an atomic lock-free snapshot with zero blocking operations, and paths are reset without reallocating native buffers, sustaining deterministic execution under 1ms total frame budget.
```kotlin
import androidx.compose.foundation.Canvas
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Path

private class RenderBufferState {
    val nativePaint = Paint().apply {
        color = 0xFF00FF00.toInt()
        strokeWidth = 4f
        isAntiAlias = true
    }
    val path = Path()

    fun release() {
        nativePaint.close()
        path.close()
    }
}

@Composable
fun RadarScanView(state: RadarTelemetryState, modifier: Modifier = Modifier) {
    val cache = remember { RenderBufferState() }
    
    DisposableEffect(cache) {
        onDispose { cache.release() }
    }

    // Pre-sampled, non-blocking lock-free state access
    val frameSnapshot = state.atomicSnapshot.get()

    Canvas(modifier = modifier) {
        drawIntoCanvas { canvas ->
            val skiaPath = cache.path
            skiaPath.reset()

            val points = frameSnapshot.points
            for (i in points.indices) {
                val pt = points[i]
                if (i == 0) skiaPath.moveTo(pt.x, pt.y) else skiaPath.lineTo(pt.x, pt.y)
            }

            canvas.nativeCanvas.drawPath(skiaPath, cache.nativePaint)
        }
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: KMP Skiko Hardware Canvas Direct Frame Budget and Thread Deadlock Guardrail

KMP Skiko Hardware Canvas Direct Frame Budget and Thread Deadlock Guardrail defines an industrial-grade behavioral standard for mobile engineering. Enforces strict sub-millisecond draw allocations, lock-free render pipelines, and asynchronous GPU texture synchronization across iOS (Metal) and Android (Vulkan/GLES) targets using Skiko and Compose Multiplatform. Prevents main-thread starvation, display-link deadlocks, and missed 120Hz VSYNC frame budgets during direct hardware canvas interop. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Allocating Skia native objects and acquiring synchronous locks inside direct Skiko hardware canvas draw passes. Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftAllocating Skia native objects and acquiring synchronous locks inside direct Skiko hardware canvas draw passes.Critical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS pre-allocate and reuse native Skiko/Skia primitives (`org.jetbrains.skia.Paint`, `Path`, `Shader`, `ColorFilter`) outside the composable drawing phase or cache them via keyed remember/render lifecycle states.

Engineers MUST ALWAYS pre-allocate and reuse native Skiko/Skia primitives (`org.jetbrains.skia.Paint`, `Path`, `Shader`, `ColorFilter`) outside the composable drawing phase or cache them via keyed remember/render lifecycle states.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT invoke `runBlocking`, blocking Mutex locks, or synchronous thread barriers within `drawWithContent`, `Canvas`, or Skiko `SkiaLayer.render` callbacks.

Engineers MUST NOT invoke `runBlocking`, blocking Mutex locks, or synchronous thread barriers within `drawWithContent`, `Canvas`, or Skiko `SkiaLayer.render` callbacks.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Code MUST isolate mutable graphics state transfers onto double-buffered, lock-free ring buffers or atomic state holders before dispatching to the native VSYNC draw pass.

Code MUST isolate mutable graphics state transfers onto double-buffered, lock-free ring buffers or atomic state holders before dispatching to the native VSYNC draw pass.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Engineers MUST NOT invoke blocking image decodes or native file/network I/O inside the direct canvas frame lifecycle

Engineers MUST NOT invoke blocking image decodes or native file/network I/O inside the direct canvas frame lifecycle; all pixel buffers must be uploaded to GPU textures ahead of time.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Platform interop bridges (Metal Layer on iOS and SurfaceView on Android) MUST yield the GPU context back within a strict 4.0ms rasterization window to maintain the 8.33ms hardware display budget.

Platform interop bridges (Metal Layer on iOS and SurfaceView on Android) MUST yield the GPU context back within a strict 4.0ms rasterization window to maintain the 8.33ms hardware display budget.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Components NEVER bridge coroutine dispatchers using `Dispatchers.Main` inside a raw Skiko frame render cycle, as iOS Metal presentation loops can deadlock the UI thread during touch event dispatch.

Components NEVER bridge coroutine dispatchers using `Dispatchers.Main` inside a raw Skiko frame render cycle, as iOS Metal presentation loops can deadlock the UI thread during touch event dispatch.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is KMP Skiko Hardware Canvas Direct Frame Budget and Thread Deadlock Guardrail critical for AI coding assistants?

Enforces strict sub-millisecond draw allocations, lock-free render pipelines, and asynchronous GPU texture synchronization across iOS (Metal) and Android (Vulkan/GLES) targets using Skiko and Compose Multiplatform. Prevents main-thread starvation, display-link deadlocks, and missed 120Hz VSYNC frame budgets during direct hardware canvas interop. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Allocating Skia native objects and acquiring synchronous locks inside direct Skiko hardware canvas draw passes.) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

KMP