GroveEngine/modules/BgfxRenderer
StillHammer 1b7703f07b feat(IIO)!: BREAKING CHANGE - Callback-based message dispatch
## Breaking Change

IIO API redesigned from manual pull+if-forest to callback dispatch.
All modules must update their subscribe() calls to pass handlers.

### Before (OLD API)
```cpp
io->subscribe("input:mouse");

void process(...) {
    while (io->hasMessages()) {
        auto msg = io->pullMessage();
        if (msg.topic == "input:mouse") {
            handleMouse(msg);
        } else if (msg.topic == "input:keyboard") {
            handleKeyboard(msg);
        }
    }
}
```

### After (NEW API)
```cpp
io->subscribe("input:mouse", [this](const Message& msg) {
    handleMouse(msg);
});

void process(...) {
    while (io->hasMessages()) {
        io->pullAndDispatch();  // Callbacks invoked automatically
    }
}
```

## Changes

**Core API (include/grove/IIO.h)**
- Added: `using MessageHandler = std::function<void(const Message&)>`
- Changed: `subscribe()` now requires `MessageHandler` callback parameter
- Changed: `subscribeLowFreq()` now requires `MessageHandler` callback
- Removed: `pullMessage()`
- Added: `pullAndDispatch()` - pulls and auto-dispatches to handlers

**Implementation (src/IntraIO.cpp)**
- Store callbacks in `Subscription.handler`
- `pullAndDispatch()` matches topic against ALL subscriptions (not just first)
- Fixed: Regex pattern compilation supports both wildcards (*) and regex (.*)
- Performance: ~1000 msg/s throughput (unchanged from before)

**Files Updated**
- 31 test/module files migrated to callback API (via parallel agents)
- 8 documentation files updated (DEVELOPER_GUIDE, USER_GUIDE, module READMEs)

## Bugs Fixed During Migration

1. **pullAndDispatch() early return bug**: Was only calling FIRST matching handler
   - Fix: Loop through ALL subscriptions, invoke all matching handlers

2. **Regex pattern compilation bug**: Pattern "player:.*" failed to match
   - Fix: Detect ".*" in pattern → use as regex, otherwise escape and convert wildcards

## Testing

 test_11_io_system: PASSED (IIO pub/sub, pattern matching, batching)
 test_threaded_module_system: 6/6 PASSED
 test_threaded_stress: 5/5 PASSED (50 modules, 100x reload, concurrent ops)
 test_12_datanode: PASSED
 10 TopicTree scenarios: 10/10 PASSED
 benchmark_e2e: ~1000 msg/s throughput

Total: 23+ tests passing

## Performance Impact

No performance regression from callback dispatch:
- IIO throughput: ~1000 msg/s (same as before)
- ThreadedModuleSystem: Speedup ~1.0x (barrier pattern expected)

## Migration Guide

For all modules using IIO:

1. Update subscribe() calls to include handler lambda
2. Replace pullMessage() loops with pullAndDispatch()
3. Move topic-specific logic from if-forest into callbacks

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 14:19:27 +07:00
..
Debug feat(BgfxRenderer): Add debug overlay with FPS and stats display 2025-11-27 21:23:29 +08:00
Frame fix: Resolve bgfx Frame 1 crash on Windows DLL + MinGW GCC 15 compatibility 2025-12-30 11:03:06 +07:00
Passes fix: Multi-texture sprite rendering - setState per batch + transient buffers 2026-01-14 14:05:56 +07:00
RenderGraph feat(BgfxRenderer): Complete Phase 4 - ShaderManager integration 2025-11-26 22:27:19 +08:00
Resources feat: Add texture support to UI widgets and update gitignore 2026-01-14 23:15:13 +07:00
RHI feat: Add texture support to UI widgets and update gitignore 2026-01-14 23:15:13 +07:00
Scene feat(IIO)!: BREAKING CHANGE - Callback-based message dispatch 2026-01-19 14:19:27 +07:00
Shaders fix: UIModule button interaction + JsonDataNode array children support 2026-01-05 18:23:16 +07:00
Text feat(BgfxRenderer): Phase 7-8 - Text, Tilemap, Multi-texture, Resize 2025-11-27 22:09:48 +08:00
BgfxRendererModule.cpp feat: Add texture support to UI widgets and update gitignore 2026-01-14 23:15:13 +07:00
BgfxRendererModule.h feat: Add texture support to UI widgets and update gitignore 2026-01-14 23:15:13 +07:00
CMakeLists.txt fix: UIModule button interaction + JsonDataNode array children support 2026-01-05 18:23:16 +07:00
README.md docs: Update all documentation to reflect development stage 2026-01-15 09:26:23 +07:00

BgfxRenderer Module

⚠️ Development Stage: Experimental, non-deterministic. See main README for limitations.

Module de rendu 2D pour GroveEngine, basé sur bgfx.

Features

  • Abstraction RHI : Aucune dépendance bgfx exposée hors de BgfxDevice.cpp
  • Multi-backend : DirectX 11/12, OpenGL, Vulkan, Metal (auto-détecté)
  • MT-ready : Architecture task-based, lock-free frame allocator
  • Hot-reload : Support complet du hot-reload GroveEngine
  • Batching : Sprites groupés par texture pour performance

Architecture

BgfxRenderer/
├── BgfxRendererModule.h/.cpp   # Point d'entrée IModule
├── RHI/                        # Render Hardware Interface
│   ├── RHITypes.h              # Handles typés, enums
│   ├── RHIDevice.h             # Interface abstraite
│   ├── RHICommandBuffer.h/.cpp # Command recording
│   └── BgfxDevice.cpp          # Implémentation bgfx
├── Frame/
│   ├── FrameAllocator.h/.cpp   # Allocateur lock-free
│   └── FramePacket.h           # Données immuables par frame
├── RenderGraph/
│   ├── RenderPass.h            # Interface pass
│   └── RenderGraph.h/.cpp      # Gestion des passes
├── Passes/
│   ├── ClearPass.h/.cpp        # Clear framebuffer
│   ├── SpritePass.h/.cpp       # Sprites + batching
│   └── DebugPass.h/.cpp        # Debug lines/shapes
├── Scene/
│   └── SceneCollector.h/.cpp   # Collecte depuis IIO
└── Resources/
    └── ResourceCache.h/.cpp    # Cache textures/shaders

Build

Windows (recommandé pour le rendu)

cd "E:\Users\Alexis Trouvé\Documents\Projets\GroveEngine"

# Build rapide
.\build_renderer.bat

# Ou avec options
.\build_renderer.bat debug     # Build Debug
.\build_renderer.bat clean     # Clean + rebuild
.\build_renderer.bat vs        # Ouvrir Visual Studio

Linux/WSL

cmake -B build -DGROVE_BUILD_BGFX_RENDERER=ON
cmake --build build -j4

Dépendances Linux :

sudo apt-get install libgl1-mesa-dev libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev

Configuration

Le module est configuré via IDataNode dans setConfiguration() :

{
    "windowWidth": 1280,
    "windowHeight": 720,
    "backend": "auto",
    "shaderPath": "./shaders",
    "vsync": true,
    "maxSpritesPerBatch": 10000,
    "frameAllocatorSizeMB": 16,
    "nativeWindowHandle": 0
}
Paramètre Type Défaut Description
windowWidth int 1280 Largeur fenêtre
windowHeight int 720 Hauteur fenêtre
backend string "auto" Backend graphique (auto, opengl, vulkan, dx11, dx12, metal)
shaderPath string "./shaders" Chemin des shaders compilés
vsync bool true Synchronisation verticale
maxSpritesPerBatch int 10000 Sprites max par batch
frameAllocatorSizeMB int 16 Taille allocateur frame (MB)
nativeWindowHandle int 0 Handle fenêtre native (HWND, Window, etc.)

Communication IIO

Le renderer subscribe à render:* et traite les messages suivants :

Sprites

// Topic: render:sprite
auto sprite = std::make_unique<JsonDataNode>("sprite");
sprite->setDouble("x", 100.0);
sprite->setDouble("y", 200.0);
sprite->setDouble("scaleX", 1.0);
sprite->setDouble("scaleY", 1.0);
sprite->setDouble("rotation", 0.0);        // Radians
sprite->setDouble("u0", 0.0);              // UV min
sprite->setDouble("v0", 0.0);
sprite->setDouble("u1", 1.0);              // UV max
sprite->setDouble("v1", 1.0);
sprite->setInt("color", 0xFFFFFFFF);       // RGBA
sprite->setInt("textureId", 0);
sprite->setInt("layer", 0);                // Z-order
io->publish("render:sprite", std::move(sprite));

Batch de sprites

// Topic: render:sprite:batch
auto batch = std::make_unique<JsonDataNode>("batch");
auto sprites = std::make_unique<JsonDataNode>("sprites");
// Ajouter plusieurs sprites comme enfants...
batch->setChild("sprites", std::move(sprites));
io->publish("render:sprite:batch", std::move(batch));

Caméra

// Topic: render:camera
auto cam = std::make_unique<JsonDataNode>("camera");
cam->setDouble("x", 0.0);
cam->setDouble("y", 0.0);
cam->setDouble("zoom", 1.0);
cam->setInt("viewportX", 0);
cam->setInt("viewportY", 0);
cam->setInt("viewportW", 1280);
cam->setInt("viewportH", 720);
io->publish("render:camera", std::move(cam));

Clear color

// Topic: render:clear
auto clear = std::make_unique<JsonDataNode>("clear");
clear->setInt("color", 0x303030FF);        // RGBA
io->publish("render:clear", std::move(clear));

Debug (lignes et rectangles)

// Topic: render:debug:line
auto line = std::make_unique<JsonDataNode>("line");
line->setDouble("x1", 0.0);
line->setDouble("y1", 0.0);
line->setDouble("x2", 100.0);
line->setDouble("y2", 100.0);
line->setInt("color", 0xFF0000FF);         // Rouge
io->publish("render:debug:line", std::move(line));

// Topic: render:debug:rect
auto rect = std::make_unique<JsonDataNode>("rect");
rect->setDouble("x", 50.0);
rect->setDouble("y", 50.0);
rect->setDouble("w", 100.0);
rect->setDouble("h", 100.0);
rect->setInt("color", 0x00FF00FF);         // Vert
rect->setBool("filled", false);
io->publish("render:debug:rect", std::move(rect));

Topics complets

Topic Description
render:sprite Un sprite
render:sprite:batch Batch de sprites
render:tilemap Chunk de tilemap
render:text Texte à afficher
render:particle Particule
render:camera Configuration caméra
render:clear Clear color
render:debug:line Ligne de debug
render:debug:rect Rectangle de debug

Intégration

Exemple minimal

#include <grove/ModuleLoader.h>
#include <grove/JsonDataNode.h>
#include <grove/IntraIOManager.h>

int main() {
    // Créer le gestionnaire IO
    auto ioManager = std::make_unique<IntraIOManager>();
    auto io = ioManager->createIO("renderer");

    // Charger le module
    ModuleLoader loader;
    loader.load("./modules/libBgfxRenderer.dll", "renderer");

    // Configurer
    JsonDataNode config("config");
    config.setInt("windowWidth", 1920);
    config.setInt("windowHeight", 1080);
    config.setInt("nativeWindowHandle", (int)(intptr_t)hwnd);  // Ton HWND

    auto* module = loader.getModule();
    module->setConfiguration(config, io.get(), nullptr);

    // Main loop
    JsonDataNode input("input");
    while (running) {
        input.setDouble("deltaTime", deltaTime);

        // Envoyer des sprites via IIO
        auto sprite = std::make_unique<JsonDataNode>("sprite");
        sprite->setDouble("x", playerX);
        sprite->setDouble("y", playerY);
        sprite->setInt("textureId", 0);
        io->publish("render:sprite", std::move(sprite));

        // Process (collecte IIO + rendu)
        module->process(input);
    }

    module->shutdown();
    return 0;
}

Règles d'architecture

Règle Raison
Zéro bgfx:: hors de BgfxDevice.cpp Abstraction propre, changement backend possible
FramePacket const dans les passes Thread-safety, pas de mutation pendant render
CommandBuffer par thread Pas de lock pendant l'encoding
Handles, jamais de pointeurs raw Indirection = safe pour relocation
Allocation via FrameAllocator Lock-free, reset gratuit chaque frame

TODO

  • Chargement textures (stb_image)
  • Compilation shaders (shaderc ou pré-compilés)
  • TilemapPass
  • TextPass + fonts (stb_truetype)
  • ParticlePass
  • Resize handling (window:resize)
  • Multi-view support
  • Render targets / post-process

Dépendances

  • bgfx : Téléchargé automatiquement via CMake FetchContent
  • GroveEngine::impl : Core engine (IModule, IIO, IDataNode)
  • spdlog : Logging