Le toolkit WebGPU¶
Le toolkit est la couche optionnelle par-dessus les bindings WebGPU : il prend en charge le cycle de vie des sessions, la présentation et le contrat d’extension qui permet à un dépôt de binding de fournir les mêmes helpers. Ce guide présente les coordonnées des modules, les points d’entrée de session et de présentation, le contrat d’extension et la validation d’une extension avec le kit de contrat.
Ajouter un module¶
Les coordonnées publiées utilisent le groupe org.graphiks.webgpu. Choisissez les modules nécessaires :
dependencies {
implementation("org.graphiks.webgpu:toolkit-core:<version>")
// Contrats de présentation et de fenêtrage.
// implementation("org.graphiks.webgpu:toolkit-surface:<version>")
// Extension navigateur présentant vers des canvas HTML.
// implementation("org.graphiks.webgpu:toolkit-web:<version>")
// Kit de contrat de protocole.
// implementation("org.graphiks.webgpu:toolkit-tck:<version>")
}
Remplacez <version> par une version release ou snapshot disponible. La valeur de développement
du dépôt est 0.1.0-SNAPSHOT ; elle ne signifie pas qu’une publication existe déjà.
webgpu-toolkit-core, webgpu-toolkit-surface et webgpu-toolkit-tck suivent la matrice de
cibles commune (JVM, Android, JS, Wasm JS et les cibles Kotlin/Native configurées) ;
webgpu-toolkit-web configure JS et Wasm JS. Les points d’entrée se trouvent dans
org.graphiks.webgpu.toolkit, les helpers de présentation dans
org.graphiks.webgpu.toolkit.surface, l’extension navigateur dans
org.graphiks.webgpu.toolkit.web et le kit de contrat dans org.graphiks.webgpu.toolkit.tck.
Ouvrir une session headless¶
withSession ouvre une session via une extension, exécute le bloc avec le périphérique actif et
arrête tout sur chaque chemin de sortie, y compris l’annulation de l’appelant :
import org.graphiks.webgpu.GPUDevice
import org.graphiks.webgpu.GPUQueue
import org.graphiks.webgpu.toolkit.WebGpuToolkitExtension
import org.graphiks.webgpu.toolkit.withSession
suspend fun <R> headless(
extension: WebGpuToolkitExtension,
block: suspend (GPUDevice, GPUQueue) -> R,
): R =
withSession(extension) { session ->
session.state.value // `Ready` : le périphérique est actif et admet du travail
block(
session.device, // emprunté : la session le ferme, les appelants jamais
session.queue, // la file principale de session.device
)
}
Le démarrage est borné par un délai et chaque échec passe par l’état Failed avant d’être
relevé. session.state publie le cycle de vie au fil de sa progression : Ready, puis
ShuttingDown pendant la libération des handles, jusqu’à Closed ; une perte de périphérique
publie Lost et annule le travail admis avec DeviceLostException. Une session dont l’arrêt
dépasse son délai publie ShutdownTimedOut pendant que le nettoyage continue ; un nouvel appel
à shutdown() retente la libération.
Présenter vers une cible¶
withSurface exécute le protocole de lease de cible — retenir une lease, demander un adaptateur
lié à la cible, négocier la configuration de surface depuis les capacités — et remet au bloc la
session et la surface obtenue :
import org.graphiks.webgpu.toolkit.surface.withSurface
import org.graphiks.webgpu.toolkit.web.CanvasSurfaceTarget
import org.graphiks.webgpu.toolkit.web.webToolkit
suspend fun presentToCanvas(canvas: HTMLCanvasElement) {
withSurface(webToolkit(), CanvasSurfaceTarget(canvas)) { session, surface ->
surface.configuration // StateFlow<PresentationConfiguration?> : format et mode alpha négociés
surface.withFrame { frame ->
// rendre dans frame.view à la taille frame.extent avec session.device
frame.present() // optionnel : withFrame présente au retour normal
}
}
}
withFrame suspend tant que la cible n’est pas dessinable — indisponible ou d’étendue nulle — et
libère la frame sur chaque chemin de sortie ; un bloc qui retourne normalement voit sa frame
présentée exactement une fois sauf présentation explicite, et un second present() échoue. Sur le
web, webToolkit() sert la façade de l’implémentation navigateur et CanvasSurfaceTarget(canvas)
enveloppe un canvas du type org.graphiks.webgpu.browser.HTMLCanvasElement : la cible observe le
canvas avec un ResizeObserver et republie son étendue — en changeant de génération — quand le
canvas est redimensionné, si bien que le flux state émet réellement et qu’un withFrame suspendu
reprend ; quand le navigateur n’offre pas de ResizeObserver, chaque lecture du state de la cible
rafraîchit l’étendue à la place (le repli par tirage). Un changement d’étendue incrémente néanmoins
la génération, ce qui tue en v1 les surfaces louées contre l’ancienne : leur prochain usage échoue
immédiatement, et une nouvelle passe withSurface négocie une nouvelle surface. L’étendue publiée
est la taille du tampon de dessin du canvas — des pixels physiques : la cible ne redimensionne
jamais le canvas, donc pour un rendu net sur écrans denses, l’application le dimensionne avec le
rapport de densité de pixels (canvas.width = largeurCss * devicePixelRatio, et la hauteur
correspondante). La validation
navigateur de l’extension web arrive avec la démo toolkit dans un changement futur ;
aujourd’hui l’extension est couverte par les tests unitaires de son module.
Ouvrir une fenêtre¶
Sur les cibles bureau, un fournisseur WindowHost ouvre des Window neutres vis-à-vis du
fenêtrage. withWindow compose la durée de vie de la fenêtre, la session et la surface négociée :
import org.graphiks.webgpu.toolkit.surface.WindowConfig
import org.graphiks.webgpu.toolkit.surface.withWindow
suspend fun runInWindow(extension: WebGpuToolkitExtension, host: WindowHost) {
withWindow(extension, host, WindowConfig(title = "toolkit")) { session, window, surface ->
window.events // demandes de fermeture et redimensionnements publiés par la fenêtre
surface.withFrame { frame ->
// rendre dans frame.view à la taille frame.extent
frame.present()
}
}
}
Ce dépôt définit uniquement le contrat de fenêtrage : la manière d’ouvrir concrètement une
fenêtre relève de la discrétion de chaque extension native — les hôtes sont des artefacts
fournisseurs publiés là où vit leur extension, jamais ici. Quand l’hôte possède ses
fenêtres (WindowRuntime.Owned), withWindow détruit la fenêtre en sortie ; une fenêtre empruntée
(WindowRuntime.Borrowed) reste ouverte, sa durée de vie appartenant à l’embedder qui l’a prêtée.
Le contrat d’extension¶
Une extension est tout ce que fournit un backend ; le contrat WebGpuToolkitExtension tient en
cinq membres :
idnomme le binding servi par l’extension, tel que"web".open(config)démarre uneToolkitInstance, la fabrique d’adaptateurs et de surfaces de la session.requestAdapter(hints)acquiert un adaptateur ;requestAdapter(forTarget, hints)en acquiert un capable de présenter vers la cible retenue exacte.createSurface(adapter, lease)crée le handle de surface d’une cible retenue : il publie ses capacités et configure, acquiert, présente et libère les frames.progressionetpumpEventsdisent comment les événements de l’hôte avancent : les backendsPumpedsont pompés par la session, les backendsEventLoopsuivent la boucle de l’hôte (la progressionManualest refusée en v1).
Où vivent les adaptateurs¶
Le toolkit embarque le contrat et l’extension web. Les dépôts dawn4k et wgpu4k publient leurs
extensions toolkit aux côtés de leurs bindings ; ce dépôt ne fournit que l’extension web
(webToolkit() dans webgpu-toolkit-web). Les hôtes de fenêtrage suivent le même partage :
rien ici ne nomme ni ne prescrit un système de fenêtrage — chaque extension native ouvre ses
fenêtres comme elle l’entend et publie son artefact d’hôte à ses côtés.
Valider une extension avec la TCK¶
webgpu-toolkit-tck exécute les cas de protocole que toute extension doit respecter.
ProtocolTck.runAll(extension, target) renvoie un TckReport dont la liste failures
porte une ligne par cas violé au lieu de lever à la première erreur, afin qu’un dépôt de binding
l’exécute contre sa propre extension dans ses tests et voie toutes les violations d’un coup.
Le SimulatedToolkitExtension du kit est la contrepartie scriptable qui prouve les chemins
stricts dans ce dépôt (parcours de cycle de vie, arrêt idempotent, présentation unique, suspension
à étendue nulle, repli de lease périmée, perte vers Lost) ; les cas qui demandent du scripting
s’omettent pour les fixtures incapables de les scripter, si bien qu’un rapport vide certifie les
cas exécutés. Les cas GPU réels — présenter via une pile graphique vivante — sont pour la v2.
Voir Architecture pour les frontières des modules et Démarrer pour l’API navigateur sous-jacente.