Nucleus
Tao backend

Contexte de rendu GPU

Donnez aux renderers GPU in-process le DirectContext Skia et le device natif de la scène pour composer sans second GPU ni copie par frame.

TaoGpuRenderContext publie le contexte GPU de la surface Compose englobante sur le backend Tao : le DirectContext Skia avec lequel la scène dessine, plus le device natif derrière (Metal sur macOS, ANGLE GLES sur Windows, EGL/GLES natif sur Linux). Un renderer in-process — moteur de carte, viewport 3D, bibliothèque de charting — alloue ses cibles de rendu sur ce même device et laisse Compose les sampler sans second GPU, sans handle partageable et sans copie inter-device par frame.

C'est le complément de TextureView, pas un remplacement. TextureView est le contrat pour les producteurs externes (moteurs hors-process, décodeurs matériels sur leur propre device). Utilisez TaoGpuRenderContext lorsque votre renderer peut créer ses objets GPU sur le contexte de la scène.

Ajouter la dépendance

build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.nucleus-application:2.4.3")
    implementation("dev.nucleusframework:nucleus.decorated-window-tao:2.4.3")
}

Les types vivent dans dev.nucleusframework.window.tao.

Obtenir le contexte

Appelez rememberTaoGpuRenderContext() dans une surface qui possède un contexte GPU (contenu d'un DecoratedWindow, couche popup native, overlay NativeView, ou panneau tray). Il renvoie null pendant le démarrage, le teardown, ou dans un environnement non matériel :

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.withFrameNanos
import dev.nucleusframework.window.tao.TaoGpuRenderContext
import dev.nucleusframework.window.tao.TaoOpenGlRenderContext
import dev.nucleusframework.window.tao.rememberTaoGpuRenderContext

@Composable
fun MapViewport() {
    val renderContext = rememberTaoGpuRenderContext() ?: return
    // Clé l'état du renderer sur l'instance : un contexte reconstruit a une nouvelle identité.
    val renderer = remember(renderContext) { MapEngineRenderer(renderContext) }
    DisposableEffect(renderer) {
        onDispose { renderer.close() }
    }
    LaunchedEffect(renderer) {
        while (true) {
            // Travail GPU à l'intérieur de withFrameNanos — voir Frame pacing ci-dessous.
            withFrameNanos { renderer.renderFrame() }
        }
    }
    // … sampler la sortie du moteur dans Compose
}

Backends

TypePlateformesAPI supplémentaire
TaoMetalRenderContextmacOSmetalDevicePtrid<MTLDevice> emprunté
TaoOpenGlRenderContextLinux, WindowswithContextCurrent { } — lie le contexte GL de la surface

Les deux exposent :

MembreDescription
backend: TaoRenderBackendMETAL ou OPENGL
skiaContext: DirectContextContexte Skia avec lequel la scène peint (emprunté)
runOnGpuThread { }Accès exclusif pour le travail Skia / GPU (Metal bascule sur le thread de rendu ; GL s'exécute en ligne sur le thread de la boucle d'événements après un contrôle de thread)

Sur Windows, OPENGL est ANGLE-on-D3D11 GLES — pas OpenGL desktop. Résolvez les points d'entrée via eglGetProcAddress d'ANGLE, comme Skia pour la scène.

Propriété et durée de vie

Chaque handle est emprunté. Ne le retenez pas, ne le libérez pas, ne le freez pas. Arrêtez d'utiliser un contexte dès que la surface en publie un autre (ou null).

rememberTaoGpuRenderContext() renvoie une nouvelle instance chaque fois que le contexte sous-jacent est reconstruit — détachement de fenêtre, cycle hide/show Wayland (reconstruction complète de la pile EGL), teardown d'un popup ou d'un panneau tray. Clé l'état du renderer sur cette instance :

val renderer = remember(renderContext) { EngineRenderer(renderContext) }
DisposableEffect(renderContext) {
    onDispose {
        renderContext.runOnGpuThread { renderer.releaseGpuResources() }
    }
}

Frame pacing

Émettez le travail GPU à l'intérieur du callback withFrameNanos (ou pendant la composition / layout / draw), pas dans le code qui suit la suspension :

  • Sur Linux, l'hôte confie le contexte EGL à un thread de swap pour le eglSwapBuffers bloquant juste après chaque frame. Une continuation post-frame perd la course et obtient withContextCurrent → null presque à chaque vsync.
  • Le callback de frame s'exécute dans la passe de rendu, pendant que le contexte est attachable.

Lorsque la scène a des imports TextureView actifs ou des consommateurs TaoGpuRenderContext, Tao maintient le VSync pendant la boucle modale move/resize Windows et les bursts de resize Linux, pour que le contenu piloté par l'horloge de frame reste au rythme de l'affichage au lieu de tourner à la vitesse de la pompe d'événements.

TextureView vs contexte de rendu GPU

TextureViewTaoGpuRenderContext
ProducteurExterne / hors-processIn-process, même device
ImportD3D11 partagé / IOSurface / DMA-BUFAllouer sur skiaContext
Usage typiqueDécodeur vidéo, moteur de jeu, caméraMoteurs type MapLibre, viewports 3D, charts

Notes

  • Appelez chaque membre depuis le thread de composition (thread de la boucle d'événements Tao ; thread principal du process sur macOS). Passez le travail GPU par runOnGpuThread / withContextCurrent.
  • withContextCurrent imbriqué est sûr ; le contexte courant précédent est restauré ensuite et le cache d'état GL de Skia est invalidé, donc les consommateurs n'ont rien à faire.
  • Chaque hôte de fenêtre Windows possède un contexte ANGLE GLES non partagé. Les purges du cache GPU sur le chemin de resize lient d'abord ce contexte pour que les textures d'une fenêtre sœur restent intactes. Les surfaces popup / tray macOS et Linux peuvent aussi posséder des contextes privés.

Et ensuite