This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-compose-skiko-surface-lifecycle-sync

Kotlin Multiplatform Compose Surface Lifecycle and Skiko Hardware Context Sync

Enforces strict ordering and lifecycle synchronization between UI surface transitions and underlying Skiko GPU contexts (Vulkan on Android, Metal on iOS). Prevents deadlocks, GPU context abandonment, and SIGSEGV memory faults when an application transitions between foreground, background, and configuration changes during Compose multiplatform rendering.

Prohibited Anti-Pattern: Accessing or reallocating Skiko render targets asynchronously during platform surface invalidation or backgrounding

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform Compose Surface Lifecycle and Skiko Hardware Context Sync - Enforces strict ordering and lifecycle synchronization between UI surface transitions and underlying Skiko GPU contexts (Vulkan on Android, Metal on iOS). Prevents deadlocks, GPU context abandonment, and SIGSEGV memory faults when an application transitions between foreground, background, and configuration changes during Compose multiplatform rendering.
globs: **/src/commonMain/**/*.kt, **/src/androidMain/**/*.kt, **/src/iosMain/**/*.kt, **/graphics/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-compose-skiko-surface-lifecycle-sync"
---

# Kotlin Multiplatform Compose Surface Lifecycle and Skiko Hardware Context Sync

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Accessing or reallocating Skiko render targets asynchronously during platform surface invalidation or backgrounding

## System Constraints
You are a Principal Mobile Graphics Engineer enforcing strict surface synchronization invariants between Compose Multiplatform and underlying Skiko render engines. You must prevent invalid access to destroyed native surfaces and eliminate graphics pipeline deadlocks between the main UI thread and the Skiko render thread during lifecycle transitions.

## Strict Guidelines
- Engineers MUST ALWAYS suspend render dispatches and synchronously flush Skiko command buffers before yielding to native platform surface destruction callbacks (e.g., surfaceDestroyed on Android, viewWillDisappear/applicationDidEnterBackground on iOS).
- Engineers MUST NOT acquire or reuse Direct3D, Metal, or Vulkan Skia DirectContexts across different surface lifecycle generations.
- Code SHOULD decouple high-frequency Compose state mutations from immediate native canvas redraws by utilizing disciplined frame synchronization signals (Choreographer / CADisplayLink).
- Native interop delegates MUST wrap Skia/Skiko canvas draw passes with thread-safe guards verifying the native surface handle's validity (e.g., ANativeWindow, CAMetalLayer) before issuing drawing commands.
- Components NEVER dispatch asynchronous background work to a Skiko Skia context after the backing surface has triggered its detachment hook.

## Bad vs Good Implementations
### Android SurfaceHolder Synchronization with Skiko Vulkan Context

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: surfaceDestroyed is invoked when the underlying OS surface is immediately invalid. Tearing down Skiko's DirectContext or finishing rendering asynchronously allows the render loop to issue Vulkan draw commands against a destroyed native window handle, causing device loss or native segmentation faults.
```kotlin
class SkikoSurfaceBridge(private val renderer: SkiaRenderer) : SurfaceHolder.Callback {
    override fun surfaceCreated(holder: SurfaceHolder) {
        renderer.initContext(holder.surface)
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        renderer.resize(width, height)
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // VIOLATION: Asynchronously destroying the context or failing to drain Skiko queue
        // causes Vulkan driver VK_ERROR_DEVICE_LOST or SIGSEGV when draw calls execute on invalid ANativeWindow
        CoroutineScope(Dispatchers.Default).launch {
            renderer.releaseContext()
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Ensures that before Android destroys the ANativeWindow pointer, the render loop is halted, all pending Skiko/Vulkan graphics commands are flushed, and context teardown completes synchronously on the calling thread.
```kotlin
class SkikoSurfaceBridge(private val renderer: SkiaRenderer) : SurfaceHolder.Callback {
    private val renderLock = Any()
    @Volatile private var isSurfaceAlive = false

    override fun surfaceCreated(holder: SurfaceHolder) {
        synchronized(renderLock) {
            renderer.initContext(holder.surface)
            isSurfaceAlive = true
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        synchronized(renderLock) {
            if (isSurfaceAlive) {
                renderer.resize(width, height)
            }
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // INVARIANT: Synchronously halt render pump, drain in-flight GPU commands,
        // and invalidate context before exiting callback
        synchronized(renderLock) {
            isSurfaceAlive = false
            renderer.flushAndSyncGpuWork()
            renderer.releaseContextSynchronous()
        }
    }
}
```

### iOS CAMetalLayer Lifecycle Invariant with Skiko Context

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Failing to deactivate the rendering clock or pause the Skiko Metal context when the view controller disappears leaves the CADisplayLink active, triggering draw passes against a detached CAMetalLayer and starving the Metal command queue.
```kotlin
class MetalSkikoViewController(private val renderDelegate: MetalRenderer) : UIViewController(null, null) {
    override fun viewDidDisappear(animated: Boolean) {
        super.viewDidDisappear(animated)
        // VIOLATION: Dropping the view without invalidating Skiko display link
        // allows CADisplayLink to fire on a detached CAMetalLayer, causing drawable acquisition timeouts
    }
}
```

#### Production Standard (Correct)
> **Standard**: Guarantees that the Metal rendering pipeline is paused synchronously prior to view transition execution, preventing deadlocks on nextDrawable() calls and properly managing memory under platform pressure.
```kotlin
class MetalSkikoViewController(private val renderDelegate: MetalRenderer) : UIViewController(null, null) {
    override fun viewWillAppear(animated: Boolean) {
        super.viewWillAppear(animated)
        renderDelegate.resumeRendering()
    }

    override fun viewWillDisappear(animated: Boolean) {
        // INVARIANT: Explicitly stop render pump before view transition completes
        renderDelegate.pauseRenderingSynchronously()
        super.viewWillDisappear(animated)
    }

    override fun didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        renderDelegate.purgeSkiaResourceCaches()
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

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

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Multiplatform Compose Surface Lifecycle and Skiko Hardware Context Sync

Kotlin Multiplatform Compose Surface Lifecycle and Skiko Hardware Context Sync defines an industrial-grade behavioral standard for mobile engineering. Enforces strict ordering and lifecycle synchronization between UI surface transitions and underlying Skiko GPU contexts (Vulkan on Android, Metal on iOS). Prevents deadlocks, GPU context abandonment, and SIGSEGV memory faults when an application transitions between foreground, background, and configuration changes during Compose multiplatform rendering. 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: Accessing or reallocating Skiko render targets asynchronously during platform surface invalidation or backgrounding 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 DriftAccessing or reallocating Skiko render targets asynchronously during platform surface invalidation or backgroundingCritical
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 suspend render dispatches and synchronously flush Skiko command buffers before yielding to native platform surface destruction callbacks (e.g., surfaceDestroyed on Android, viewWillDisappear/applicationDidEnterBackground on iOS).

Engineers MUST ALWAYS suspend render dispatches and synchronously flush Skiko command buffers before yielding to native platform surface destruction callbacks (e.g., surfaceDestroyed on Android, viewWillDisappear/applicationDidEnterBackground on iOS).

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 acquire or reuse Direct3D, Metal, or Vulkan Skia DirectContexts across different surface lifecycle generations.

Engineers MUST NOT acquire or reuse Direct3D, Metal, or Vulkan Skia DirectContexts across different surface lifecycle generations.

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 SHOULD decouple high-frequency Compose state mutations from immediate native canvas redraws by utilizing disciplined frame synchronization signals (Choreographer / CADisplayLink).

Code SHOULD decouple high-frequency Compose state mutations from immediate native canvas redraws by utilizing disciplined frame synchronization signals (Choreographer / CADisplayLink).

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

Native interop delegates MUST wrap Skia/Skiko canvas draw passes with thread-safe guards verifying the native surface handle's validity (e.g., ANativeWindow, CAMetalLayer) before issuing drawing commands.

Native interop delegates MUST wrap Skia/Skiko canvas draw passes with thread-safe guards verifying the native surface handle's validity (e.g., ANativeWindow, CAMetalLayer) before issuing drawing commands.

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

Components NEVER dispatch asynchronous background work to a Skiko Skia context after the backing surface has triggered its detachment hook.

Components NEVER dispatch asynchronous background work to a Skiko Skia context after the backing surface has triggered its detachment hook.

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 Kotlin Multiplatform Compose Surface Lifecycle and Skiko Hardware Context Sync critical for AI coding assistants?

Enforces strict ordering and lifecycle synchronization between UI surface transitions and underlying Skiko GPU contexts (Vulkan on Android, Metal on iOS). Prevents deadlocks, GPU context abandonment, and SIGSEGV memory faults when an application transitions between foreground, background, and configuration changes during Compose multiplatform rendering. 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 (Accessing or reallocating Skiko render targets asynchronously during platform surface invalidation or backgrounding) 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

Kotlin