Vireo RHI est une bibliothèque C++ open-source qui implémente une couche d'abstraction commune au-dessus des APIs graphiques modernes avec pour but d'offrir une interface bas-niveau, haute performance, qui masque le boilerplate de Vulkan et DirectX 12 sans sacrifier le contrôle explicite sur le GPU.
Vireo n'est pas un moteur de rendu. C'est une bibliothèque destinée à être utilisée comme sous-module ou dépendence d'un moteur ou d'une application, comme c'est le cas de Lysa Engine. Les fonctionnalités sont ajoutées au fur et à mesure des besoins des projets dépendants.
Deux backends sont actuellement implémentés :
Vireo s'appui sur les modules C++23 (.ixx) plutôt que sur les traditionnels fichiers d'en-tête.
Cela accélère la compilation, réduit la pollution des namespaces et rend les dépendances internes parfaitement explicites.
Le compilateur minimum requis est MSVC 19+ ou LLVM/MingW 21+ sur Windows, et LLVM 21+ sur Linux.
L'architecture de Vireo repose sur un empilement de couches : le code applicatif n'interagit qu'avec la couche RHI abstraite, qui délègue au backend Vulkan ou DirectX 12 sélectionné à l'exécution.
La sélection du backend se fait à l'instanciation de l'object Vireo
La création de l'objet principal se fait via la méthode statique Vireo::create() qui reçoit
un object vireo::BackendConfiguration
configuration. Le type de backend est passé sous forme d'énumération :
import vireo;
auto config = vireo::BackendConfiguration{};
config.backend = vireo::Backend::VULKAN; // ou DIRECTX
auto vireoInstance = vireo::Vireo::create(config);
La méthode statique Vireo::isBackendSupported()
permet de tester à l'exécution si un backend donné est disponible au moment de l'exécution.
Chaque backend est organisé en un ensemble de modules implémentant les mêmes domaines fonctionnels :
Vireo.ixxInterface abstraiteVKVireo / DXVireoImplémentation*DevicesInstance, GPU*ResourcesBuffers, Images*PipelinesGraphique/Compute*CommandsCommandListToutes ces classes sont des interfaces abstraites pures dont les implémentations concrètes
vivent dans les namespaces internes des backends (VK* / DX*).
L'application ne manipule que des std::shared_ptr
vers les interfaces abstraites.
Vireo distingue précisément la localisation mémoire selon le type de buffer.
Les buffers VERTEX
et INDEX
résident en mémoire device-only (VRAM pure, inaccessible au CPU),
tandis que les buffers UNIFORM et les buffers de
transfert résident en mémoire host-accessible.
Pour charger des données en VRAM, la méthode
CommandList::upload() gère automatiquement
la création d'un staging buffer temporaire :
// Création d'un vertex buffer en device-only memory
auto vertexBuffer = vireo->createBuffer(
vireo::BufferType::VERTEX,
sizeof(Vertex),
vertices.size());
// Upload via staging buffer automatique
cmdList->begin();
cmdList->upload(vertexBuffer, vertices.data());
cmdList->end();
transferQueue->submit({cmdList});
transferQueue->waitIdle();
Les images supportent un large éventail de formats : R8 à R32G32B32A32 en entier/float,
les formats depth/stencil D16, D24_UNORM_S8, D32_SFLOAT, et les 14 formats de compression BCn (BC1 à BC7). Les images read-only, read/write et render target se créent avec des méthodes dédiées sur l'objet Vireo.
Le système de descripteurs de Vireo suit le modèle Vulkan : on déclare d'abord un
DescriptorLayout qui décrit
quelles ressources sont accessibles (uniform, image, sampler…), puis on crée un
DescriptorSet qui lie les ressources
concrètes à ce layout, et enfin on groupe les layouts dans un
PipelineResources
(équivalent du Root Signature DirectX ou du Pipeline Layout Vulkan).
// Déclaration du layout
auto layout = vireo->createDescriptorLayout();
layout->add(BINDING_GLOBAL, vireo::DescriptorType::UNIFORM);
layout->add(BINDING_TEXTURES, vireo::DescriptorType::SAMPLED_IMAGE, texCount);
layout->build();
// Création et alimentation du descriptor set
auto descSet = vireo->createDescriptorSet(layout);
descSet->update(BINDING_GLOBAL, globalUniform);
descSet->update(BINDING_TEXTURES, textures);
Vireo supporte les push constants pour les petites données fréquemment mises à jour, les dynamic uniforms pour indexer dans un tableau d'uniformes sans recrér de descriptor set, et les read/write images (UAV) pour les compute shaders.
Les CommandList sont créées à
partir d'un CommandAllocator.
Il n'existe pas d'objet render pass au sens Vulkan classique :
une passe de rendu est simplement une séquence de commandes
délimitée par beginRendering() / endRendering(),
avec des pipeline barriers explicites aux transitions d'état des ressources.
// Boucle de rendu typique
frame.commandAllocator->reset();
auto cmd = frame.commandList;
cmd->begin();
// Transition : UNDEFINED → RENDER_TARGET_COLOR
cmd->barrier(swapChain,
vireo::ResourceState::UNDEFINED,
vireo::ResourceState::RENDER_TARGET_COLOR);
cmd->beginRendering(renderingConfig);
cmd->bindPipeline(pipeline);
cmd->bindDescriptor(pipeline, descSet, SET_GLOBAL);
cmd->setViewport(viewport);
cmd->setScissor(scissor);
cmd->bindVertexBuffer(vertexBuffer);
cmd->draw(3);
cmd->endRendering();
// Transition : RENDER_TARGET_COLOR → PRESENT
cmd->barrier(swapChain,
vireo::ResourceState::RENDER_TARGET_COLOR,
vireo::ResourceState::PRESENT);
cmd->end();
graphicQueue->submit(frame.inFlightFence, swapChain, {cmd});
swapChain->present();
swapChain->nextFrameIndex();
La SwapChain est le pont entre le GPU et les fenêtres système.
Elle gère le double ou triple buffering et expose deux modes de présentation :
IMMEDIATE (peut produire du tearing) et
VSYNC.
La synchronisation GPU/GPU entre le rendu et la présentation est gérée en interne par la SwapChain
et la SubmitQueue pour assurer la portabilité entre les APIs.
Vireo expose deux types de pipelines.
Le GraphicPipeline encapsule l'intégralité de l'état GPU nécessaire au rendu rasterisé :
formats d'attachement couleur, color blending par attachement, format des vertices (VertexInputLayout),
topology primitives, mode polygone, culling, depth/stencil, MSAA, et les shader modules.
Le ComputePipeline n'associe qu'un seul compute shader à ses ressources.
La compilation d'un pipeline (traduction SPIR-V/DXIL → ISA matérielle) est coûteuse. Il ne faut jamais créer de pipeline à la volée pendant le rendu. Tous les pipelines doivent être créés pendant la phase d'initialisation de l'application.
Vireo recommande l'utilisation du langage Slang, un langage dérivé de HLSL conçu pour la portabilité multi-API. Un unique fichier source .slang est compilé vers les deux formats binaires intermédiaires requis :
shader.slangSource uniqueslangcVulkan SDK.spvSPIR-V (Vulkan).dxilDXIL (DirectX 12)ShaderModuleChargement autoLa convention de nommage des fichiers pilote automatiquement le type de compilation dans le
script CMake fourni avec les example :
.vert.slang pour les vertex shaders, .frag.slang pour les fragment shaders, .comp.slang pour les compute shaders, .hull.slang, .domain.slang et .geom.slang pour les étapes optionnelles de tessellation et géométrie. Un fichier .inc.slang est ignoré (il s'agit d'un include partagé).
À l'exécution, Vireo::createShaderModule() prend un nom de
fichier sans extension. La bibliothèque ajoute automatiquement l'extension correcte selon le backend actif
(.spv ou .dxil), ce qui rend le code applicatif entièrement indépendant du backend.
// Exemple : compute shader d'effet vagues (Slang)
struct Params {
uint2 imageSize;
float time;
};
ConstantBuffer<Params> params : register(b0);
RWTexture2D output : register(u1);
[shader("compute")]
[numthreads(8, 8, 1)]
void main(uint3 id : SV_DispatchThreadID) {
float2 uv = float2(id.xy) / params.imageSize;
// Aucune directive Vulkan ou DirectX spécifique
output[id.xy] = float4(computeColor(uv, params.time), 1.0);
}
La compatibilité SPIR-V pour DirectX 12 est annoncée avec Shader Model 7, ce qui supprimera à terme la nécessité de maintenir deux fichiers binaires distincts.
Vireo expose trois primitives de synchronisation distinctes, couvrant les trois niveaux de coordination nécessaires dans un moteur de rendu moderne :
| Primitive | Portée | Usage typique |
|---|---|---|
Fence |
CPU ↔ GPU | Attendre la fin du rendu d'une frame avant de réutiliser ses ressources |
Semaphore |
GPU ↔ GPU | Synchroniser des render passes distinctes, ou des soumissions en pipeline |
Barrier (cmdList::barrier()) |
GPU interne | Transitions d'état des ressources entre sous-passes dans une même command list |
Le modèle de synchronisation est explicite : il n'y a pas de synchronisation implicite entre passes.
Le développeur doit insérer les barrières aux bons endroits pour les transitions de layout
(UNDEFINED → RENDER_TARGET_COLOR → PRESENT). Cette approche, fidèle à Vulkan et DX12,
maximise les opportunités d'overlap sur le GPU.
Le pattern recommandé pour le rendu multi-frames est d'allouer une Fence
et un CommandAllocator par frame.
La SwapChain::acquire(fence) bloque le CPU jusqu'à ce que l'image soit libre,
puis SubmitQueue::submit(fence, swapChain, {cmd}) signale la fence à la complétion du rendu :
// Données par frame (allouées à l'init)
struct FrameData {
std::shared_ptr<vireo::CommandAllocator> cmdAlloc;
std::shared_ptr<vireo::CommandList> cmdList;
std::shared_ptr<vireo::Fence> inFlightFence;
};
// Render loop
auto& frame = frames[swapChain->getCurrentFrameIndex()];
if (!swapChain->acquire(frame.inFlightFence)) return;
frame.cmdAlloc->reset();
// ... record commands ...
graphicQueue->submit(frame.inFlightFence, swapChain, {frame.cmdList});
swapChain->present();
swapChain->nextFrameIndex();
Vireo RHI est conçu pour être intégré comme sous-module CMake (add_subdirectory)
ou comme sous-module Git. Les dépendances tierces (DX12 headers, SDL, etc.) sont automatiquement récupérées par
CMake via FetchContent, à l'exception du SDK Vulkan qui doit être installé manuellement.
| Plateforme | Compilateur | Notes |
|---|---|---|
| Windows | MSVC 19+ / LLVM+MingW 21+ | DirectX + Vulkan (LLVM : Vulkan uniquement) |
| Linux | LLVM 21+ | Vulkan uniquement, X11 & Wayland via SDL3 |
DIRECTX_BACKEND — Active le backend DirectX 12 (défaut : ON sur Windows)LUA_BINDING — Compile les bindings Lua 5.4+ (défaut : OFF)USE_SDL3 — Utilise SDL3 comme couche d'abstraction fenêtrage (défaut : ON sur Linux)git clone https://github.com/HenriMichelon/vireo_rhi
cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=Release
cmake --build build
Lorsque LUA_BINDING=ON, un module supplémentaire vireo.lua
expose l'intégralité de l'API Vireo via LuaBridge 3.
Cela permet d'écrire la logique haut-niveau d'une application en Lua 5.4+ tout en conservant les performances du runtime C++23
pour le rendu bas-niveau.
La documentation officielle de Vireo RHI est hébergée sur GitHub Pages
Le tutoriel Hello, Triangle ! guide le développeur depuis la création de l'environnement de développement jusqu'au rendu du premier triangle coloré. Il couvre dans l'ordre : la configuration du projet CMake, l'instanciation de l'objet Vireo, la création des queues de soumission, la swap chain, les command allocators et command lists, l'eregistrement des commandes, les viewports, la mise en place des vertex data, la création du pipeline, la compilation et chargement des shaders, la configuration finale du pipeline et les commandes de dessin.
Le dépôt github.com/HenriMichelon/vireo_samples contient une série de programmes d'exemple progressifs, couvrant l'essentiel des fonctionnalités de Vireo. Chaque exemple est autonome, intègre ses propres shaders Slang compilés en SPIR-V et DXIL, et illustre un ou plusieurs concepts supplémentaires par rapport à l'exemple précédent.
Le classique « Hello Triangle » avec un dégradé RGB par vertex. Point de départ absolu : swap chain, command lists, vertex buffer, pipeline graphique, image barriers, fences.
Même triangle avec une texture chargée et appliquée. Introduit les images, les samplers, les descriptor layouts et descriptor sets.
Plusieurs triangles avec transparence et matériau shader-based. Uniform buffers, color blending, push constants, pipelines multiples.
Dessin indirect indexé (drawIndexedIndirect) — le GPU détermine les paramètres de dessin depuis un buffer en VRAM, sans aller-retour CPU.
Anti-aliasing multi-samples. Introduit les render targets custom et le pipeline MSAA avec resolve automatique.
Effet de vagues animées généré entièrement sur GPU via un compute shader Slang. Read/Write images, compute pipeline, copie d'images.
Deux cubes texturés rotatifs, caméra, lumière, skybox. Forward rendering complet avec depth pre-pass, cubemap, semaphores, dynamic uniforms, et post-process (SMAA, FXAA, gamma, voronoï).
Même scène en rendu différé : G-buffers, éclairage deferred, transparence OIT (Order Independent Transparency) pondérée, stencil pour optimisation skybox, push constants en remplacement des dynamic uniforms.
Les samples Cube et Deferred constituent les références les plus complètes : ils couvrent en pratique la quasi-totalité des fonctionnalités de Vireo RHI dans un contexte de rendu réaliste, et leurs shaders Slang illustrent des techniques classiques de rendu (Phong, Gbuffers, SMAA, FXAA, TAA, OIT).