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

KMP Metal/Vulkan GPU Swapchain Lifecycle Eviction & Surface Drain Standard

Enforces deterministic tearing down and recreation of Skia/Metal and Vulkan rendering surfaces during mobile app backgrounding transitions. Eliminates OS-level Jetsam termination (iOS 0x8badf00d/memory pressure kill) and Android VK_ERROR_SURFACE_LOST_KHR crashes by synchronously draining inflight GPU command buffers and purging GPU-backed swapchains.

Prohibited Anti-Pattern: Retaining active CAMetalLayer/VkSwapchainKHR buffers across lifecycle background transitions

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP Metal/Vulkan GPU Swapchain Lifecycle Eviction & Surface Drain Standard - Enforces deterministic tearing down and recreation of Skia/Metal and Vulkan rendering surfaces during mobile app backgrounding transitions. Eliminates OS-level Jetsam termination (iOS 0x8badf00d/memory pressure kill) and Android VK_ERROR_SURFACE_LOST_KHR crashes by synchronously draining inflight GPU command buffers and purging GPU-backed swapchains.
globs: **/src/iosMain/**/*.kt, **/src/androidMain/**/*.kt, **/src/commonMain/**/*Surface*.kt, **/src/nativeMain/**/*.kt, **/iosApp/**/*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-metal-vulkan-surface-lifecycle-drain-standard"
---

# KMP Metal/Vulkan GPU Swapchain Lifecycle Eviction & Surface Drain Standard

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Retaining active CAMetalLayer/VkSwapchainKHR buffers across lifecycle background transitions

## System Constraints
You are a Principal Mobile Graphics Architect enforcing zero-tolerance GPU resource retention policies across Kotlin Multiplatform (KMP) runtimes on iOS (Metal) and Android (Vulkan/Skia). All graphics pipelines, Skia DirectContexts, and native surface layers MUST release their hardware-bound framebuffers when transitioning to background states.

## Strict Guidelines
- Engineers MUST ALWAYS register platform lifecycle hooks (UIApplication.didEnterBackgroundNotification / Android Lifecycle.Event.ON_STOP) to trigger synchronous GPU pipeline drains.
- Engineers MUST synchronously wait for inflight command buffers via MTLCommandBuffer.waitUntilCompleted() or vkQueueWaitIdle() before releasing native swapchain surfaces.
- Code MUST explicitly evict the Skia DirectContext resource cache using DirectContext.freeGpuResources() or DirectContext.purgeUnlockedResources(true) upon backgrounding.
- Engineers MUST NOT attempt to schedule new draw passes, present CAMetalDrawable, or invoke vkQueuePresentKHR after background transition callbacks initiate.
- Services MUST isolate swapchain recreation behind a lazily evaluated surface state machine that reconstructs GPU targets strictly after application resume signals.
- Components NEVER hold strong references to CAMetalLayer, ANativeWindow, or SkSurface instances within persistent singleton scopes.

## Bad vs Good Implementations
### Metal/Skia Lifecycle Surface Handler in KMP iOS Target

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Failing to flush inflight Metal command buffers and retaining allocated CAMetalDrawable targets upon entering background triggers iOS Jetsam watchdog termination or Mach port allocation traps.
```kotlin
// BAD: Retains CAMetalLayer and active Skia render target while entering background
class MetalSurfaceRenderer(
    private val metalLayer: CAMetalLayer,
    private val skiaContext: DirectContext
) {
    private var isRendering = true

    fun onBackground() {
        // Defective: Leaves inflight command buffers pending and maintains drawable cache
        isRendering = false
        // DirectContext is retained; iOS Jetsam kills the app due to GPU memory quota violations
    }

    fun render() {
        if (!isRendering) return
        val drawable = metalLayer.nextDrawable() ?: return
        // Draws to drawable without lifecycle bounds checks
    }
}
```

#### Production Standard (Correct)
> **Standard**: Flushes and synchronizes inflight work with syncCpu=true, purges Skia context allocations with freeGpuResources(), and sets contents to null while dropping layer references, eliminating Jetsam background kills.
```kotlin
// GOOD: Deterministically drains GPU commands, purges Skia caches, and detaches surface
class MetalSurfaceRenderer(
    private var metalLayer: CAMetalLayer?,
    private val skiaContext: DirectContext
) {
    private var isSurfaceAttached = true
    private val renderLock = kotlinx.atomicfu.locks.ReentrantLock()

    fun onBackground() {
        renderLock.withLock {
            isSurfaceAttached = false
            // 1. Evict Skia GPU texture allocations
            skiaContext.flushAndSubmit(syncCpu = true)
            skiaContext.freeGpuResources()
            
            // 2. Clear CAMetalLayer framebuffers to yield hardware backing to OS
            metalLayer?.contents = null
            metalLayer = null
        }
    }

    fun onForeground(newMetalLayer: CAMetalLayer) {
        renderLock.withLock {
            metalLayer = newMetalLayer
            skiaContext.resetContext()
            isSurfaceAttached = true
        }
    }

    fun render() {
        renderLock.withLock {
            if (!isSurfaceAttached) return
            val layer = metalLayer ?: return
            val drawable = layer.nextDrawable() ?: return
            // Render safely with active foreground surface target
        }
    }
}
```

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: KMP Metal/Vulkan GPU Swapchain Lifecycle Eviction & Surface Drain Standard

KMP Metal/Vulkan GPU Swapchain Lifecycle Eviction & Surface Drain Standard defines an industrial-grade behavioral standard for mobile engineering. Enforces deterministic tearing down and recreation of Skia/Metal and Vulkan rendering surfaces during mobile app backgrounding transitions. Eliminates OS-level Jetsam termination (iOS 0x8badf00d/memory pressure kill) and Android VK_ERROR_SURFACE_LOST_KHR crashes by synchronously draining inflight GPU command buffers and purging GPU-backed swapchains. 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: Retaining active CAMetalLayer/VkSwapchainKHR buffers across lifecycle background transitions 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 DriftRetaining active CAMetalLayer/VkSwapchainKHR buffers across lifecycle background transitionsCritical
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 register platform lifecycle hooks (UIApplication.didEnterBackgroundNotification / Android Lifecycle.Event.ON_STOP) to trigger synchronous GPU pipeline drains.

Engineers MUST ALWAYS register platform lifecycle hooks (UIApplication.didEnterBackgroundNotification / Android Lifecycle.Event.ON_STOP) to trigger synchronous GPU pipeline drains.

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 synchronously wait for inflight command buffers via MTLCommandBuffer.waitUntilCompleted() or vkQueueWaitIdle() before releasing native swapchain surfaces.

Engineers MUST synchronously wait for inflight command buffers via MTLCommandBuffer.waitUntilCompleted() or vkQueueWaitIdle() before releasing native swapchain surfaces.

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 explicitly evict the Skia DirectContext resource cache using DirectContext.freeGpuResources() or DirectContext.purgeUnlockedResources(true) upon backgrounding.

Code MUST explicitly evict the Skia DirectContext resource cache using DirectContext.freeGpuResources() or DirectContext.purgeUnlockedResources(true) upon backgrounding.

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 attempt to schedule new draw passes, present CAMetalDrawable, or invoke vkQueuePresentKHR after background transition callbacks initiate.

Engineers MUST NOT attempt to schedule new draw passes, present CAMetalDrawable, or invoke vkQueuePresentKHR after background transition callbacks initiate.

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

Services MUST isolate swapchain recreation behind a lazily evaluated surface state machine that reconstructs GPU targets strictly after application resume signals.

Services MUST isolate swapchain recreation behind a lazily evaluated surface state machine that reconstructs GPU targets strictly after application resume signals.

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 hold strong references to CAMetalLayer, ANativeWindow, or SkSurface instances within persistent singleton scopes.

Components NEVER hold strong references to CAMetalLayer, ANativeWindow, or SkSurface instances within persistent singleton scopes.

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 Metal/Vulkan GPU Swapchain Lifecycle Eviction & Surface Drain Standard critical for AI coding assistants?

Enforces deterministic tearing down and recreation of Skia/Metal and Vulkan rendering surfaces during mobile app backgrounding transitions. Eliminates OS-level Jetsam termination (iOS 0x8badf00d/memory pressure kill) and Android VK_ERROR_SURFACE_LOST_KHR crashes by synchronously draining inflight GPU command buffers and purging GPU-backed swapchains. 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 (Retaining active CAMetalLayer/VkSwapchainKHR buffers across lifecycle background transitions) 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