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

Compose Multiplatform Skiko GPU Surface Lifecycle Fencing

Enforces synchronous decoupling and fencing of Skiko hardware rendering surfaces prior to native platform surface destruction. Eliminates fatal Vulkan (VK_ERROR_SURFACE_LOST_KHR) and Metal context invalidation crashes triggered during Android 15 predictive back gesture transitions and iOS app-switcher backgrounding.

Prohibited Anti-Pattern: Implicit Asynchronous Surface Tear-Down Without Deterministic Skiko GPU Render Fencing

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Compose Multiplatform Skiko GPU Surface Lifecycle Fencing - Enforces synchronous decoupling and fencing of Skiko hardware rendering surfaces prior to native platform surface destruction. Eliminates fatal Vulkan (VK_ERROR_SURFACE_LOST_KHR) and Metal context invalidation crashes triggered during Android 15 predictive back gesture transitions and iOS app-switcher backgrounding.
globs: **/src/androidMain/**/*.kt, **/src/iosMain/**/*.kt, **/src/commonMain/**/ui/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/cmp-skiko-gpu-surface-lifecycle-fencing"
---

# Compose Multiplatform Skiko GPU Surface Lifecycle Fencing

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Implicit Asynchronous Surface Tear-Down Without Deterministic Skiko GPU Render Fencing

## System Constraints
You are a Principal Mobile Architect enforcing GPU surface lifecycle boundaries in Compose Multiplatform (CMP) across Metal and Vulkan. You mandate strict, synchronous render decoupling barriers during window transitions, predictive back events (Android 15), and app-switcher suspensions (iOS). Disallow asynchronous or uncoordinated teardowns that result in surface destruction while Skiko command buffers remain inflight.

## Strict Guidelines
- Engineers MUST ALWAYS synchronously detach the SkiaLayer or Skiko render delegate before native surface destruction callbacks (e.g., surfaceDestroyed on Android, viewDidDisappear/applicationDidEnterBackground on iOS) complete execution.
- Engineers MUST NOT dispatch render passes or swapchain frame acquisitions when the host SurfaceHolder or CAMetalLayer is marked invalid, invisible, or entering predictive back cancellation.
- Code SHOULD intercept Android 15 predictive back gesture callbacks via OnBackAnimationCallback and fence the Skiko render loop, transitioning the composition to a cached snapshot or idle state during the progress phase.
- Services MUST flush all inflight GPU command buffers and invoke deterministic context invalidation before releasing native window handles.
- Components NEVER assume the native GPU window remains valid across lifecycle pause events; all native rendering targets must be guarded by an active atomic surface-availability fence.

## Bad vs Good Implementations
### Android 15 SurfaceView & Predictive Back Skiko Lifecycle Fencing

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: When surfaceDestroyed returns without synchronously halting Skiko's Vulkan swapchain presentation, Choreographer frame callbacks dispatch drawing operations against an already-destroyed ANativeWindow. This causes VK_ERROR_SURFACE_LOST_KHR or fatal SIGSEGV crashes during Android 15 predictive back gestures.
```kotlin
// BAD: Asynchronous surface destruction allows Skiko to issue draw calls against a dead SurfaceView
class UnsafeSkikoView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
    private val skiaLayer = SkiaLayer()

    init {
        holder.addCallback(this)
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        skiaLayer.attachTo(holder)
        skiaLayer.needRedraw()
    }

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

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // Defect: Launching cleanup asynchronously or failing to block Choreographer
        CoroutineScope(Dispatchers.Default).launch {
            skiaLayer.detach()
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: By utilizing an atomic availability fence and invoking skiaLayer.detach() synchronously within surfaceDestroyed on the main thread, all pending Skiko render passes are fenced and terminated before Android recycles the underlying ANativeWindow.
```kotlin
// GOOD: Deterministic, synchronous rendering fence that drains and blocks Skiko frame submission
class FencedSkikoSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
    private val isSurfaceValid = AtomicBoolean(false)
    private val skiaLayer = SkiaLayer()

    init {
        holder.addCallback(this)
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        isSurfaceValid.set(true)
        skiaLayer.attachTo(holder)
        skiaLayer.needRedraw()
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        if (isSurfaceValid.get()) {
            skiaLayer.needRedraw()
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // Synchronously close the fence barrier
        isSurfaceValid.set(false)
        // Block Choreographer and flush pending GPU commands before returning
        runBlocking(Dispatchers.Main.immediate) {
            skiaLayer.detach()
            // Invalidate Metal/Vulkan context references synchronously
            skiaLayer.view = null
        }
    }
}
```

### iOS Metal Surface Fencing on App-Switcher / Background Transition

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Failing to suspend the Skiko render loop and decouple the CAMetalLayer before applicationDidEnterBackground results in iOS watchdog terminates (0x8badf00d) or Metal layer validation errors when the app switcher snapshots the view.
```kotlin
// BAD: iOS Skiko metal surface remains attached while CAMetalLayer enters background
fun initIosViewController(skikoUIView: SkikoUIView): UIViewController {
    return object : UIViewController(null, null) {
        override fun viewDidLoad() {
            super.viewDidLoad()
            view.addSubview(skikoUIView)
        }
        // Defect: Fails to decouple Skiko delegate on scene phase transitions
    }
}
```

#### Production Standard (Correct)
> **Standard**: Synchronously listening for UIApplicationWillResignActiveNotification and triggering detachRenderDelegate() ensures the Metal draw pipeline pauses before the iOS compositor unbinds the render buffer during app-switcher transitions.
```kotlin
// GOOD: Explicitly fencing Metal display link across view and application state changes
fun initFencedIosViewController(skikoUIView: SkikoUIView): UIViewController {
    return object : UIViewController(null, null) {
        private var isRenderActive = true

        override fun viewDidLoad() {
            super.viewDidLoad()
            view.addSubview(skikoUIView)
            NSNotificationCenter.defaultCenter.addObserverForName(
                name = UIApplicationWillResignActiveNotification,
                `object` = null,
                queue = NSOperationQueue.mainQueue
            ) { _ ->
                if (isRenderActive) {
                    skikoUIView.detachRenderDelegate()
                    isRenderActive = false
                }
            }
            NSNotificationCenter.defaultCenter.addObserverForName(
                name = UIApplicationDidBecomeActiveNotification,
                `object` = null,
                queue = NSOperationQueue.mainQueue
            ) { _ ->
                if (!isRenderActive) {
                    skikoUIView.attachRenderDelegate()
                    isRenderActive = true
                }
            }
        }

        override fun viewWillDisappear(animated: Boolean) {
            super.viewWillDisappear(animated)
            if (isRenderActive) {
                skikoUIView.detachRenderDelegate()
                isRenderActive = false
            }
        }
    }
}
```

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: Compose Multiplatform Skiko GPU Surface Lifecycle Fencing

Compose Multiplatform Skiko GPU Surface Lifecycle Fencing defines an industrial-grade behavioral standard for mobile engineering. Enforces synchronous decoupling and fencing of Skiko hardware rendering surfaces prior to native platform surface destruction. Eliminates fatal Vulkan (VK_ERROR_SURFACE_LOST_KHR) and Metal context invalidation crashes triggered during Android 15 predictive back gesture transitions and iOS app-switcher backgrounding. 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: Implicit Asynchronous Surface Tear-Down Without Deterministic Skiko GPU Render Fencing 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 DriftImplicit Asynchronous Surface Tear-Down Without Deterministic Skiko GPU Render FencingCritical
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 synchronously detach the SkiaLayer or Skiko render delegate before native surface destruction callbacks (e.g., surfaceDestroyed on Android, viewDidDisappear/applicationDidEnterBackground on iOS) complete execution.

Engineers MUST ALWAYS synchronously detach the SkiaLayer or Skiko render delegate before native surface destruction callbacks (e.g., surfaceDestroyed on Android, viewDidDisappear/applicationDidEnterBackground on iOS) complete execution.

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 dispatch render passes or swapchain frame acquisitions when the host SurfaceHolder or CAMetalLayer is marked invalid, invisible, or entering predictive back cancellation.

Engineers MUST NOT dispatch render passes or swapchain frame acquisitions when the host SurfaceHolder or CAMetalLayer is marked invalid, invisible, or entering predictive back cancellation.

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 intercept Android 15 predictive back gesture callbacks via OnBackAnimationCallback and fence the Skiko render loop, transitioning the composition to a cached snapshot or idle state during the progress phase.

Code SHOULD intercept Android 15 predictive back gesture callbacks via OnBackAnimationCallback and fence the Skiko render loop, transitioning the composition to a cached snapshot or idle state during the progress phase.

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

Services MUST flush all inflight GPU command buffers and invoke deterministic context invalidation before releasing native window handles.

Services MUST flush all inflight GPU command buffers and invoke deterministic context invalidation before releasing native window handles.

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 assume the native GPU window remains valid across lifecycle pause events

Components NEVER assume the native GPU window remains valid across lifecycle pause events; all native rendering targets must be guarded by an active atomic surface-availability fence.

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 Compose Multiplatform Skiko GPU Surface Lifecycle Fencing critical for AI coding assistants?

Enforces synchronous decoupling and fencing of Skiko hardware rendering surfaces prior to native platform surface destruction. Eliminates fatal Vulkan (VK_ERROR_SURFACE_LOST_KHR) and Metal context invalidation crashes triggered during Android 15 predictive back gesture transitions and iOS app-switcher backgrounding. 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 (Implicit Asynchronous Surface Tear-Down Without Deterministic Skiko GPU Render Fencing) 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

Compose