mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
ROADMAP.md
This commit is contained in:
@@ -640,6 +640,18 @@ target_link_libraries(graphics_service_buffer_lifecycle_test PRIVATE
|
||||
)
|
||||
add_test(NAME graphics_service_buffer_lifecycle_test COMMAND graphics_service_buffer_lifecycle_test)
|
||||
|
||||
# Test: Render graph validation (cycles + unknown dependencies)
|
||||
add_executable(render_graph_service_test
|
||||
tests/render_graph_service_test.cpp
|
||||
src/services/impl/render_graph_service.cpp
|
||||
)
|
||||
target_include_directories(render_graph_service_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
target_link_libraries(render_graph_service_test PRIVATE
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
add_test(NAME render_graph_service_test COMMAND render_graph_service_test)
|
||||
|
||||
# Test: Bgfx Draw bounds validation (TDD test for buffer overflow crash)
|
||||
add_executable(bgfx_draw_bounds_validation_test
|
||||
tests/bgfx_draw_bounds_validation_test.cpp
|
||||
|
||||
73
ROADMAP.md
73
ROADMAP.md
@@ -213,6 +213,79 @@ Option B: per-shader only
|
||||
- [ ] Budget enforcement tests (over-limit textures, transient pool caps)
|
||||
- [ ] Smoke test: cube demo boots with config-first scene definition
|
||||
|
||||
## Test Strategy (Solid Coverage Plan)
|
||||
### Goals
|
||||
- Fail fast on config errors, graph issues, and resource constraints before runtime.
|
||||
- Protect crash recovery and rendering safety invariants with regression tests.
|
||||
- Keep config-first path validated even while Lua fallback exists.
|
||||
|
||||
### Layered Test Plan
|
||||
- Unit: schema validation, config merges (`extends`, `@delete`), IR compilation edge cases.
|
||||
- Service: render graph validation (cycles, unknown refs, duplicates), shader pipeline validation, budget enforcement.
|
||||
- Integration: shader compilation, MaterialX generation + validation, crash recovery timeouts.
|
||||
- Smoke: config-first boot of the cube demo with no Lua scene execution.
|
||||
|
||||
### Coverage Matrix (What We Must Prove)
|
||||
- Config parsing + schema errors produce JSON Pointer diagnostics.
|
||||
- Merge behavior is deterministic and well-documented for arrays and deletes.
|
||||
- Render graph validation detects cycles, unknown passes/outputs, and produces stable schedules.
|
||||
- Shader pipelines reject layout mismatches and uniform incompatibilities.
|
||||
- Budget enforcement fails safely (textures + GUI caches now, buffers later).
|
||||
- Crash recovery detects hangs and returns promptly.
|
||||
|
||||
### Test Assets + Determinism
|
||||
- Prefer tiny synthetic assets in `tests/` for deterministic behavior.
|
||||
- Keep large MaterialX assets for integration tests only.
|
||||
- Avoid network access in tests; all inputs must be local.
|
||||
|
||||
### CI Gate Suggestions
|
||||
- Quick: unit + service tests (schema/merge/render graph/pipeline validator).
|
||||
- Full: integration tests (MaterialX + shader linking) and smoke config-first boot.
|
||||
|
||||
## Troubleshooting Guide (Segfaults, Ordering, Shader Quirks)
|
||||
### Common Failure Modes
|
||||
- Segfaults after startup: often caused by invalid bgfx handles, resource exhaustion, or pre-frame usage.
|
||||
- Draw crashes: index/vertex buffer mismatch or using buffers before upload.
|
||||
- Shader issues: missing uniforms, incorrect layout qualifiers, or wrong backend profile.
|
||||
- Ordering bugs: loading shaders/textures before the first `BeginFrame` + `EndFrame` priming pass.
|
||||
|
||||
### Immediate Triage Steps
|
||||
- Re-run with trace logging enabled (`--trace`) and capture the last 50 lines of the log.
|
||||
- Confirm config schema validation passes and print loaded JSON (`--dump-json`).
|
||||
- Check that shaders are compiled for the active renderer (Vulkan vs OpenGL).
|
||||
- Ensure bgfx is initialized and has seen a frame before loading textures/shaders.
|
||||
|
||||
### Known Hotspots To Inspect
|
||||
- Shader pipeline validation: `src/services/impl/shader_pipeline_validator.cpp`
|
||||
- Texture load guards + budgets: `src/services/impl/bgfx_graphics_backend.cpp`
|
||||
- Render graph scheduling: `src/services/impl/render_graph_service.cpp`
|
||||
- Config compiler diagnostics: `src/services/impl/config_compiler_service.cpp`
|
||||
- Crash recovery timeouts: `src/services/impl/crash_recovery_service.cpp`
|
||||
|
||||
### Ordering Checklist (When Things Crash)
|
||||
- `InitializeDevice` → `InitializeSwapchain` → `BeginFrame` → `EndFrame` before loading shaders/textures.
|
||||
- Load shaders once, then upload geometry, then render.
|
||||
- Avoid calling bgfx APIs after shutdown or on invalid handles.
|
||||
|
||||
### Shader Debug Checklist
|
||||
- Verify `layout(location = N)` on all GLSL inputs/outputs (SPIR-V requirement).
|
||||
- Check uniform types match expected (sampler vs vec types).
|
||||
- Validate vertex layout matches shader inputs.
|
||||
|
||||
### When Filing A Bug
|
||||
- Include config JSON, active renderer, last log lines, and crash report (if any).
|
||||
- Note whether `runtime.scene_source` is `config` or `lua`.
|
||||
|
||||
### Known Fixes And Evidence
|
||||
- Texture load crashes: see `tests/bgfx_texture_loading_test.cpp` and `FIXES_IMPLEMENTED.md`.
|
||||
- Shader uniform mapping failures: see `tests/shaderc_uniform_mapping_test.cpp` and `tests/gui_shader_linking_failure_test.cpp`.
|
||||
- Initialization order regressions: see `tests/bgfx_initialization_order_test.cpp` and `tests/bgfx_frame_requirement_test.cpp`.
|
||||
- Render graph validation gaps: see `tests/render_graph_service_test.cpp` (cycles/unknown refs/duplicates).
|
||||
- Crash recovery timeouts: see `tests/crash_recovery_timeout_test.cpp`.
|
||||
|
||||
### Vendored Library Caveat
|
||||
- Treat any library code pasted into `src/` (or similar vendor folders) as locally modified until verified.
|
||||
- Do not assume upstream behavior; always confirm against the local copy when debugging.
|
||||
## Open Questions
|
||||
- Preferred merge behavior for array fields (replace vs keyed merge by `id`)
|
||||
- Scope of hot-reload (full scene reload vs incremental updates)
|
||||
|
||||
95
tests/render_graph_service_test.cpp
Normal file
95
tests/render_graph_service_test.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "services/impl/render_graph_service.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
bool HasDiagnosticCode(const std::vector<sdl3cpp::services::ProbeReport>& diagnostics,
|
||||
const std::string& code) {
|
||||
for (const auto& report : diagnostics) {
|
||||
if (report.code == code) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
sdl3cpp::services::RenderPassIR MakePass(const std::string& id,
|
||||
const std::string& inputPassId = {}) {
|
||||
sdl3cpp::services::RenderPassIR pass;
|
||||
pass.id = id;
|
||||
pass.jsonPath = "/render/passes/" + id;
|
||||
if (!inputPassId.empty()) {
|
||||
sdl3cpp::services::RenderPassInputIR input;
|
||||
input.name = "color";
|
||||
input.jsonPath = pass.jsonPath + "/inputs/color";
|
||||
input.ref.type = sdl3cpp::services::RenderResourceRefType::PassOutput;
|
||||
input.ref.passId = inputPassId;
|
||||
input.ref.outputName = "output";
|
||||
input.ref.jsonPath = input.jsonPath;
|
||||
pass.inputs.push_back(input);
|
||||
}
|
||||
return pass;
|
||||
}
|
||||
|
||||
TEST(RenderGraphServiceTest, EmptyGraphSucceeds) {
|
||||
sdl3cpp::services::RenderGraphIR graph;
|
||||
sdl3cpp::services::impl::RenderGraphService service(nullptr);
|
||||
auto result = service.BuildGraph(graph);
|
||||
|
||||
EXPECT_TRUE(result.success);
|
||||
EXPECT_TRUE(result.passOrder.empty());
|
||||
EXPECT_TRUE(result.diagnostics.empty());
|
||||
}
|
||||
|
||||
TEST(RenderGraphServiceTest, ValidDependencyOrdersPasses) {
|
||||
sdl3cpp::services::RenderGraphIR graph;
|
||||
graph.passes.push_back(MakePass("gbuffer"));
|
||||
graph.passes.push_back(MakePass("tonemap", "gbuffer"));
|
||||
|
||||
sdl3cpp::services::impl::RenderGraphService service(nullptr);
|
||||
auto result = service.BuildGraph(graph);
|
||||
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.passOrder.size(), 2u);
|
||||
EXPECT_EQ(result.passOrder[0], "gbuffer");
|
||||
EXPECT_EQ(result.passOrder[1], "tonemap");
|
||||
}
|
||||
|
||||
TEST(RenderGraphServiceTest, DetectsUnknownPassInput) {
|
||||
sdl3cpp::services::RenderGraphIR graph;
|
||||
graph.passes.push_back(MakePass("main", "missing"));
|
||||
|
||||
sdl3cpp::services::impl::RenderGraphService service(nullptr);
|
||||
auto result = service.BuildGraph(graph);
|
||||
|
||||
EXPECT_FALSE(result.success);
|
||||
EXPECT_TRUE(HasDiagnosticCode(result.diagnostics, "RG_INPUT_UNKNOWN_PASS"));
|
||||
}
|
||||
|
||||
TEST(RenderGraphServiceTest, DetectsDuplicatePassIds) {
|
||||
sdl3cpp::services::RenderGraphIR graph;
|
||||
graph.passes.push_back(MakePass("main"));
|
||||
graph.passes.push_back(MakePass("main"));
|
||||
|
||||
sdl3cpp::services::impl::RenderGraphService service(nullptr);
|
||||
auto result = service.BuildGraph(graph);
|
||||
|
||||
EXPECT_FALSE(result.success);
|
||||
EXPECT_TRUE(HasDiagnosticCode(result.diagnostics, "RG_PASS_ID_DUPLICATE"));
|
||||
}
|
||||
|
||||
TEST(RenderGraphServiceTest, DetectsCycles) {
|
||||
sdl3cpp::services::RenderGraphIR graph;
|
||||
graph.passes.push_back(MakePass("a", "b"));
|
||||
graph.passes.push_back(MakePass("b", "a"));
|
||||
|
||||
sdl3cpp::services::impl::RenderGraphService service(nullptr);
|
||||
auto result = service.BuildGraph(graph);
|
||||
|
||||
EXPECT_FALSE(result.success);
|
||||
EXPECT_TRUE(HasDiagnosticCode(result.diagnostics, "RG_CYCLE_DETECTED"));
|
||||
EXPECT_LT(result.passOrder.size(), graph.passes.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user