Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ if(RENDER_BUILD_TESTS)
render_apply_project_options(render_headless_validation)
render_apply_warnings(render_headless_validation)

add_executable(render_renderer_lifecycle_tests tests/render/renderer_lifecycle_tests.cpp)
target_link_libraries(render_renderer_lifecycle_tests PRIVATE render::renderer)
render_apply_project_options(render_renderer_lifecycle_tests)
render_apply_warnings(render_renderer_lifecycle_tests)

add_test(NAME unit.platform.types COMMAND render_platform_types_tests)
set_tests_properties(unit.platform.types PROPERTIES LABELS "unit;platform")
add_test(NAME unit.core.runtime COMMAND render_core_runtime_tests)
Expand All @@ -189,12 +194,14 @@ if(RENDER_BUILD_TESTS)
set_tests_properties(unit.serialization.roundtrip PROPERTIES LABELS "unit;serialization")
add_test(NAME unit.filesystem.service COMMAND render_filesystem_tests)
set_tests_properties(unit.filesystem.service PROPERTIES LABELS "unit;filesystem")
add_test(NAME unit.renderer.lifecycle COMMAND render_renderer_lifecycle_tests)
set_tests_properties(unit.renderer.lifecycle PROPERTIES LABELS "unit;renderer")
add_test(NAME headless.smoke.startup COMMAND render_headless_validation)
set_tests_properties(headless.smoke.startup PROPERTIES LABELS "headless;smoke")

add_custom_target(render_test_unit
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --label-regex unit
DEPENDS render_platform_types_tests render_core_runtime_tests render_serialization_tests render_filesystem_tests
DEPENDS render_platform_types_tests render_core_runtime_tests render_serialization_tests render_filesystem_tests render_renderer_lifecycle_tests
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Running render unit tests"
)
Expand Down
125 changes: 91 additions & 34 deletions docs/rendering.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,109 @@
# Renderer Integration (Statement 4)
# Renderer Runtime Lifecycle (Statement 9)

## Scope

Statement 4 introduces a renderer foundation powered by bgfx/bx/bimg but exposed through engine-owned interfaces.
Statement 9 hardens renderer runtime behavior around startup, backend selection, frame flow, resize handling, frame pacing, and recovery-oriented state management.

## Where code lives
## Lifecycle model

- Public interfaces and engine-owned types: `engine/render/*.hpp`
- bgfx backend implementation: `engine/render/bgfx/renderer_bgfx.cpp`
- Shader sources: `shaders/src/*.sc`
- Shader build hook: `cmake/ShaderCompilation.cmake`, `cmake/compile_shaders.cmake`
`render::rendering::Renderer` now owns an explicit lifecycle state machine:

## Allowed direct third-party usage
- `Uninitialized`
- `Initializing`
- `Ready`
- `Resizing`
- `Recovering`
- `ShuttingDown`
- `Failed`

- `engine/render/bgfx/*`: may include and call bgfx/bx/bimg APIs
- `engine/platform/sdl/*`: may include and call SDL APIs
The runtime shell and future systems can query:

All other engine/gameplay modules should stay on engine-owned types.
- `state()`
- `is_ready()`
- `can_render()`
- `status()` (`RendererStatus` snapshot including frame stats)

## SDL window handoff
The renderer logs every lifecycle transition.

Renderer initialization receives `PlatformRuntime` and reads the SDL window pointer through `native_window_handle()`.
The bgfx backend extracts native platform handles from SDL window properties and fills `bgfx::Init::platformData`.
## Startup and backend selection

## Current renderer API coverage
Startup is configuration-driven via `RendererConfig`:

- lifecycle (`initialize`, `shutdown`)
- backend selection (`RendererBackend`)
- frame flow (`begin_frame`, `end_frame`, `resize`)
- view setup (`set_view`, `set_view_transform`)
- resource creation (`create_vertex_buffer`, `create_index_buffer`, `create_program`, `create_solid_color_texture`)
- draw submission (`submit`)
- diagnostics (`capabilities`, debug text)
- `backend` (engine-owned enum, auto or explicit)
- `width`, `height`
- `debug`
- `vsync`
- `reset_flags` (engine-owned config mask)
- `min_frame_time_ms` (lightweight frame pacing floor)
- `allow_automatic_recovery`

## Current validation target
Validation is explicit through `validate_renderer_config(...)`.

Backend behavior:

1. Engine backend enum maps to bgfx renderer type internally.
2. Requested backend and actual backend are both logged.
3. Explicit requests that are not selected log a warning and continue with the chosen backend.

## Frame lifecycle contract

Per-frame flow is now explicit and misuse-resistant:

1. `begin_frame()`
- rejects invalid states
- detects begin/begin misuse
- applies deferred resize
- returns `false` when rendering should be skipped (minimized/invalid state)
2. app records view setup + submissions
3. `end_frame()`
- detects end without begin
- presents (`bgfx::frame()`)
- records frame stats and optional pacing sleep

This creates a stable contract for future view/pass expansion without redesigning loop ownership.

## Resize policy

Resize is explicit and resilient:

- shell forwards resize events with `request_resize(width, height)`
- resize requests are deduplicated and applied at frame boundary
- zero/minimized dimensions are treated as non-renderable and do not hard-fail the renderer
- rendering automatically resumes when a valid size arrives

## Frame pacing policy (current stage)

Current pacing is intentionally lightweight:

- primary pacing remains present/vsync (`vsync` + reset flags)
- optional minimum frame duration (`min_frame_time_ms`) prevents runaway present loops when desired
- no simulation scheduler or fixed-step governor is introduced at this stage

## Recovery/device-loss philosophy

A sane engine-owned recovery path is established:

- failed/recovery states are explicit
- `try_recover()` attempts an in-place reset path (`bgfx::reset`) using current backbuffer
- automatic recovery attempts are gated by `allow_automatic_recovery`
- recovery attempts and outcomes are logged

This is a practical base for later full resource re-creation pipelines.

## Runtime shell integration

`render_shell` now:

1. starts SDL runtime/window
2. initializes renderer with config
3. clears the main view every frame
4. submits a simple indexed triangle when shader binaries are available
5. handles resize and clean shutdown
- forwards platform resize events through `request_resize`
- gates rendering work on `begin_frame()` success
- avoids undefined frame flow when minimized or otherwise non-renderable
- keeps shutdown ordered (`destroy resources` -> `renderer.shutdown()` -> `runtime.shutdown()`)

## Current non-goals / deferred work

## Follow-up work (later statements)
Still deferred to later statements:

- richer resource lifetime/allocator systems
- robust shader pipeline tooling and packaging
- material and scene rendering systems
- texture/image asset loading pipeline
- render graph/pass scheduling
- full multi-device-loss backend edge-case matrix
- explicit persistent resource registry for automated rebuild
- render graph and multi-pass scheduling
- scene/material-driven rendering orchestration
12 changes: 12 additions & 0 deletions engine/render/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ Engine-owned rendering interfaces and resource types.
- bgfx-specific backend implementation: `engine/render/bgfx/*`

Higher-level engine and gameplay code should include only the public renderer headers.

## Lifecycle contract (Statement 9)

The public renderer API now exposes an explicit lifecycle and frame contract:

- Startup/shutdown: `initialize`, `shutdown`
- Query: `state`, `status`, `is_ready`, `can_render`
- Frame flow: `begin_frame`, `end_frame`
- Resize flow: `request_resize`, `resize`
- Recovery hook: `try_recover`

This keeps app/runtime code independent of bgfx lifecycle details.
Loading
Loading