Vienna-WebGPU-Engine

Vienna-WebGPU-Engine Architecture

This document describes the fundamental architecture, design patterns, and coding conventions used in the Vienna-WebGPU-Engine.

Table of Contents

  1. Core Architecture
  2. Layered Design
  3. Resource Management
  4. Factory Pattern
  5. Rendering System
  6. Scene Graph
  7. Coding Conventions
  8. Key Design Principles

Path Management

Two path systems via PathProvider:

  1. Application Assets: getTextures(), getModels() → <example>/assets/
  2. Engine Resources: getResource() → <engine>/resources/

Rules:


Core Architecture

The engine follows a layered architecture with clear separation of concerns:

┌─────────────────────────────────────┐
│      Application Layer              │
│  (Game Loop, Input, UI)             │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│      Scene Graph Layer              │
│  (Nodes, Transforms, Camera)        │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│      Rendering Layer                │
│  (Renderer, Passes, Pipelines)      │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│      Resource Layer                 │
│  (Managers, Loaders, Factories)     │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│      Core Layer                     │
│  (Handle, Identifiable, Versioned)  │
└─────────────────────────────────────┘

Layered Design

1. Core Layer

Location: include/engine/core/

Foundation classes used throughout the engine:

2. Resource Layer

Location: include/engine/resources/

Manages CPU-side resources:

Key Rules:

3. Rendering Layer

Location: include/engine/rendering/

Handles GPU-side rendering:

Key Rule: All GPU resource creation goes through factories accessed via WebGPUContext.

4. Scene Graph Layer

Location: include/engine/scene/

Hierarchical scene representation:

5. Application Layer

Location: include/engine/

Top-level game loop integration:


Resource Management

CPU Resources → GPU Resources Flow

Load via Manager → Create GPU version via Factory → Use in Renderer

Example:

// 1. Load CPU resource via manager
auto textureOpt = textureManager->createTextureFromFile("texture.png");
if (!textureOpt)
    return;

// 2. Create GPU resource via factory
auto gpuTexture = context.textureFactory().createFromHandle(textureOpt.value()->getHandle());

// 3. Use in renderer
material->setTexture("albedo", gpuTexture);

Resource Handles

Use Handle<T> for type-safe resource references:

auto textureOpt = textureManager->createTextureFromFile("texture.png");
if (textureOpt) {
    auto texture = textureOpt.value();
    // Use texture
}

Factory Pattern

Rule: Always use factories for GPU resource creation. Never create WebGPU resources manually.

Accessing Factories

All factories are accessed through WebGPUContext:

// Texture creation
auto texture = context.textureFactory().createFromHandle(handle);

// Material creation
auto material = context.materialFactory().createFromHandle(handle);

// Mesh creation
auto mesh = context.meshFactory().createFromHandle(handle);

// Pipeline creation
auto pipeline = context.pipelineFactory().createPipeline(descriptor, layouts, count);

Common Factories

Factory Purpose Key Methods
WebGPUTextureFactory GPU textures createFromHandle(), createFromColor(), getWhiteTexture()
WebGPUMaterialFactory GPU materials with bind groups createFromHandle()
WebGPUMeshFactory GPU mesh buffers createFromHandle()
WebGPUModelFactory GPU models createFromHandle()
WebGPUPipelineFactory Render pipelines createPipeline(), getDefaultRenderPipeline()
WebGPUBufferFactory GPU buffers createBuffer(), createBufferWithData()
WebGPUBindGroupFactory Bind groups and layouts createBindGroup(), createBindGroupLayout()

Rendering System

Frame Lifecycle

1. clearForNewFrame()          // Clear frame cache
2. prepareGPUResources()       // Upload uniforms, create bind groups
3. processBindGroupProviders() // Process custom bind groups
4. Render passes:
   - Shadow pass (optional)
   - Main mesh pass
   - Debug pass (optional)
5. Present frame

Bind Group System

binder.bind(renderPass, pipeline, cameraId, bindGroups, objectId, materialId);

Features:

Render Passes

Pass Purpose Outputs
ShadowPass Shadow map rendering 2D and cube shadow maps
MeshPass Main scene rendering Color + depth
DebugPass Debug visualization Debug primitives overlay

Scene Graph

Node Hierarchy

Every Scene contains:

Example:

// Scene automatically creates root and camera
auto scene = std::make_shared<Scene>();

// Add custom nodes
auto objectNode = std::make_shared<Node>();
objectNode->getTransform().setLocalPosition({0, 1, 0});
scene->getRoot()->addChild(objectNode);

Node Types

Transform System

Unity-like lazy matrix updates:

transform.setLocalPosition({0, 1, 0});
transform.setLocalEulerAngles({0, 45, 0});  // Euler angles in degrees
transform.setLocalScale({2, 2, 2});

// Matrices updated lazily on first access
glm::mat4 world = transform->getWorldMatrix();

Coding Conventions

Naming Conventions

Indentation

Use tabs for indentation, not spaces.

Include Order

Use blank lines between groups:

  1. Header for implementation file
  2. Standard library headers
  3. Third-party library headers
  4. Project-specific headers
  5. Platform-specific headers (optional)

Example:

#include "MyClass.h"

#include <memory>
#include <vector>

#include <glm/glm.hpp>
#include <spdlog/spdlog.h>

#include "engine/rendering/Renderer.h"
#include "engine/scene/Scene.h"

Header Guards

Use #pragma once for all header files.

File Structure

Organize by feature/module with clear naming:

engine/
├── core/           # Foundation classes
├── resources/      # Resource management
├── rendering/      # Rendering system
│   └── webgpu/     # WebGPU-specific code
├── scene/          # Scene graph
└── input/          # Input handling

Key Design Principles

1. Factory Pattern for GPU Resources

Never create WebGPU resources manually. Always use factories:

// ❌ Bad
wgpu::Buffer buffer = device.createBuffer(desc);

// ✅ Good
auto buffer = context.bufferFactory().createBuffer(desc);

2. Resource Flow: Manager → Factory → Renderer

// 1. Manager (CPU)
auto textureOpt = textureManager->createTextureFromFile("texture.png");
if (!textureOpt)
    return;

// 2. Factory (GPU)
auto gpuTexture = context.textureFactory().createFromHandle(textureOpt.value()->getHandle());

// 3. Renderer (Usage)
material->setTexture("albedo", gpuTexture);

3. EngineContext for System Access

Nodes access engine systems via EngineContext:

// In custom UpdateNode
void update(float deltaTime) override {
    if (engine()->input()->isKey(SDL_SCANCODE_W)) {
        // Move forward
    }
    
    auto textureOpt = engine()->resources()->m_textureManager->createTextureFromFile("tex.png");
}

4. Lazy Resource Creation

5. Explicit Resource Cleanup

WebGPU resources need explicit cleanup:

texture.release();
buffer.release();
bindGroup.release();

6. Bind Group Organization

Standard bind group indices:

7. Hot-Reloading Support

Use PipelineManager for shader hot-reloading:

pipelineManager->createPipeline("myShader", config);
// Later...
pipelineManager->reloadPipeline("myShader");

Additional Resources


Quick Reference

Essential Classes

Class Purpose Location
WebGPUContext Central GPU resource hub rendering/webgpu/
FrameCache Frame-wide rendering data rendering/
BindGroupBinder Centralized bind group binding rendering/
Scene Scene graph root scene/
ResourceManager Aggregate resource manager resources/

See Also