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

KMP Compose Multiplatform Texture Memory Lifecycle Guardrail

Enforces deterministic lifecycle management and cleanup for Skia/Skiko native graphic textures across iOS Metal and Android Vulkan backends. Prevents unbounded VRAM leaks and graphics pipeline hangs caused by decoupled Kotlin Native/JVM garbage collection during rapid view composition and navigation unmounts.

Prohibited Anti-Pattern: Retaining Raw Skia/Platform Textures in Composition Without Deterministic Native Deallocation

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP Compose Multiplatform Texture Memory Lifecycle Guardrail - Enforces deterministic lifecycle management and cleanup for Skia/Skiko native graphic textures across iOS Metal and Android Vulkan backends. Prevents unbounded VRAM leaks and graphics pipeline hangs caused by decoupled Kotlin Native/JVM garbage collection during rapid view composition and navigation unmounts.
globs: **/commonMain/**/*.kt, **/iosMain/**/*.kt, **/androidMain/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-compose-texture-memory-lifecycle-guardrail"
---

# KMP Compose Multiplatform Texture Memory Lifecycle Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Retaining Raw Skia/Platform Textures in Composition Without Deterministic Native Deallocation

## System Constraints
You are a Principal Mobile Architect enforcing strict native graphics memory lifecycle guardrails in Kotlin Multiplatform with Compose Multiplatform. You must prevent catastrophic VRAM leaks across iOS Metal and Android Vulkan backends by ensuring every GPU-backed surface, Skia Image, and native texture allocation is bound to Compose lifecycle nodes via deterministic DisposableEffect scopes and explicit close() invocations.

## Strict Guidelines
- Engineers MUST ALWAYS bind Skia/Skiko native images (`org.jetbrains.skia.Image`, `Bitmap`, or `Surface`) directly to a `DisposableEffect` to trigger `.close()` deterministically on composition departure.
- Engineers MUST NOT rely on Kotlin/Native ARC or JVM garbage collection to release GPU-resident VRAM buffers or Metal/Vulkan command-queue resources.
- Components NEVER store persistent native hardware texture references in plain state containers (`remember { mutableStateOf(...) }`) without lifecycle-aware wrapping or a dedicated texture cache pool.
- Code SHOULD leverage native render pool allocators for transient texture manipulations to prevent device swap-chain thrashing and command buffer exhaustion during high-frequency recomposition.
- Services MUST explicitly call `GrDirectContext.performDeferredCleanup()` or platform equivalent when releasing large composite canvas scenes during screen navigation events.

## Bad vs Good Implementations
### Deterministic Skia Texture Lifecycle Disposal

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: SkiaImage allocates GPU-resident texture buffers (MTLTexture on iOS, VkImage on Android). The Compose remember block caches the reference, but when the Composable exits the composition graph, the native graphics memory remains allocated until the platform GC/ARC finalizer runs, causing massive VRAM spikes and OOM panics during rapid navigation.
```kotlin
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.foundation.Image
import org.jetbrains.skia.Image as SkiaImage
import androidx.compose.ui.graphics.asComposeImageBitmap

@Composable
fun LeakyHardwareTextureView(textureBytes: ByteArray) {
    // ANTI-PATTERN: SkiaImage allocates non-heap native GPU textures via Metal/Vulkan.
    // remember without disposable teardown leaves native allocations orphaned
    // during rapid screen pops, triggering high-memory jetpack/iOS kills.
    val nativeImage = remember(textureBytes) {
        SkiaImage.makeFromEncoded(textureBytes)
    }

    Image(
        bitmap = nativeImage.asComposeImageBitmap(),
        contentDescription = null
    )
}
```

#### Production Standard (Correct)
> **Standard**: The native Skia texture allocation is strictly managed within DisposableEffect. When the Composable exits composition (e.g., screen popped from backstack), onDispose immediately executes, invoking .close() to purge the GPU-backed allocations from the Vulkan or Metal device memory context before GC can introduce latency.
```kotlin
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
import androidx.compose.foundation.Image
import org.jetbrains.skia.Image as SkiaImage
import androidx.compose.ui.graphics.asComposeImageBitmap

@Composable
fun SafeHardwareTextureView(textureBytes: ByteArray) {
    val nativeImageState = remember(textureBytes) {
        mutableStateOf<SkiaImage?>(null)
    }

    DisposableEffect(textureBytes) {
        val allocatedImage = SkiaImage.makeFromEncoded(textureBytes)
        nativeImageState.value = allocatedImage

        onDispose {
            // INVARIANT: Deterministically release GPU backend memory immediately upon unmount
            nativeImageState.value = null
            if (!allocatedImage.isClosed) {
                allocatedImage.close()
            }
        }
    }

    nativeImageState.value?.let { validImage ->
        Image(
            bitmap = validImage.asComposeImageBitmap(),
            contentDescription = null
        )
    }
}
```

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 Compose Multiplatform Texture Memory Lifecycle Guardrail

KMP Compose Multiplatform Texture Memory Lifecycle Guardrail defines an industrial-grade behavioral standard for mobile engineering. Enforces deterministic lifecycle management and cleanup for Skia/Skiko native graphic textures across iOS Metal and Android Vulkan backends. Prevents unbounded VRAM leaks and graphics pipeline hangs caused by decoupled Kotlin Native/JVM garbage collection during rapid view composition and navigation unmounts. 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 Raw Skia/Platform Textures in Composition Without Deterministic Native Deallocation 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 Raw Skia/Platform Textures in Composition Without Deterministic Native DeallocationCritical
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 bind Skia/Skiko native images (`org.jetbrains.skia.Image`, `Bitmap`, or `Surface`) directly to a `DisposableEffect` to trigger `.close()` deterministically on composition departure.

Engineers MUST ALWAYS bind Skia/Skiko native images (`org.jetbrains.skia.Image`, `Bitmap`, or `Surface`) directly to a `DisposableEffect` to trigger `.close()` deterministically on composition departure.

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 rely on Kotlin/Native ARC or JVM garbage collection to release GPU-resident VRAM buffers or Metal/Vulkan command-queue resources.

Engineers MUST NOT rely on Kotlin/Native ARC or JVM garbage collection to release GPU-resident VRAM buffers or Metal/Vulkan command-queue resources.

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

Components NEVER store persistent native hardware texture references in plain state containers (`remember { mutableStateOf(...) }`) without lifecycle-aware wrapping or a dedicated texture cache pool.

Components NEVER store persistent native hardware texture references in plain state containers (`remember { mutableStateOf(...) }`) without lifecycle-aware wrapping or a dedicated texture cache pool.

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

Code SHOULD leverage native render pool allocators for transient texture manipulations to prevent device swap-chain thrashing and command buffer exhaustion during high-frequency recomposition.

Code SHOULD leverage native render pool allocators for transient texture manipulations to prevent device swap-chain thrashing and command buffer exhaustion during high-frequency recomposition.

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 explicitly call `GrDirectContext.performDeferredCleanup()` or platform equivalent when releasing large composite canvas scenes during screen navigation events.

Services MUST explicitly call `GrDirectContext.performDeferredCleanup()` or platform equivalent when releasing large composite canvas scenes during screen navigation events.

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 Compose Multiplatform Texture Memory Lifecycle Guardrail critical for AI coding assistants?

Enforces deterministic lifecycle management and cleanup for Skia/Skiko native graphic textures across iOS Metal and Android Vulkan backends. Prevents unbounded VRAM leaks and graphics pipeline hangs caused by decoupled Kotlin Native/JVM garbage collection during rapid view composition and navigation unmounts. 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 Raw Skia/Platform Textures in Composition Without Deterministic Native Deallocation) 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