mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
ROADMAP.md
This commit is contained in:
@@ -47,6 +47,34 @@
|
||||
"type": "luma_range",
|
||||
"min_luma": 0.01,
|
||||
"max_luma": 0.95
|
||||
},
|
||||
{
|
||||
"type": "mean_color",
|
||||
"color": [0.4, 0.45, 0.5],
|
||||
"tolerance": 0.25
|
||||
},
|
||||
{
|
||||
"type": "sample_points",
|
||||
"points": [
|
||||
{
|
||||
"x": 0.25,
|
||||
"y": 0.25,
|
||||
"color": [0.2, 0.3, 0.4],
|
||||
"tolerance": 0.35
|
||||
},
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"color": [0.1, 0.2, 0.6],
|
||||
"tolerance": 0.4
|
||||
},
|
||||
{
|
||||
"x": 0.75,
|
||||
"y": 0.75,
|
||||
"color": [0.8, 0.85, 0.9],
|
||||
"tolerance": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -49,3 +49,7 @@ Avoid stagnation; improve structure, clarity, and expressiveness over time.
|
||||
Roadmap discipline.
|
||||
If you run out of ROADMAP.md tasks, stop and report.
|
||||
We should reassess priorities before inventing work.
|
||||
|
||||
Prefer JSON workflows over LUA - Steps can be written in host language.
|
||||
|
||||
Vulkan can be a bit sketchy at first, very cryptic crashes. Try OpenGL.
|
||||
|
||||
@@ -74,6 +74,19 @@ std::filesystem::path GetSeedConfigPath() {
|
||||
return repoRoot / "config" / "seed_runtime.json";
|
||||
}
|
||||
|
||||
std::string DeterminePreferredRenderer() {
|
||||
if (const char* envRenderer = std::getenv("BGFX_RENDERER")) {
|
||||
return envRenderer;
|
||||
}
|
||||
if (const char* videoDriver = std::getenv("SDL_VIDEODRIVER")) {
|
||||
std::string driver(videoDriver);
|
||||
if (driver == "offscreen" || driver == "dummy") {
|
||||
return "opengl";
|
||||
}
|
||||
}
|
||||
return "opengl";
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadFileContents(const std::filesystem::path& path) {
|
||||
std::ifstream input(path);
|
||||
if (!input) {
|
||||
@@ -385,154 +398,146 @@ bool RunGpuRenderTest(int& failures,
|
||||
|
||||
if (success) {
|
||||
std::cout << "GPU render test: Validating full render pipeline with scene geometry\n";
|
||||
|
||||
|
||||
// Load and render the actual cube scene to catch color, geometry, and animation issues
|
||||
auto scriptPath = GetCubeScriptPath();
|
||||
auto configPath = GetSeedConfigPath();
|
||||
auto configJson = ReadFileContents(configPath);
|
||||
if (!configJson) {
|
||||
std::cerr << "GPU render test failed: could not load scene config\n";
|
||||
auto sceneConfigService = configService;
|
||||
auto meshService = std::make_shared<sdl3cpp::services::impl::MeshService>(sceneConfigService, logger);
|
||||
auto audioService = std::make_shared<StubAudioCommandService>();
|
||||
auto physicsService = std::make_shared<sdl3cpp::services::impl::PhysicsBridgeService>(logger);
|
||||
|
||||
auto engineService = std::make_shared<sdl3cpp::services::impl::ScriptEngineService>(
|
||||
scriptPath,
|
||||
logger,
|
||||
meshService,
|
||||
audioService,
|
||||
physicsService,
|
||||
nullptr,
|
||||
nullptr,
|
||||
sceneConfigService,
|
||||
false);
|
||||
engineService->Initialize();
|
||||
|
||||
auto sceneScriptService = std::make_shared<sdl3cpp::services::impl::SceneScriptService>(engineService, logger);
|
||||
auto objects = sceneScriptService->LoadSceneObjects();
|
||||
|
||||
if (objects.size() != 15) {
|
||||
std::cerr << "GPU render test: Scene loaded " << objects.size() << " objects, expected 15\n";
|
||||
++failures;
|
||||
success = false;
|
||||
} else {
|
||||
auto sceneConfigService = std::make_shared<CubeDemoConfigService>(scriptPath, *configJson, "vulkan");
|
||||
auto meshService = std::make_shared<sdl3cpp::services::impl::MeshService>(sceneConfigService, logger);
|
||||
auto audioService = std::make_shared<StubAudioCommandService>();
|
||||
auto physicsService = std::make_shared<sdl3cpp::services::impl::PhysicsBridgeService>(logger);
|
||||
|
||||
auto engineService = std::make_shared<sdl3cpp::services::impl::ScriptEngineService>(
|
||||
scriptPath,
|
||||
logger,
|
||||
meshService,
|
||||
audioService,
|
||||
physicsService,
|
||||
nullptr,
|
||||
nullptr,
|
||||
sceneConfigService,
|
||||
false);
|
||||
engineService->Initialize();
|
||||
|
||||
auto sceneScriptService = std::make_shared<sdl3cpp::services::impl::SceneScriptService>(engineService, logger);
|
||||
auto objects = sceneScriptService->LoadSceneObjects();
|
||||
|
||||
if (objects.size() != 15) {
|
||||
std::cerr << "GPU render test: Scene loaded " << objects.size() << " objects, expected 15\n";
|
||||
}
|
||||
|
||||
// Validate all geometry is present
|
||||
bool hasFloor = false;
|
||||
bool hasCeiling = false;
|
||||
int wallCount = 0;
|
||||
int lanternCount = 0;
|
||||
bool hasCube = false;
|
||||
|
||||
for (const auto& obj : objects) {
|
||||
const auto& type = obj.objectType;
|
||||
|
||||
if (type == "floor") {
|
||||
hasFloor = true;
|
||||
} else if (type == "ceiling") {
|
||||
hasCeiling = true;
|
||||
} else if (type == "wall") {
|
||||
wallCount++;
|
||||
} else if (type == "lantern") {
|
||||
lanternCount++;
|
||||
} else if (type == "physics_cube" || type == "spinning_cube") {
|
||||
hasCube = true;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(hasFloor, "GPU render test: Missing floor geometry", failures);
|
||||
Assert(hasCeiling, "GPU render test: Missing ceiling geometry", failures);
|
||||
Assert(wallCount == 4, "GPU render test: Expected 4 walls, got " + std::to_string(wallCount), failures);
|
||||
Assert(lanternCount == 8, "GPU render test: Expected 8 lanterns, got " + std::to_string(lanternCount), failures);
|
||||
Assert(hasCube, "GPU render test: Missing physics cube geometry", failures);
|
||||
|
||||
// Validate all scene objects have valid shader keys (critical for rendering)
|
||||
for (size_t i = 0; i < objects.size(); ++i) {
|
||||
const auto& obj = objects[i];
|
||||
Assert(!obj.shaderKeys.empty(),
|
||||
"GPU render test: Object " + std::to_string(i) + " (" + obj.objectType + ") has no shader keys",
|
||||
failures);
|
||||
|
||||
// Validate room geometry (floor, ceiling, walls) has expected shader keys
|
||||
if (obj.objectType == "floor") {
|
||||
Assert(!obj.shaderKeys.empty() && obj.shaderKeys.front() == "floor",
|
||||
"GPU render test: Floor should have shader key 'floor'", failures);
|
||||
Assert(obj.vertices.size() >= 100,
|
||||
"GPU render test: Floor should have tessellated geometry (expected >= 100 vertices, got " +
|
||||
std::to_string(obj.vertices.size()) + ")", failures);
|
||||
} else if (obj.objectType == "ceiling") {
|
||||
Assert(!obj.shaderKeys.empty() && obj.shaderKeys.front() == "ceiling",
|
||||
"GPU render test: Ceiling should have shader key 'ceiling'", failures);
|
||||
Assert(obj.vertices.size() >= 100,
|
||||
"GPU render test: Ceiling should have tessellated geometry (expected >= 100 vertices, got " +
|
||||
std::to_string(obj.vertices.size()) + ")", failures);
|
||||
} else if (obj.objectType == "wall") {
|
||||
Assert(!obj.shaderKeys.empty() && obj.shaderKeys.front() == "wall",
|
||||
"GPU render test: Wall should have shader key 'wall'", failures);
|
||||
}
|
||||
}
|
||||
|
||||
// Create actual render buffers and render multiple frames to test animation
|
||||
auto ecsService = std::make_shared<sdl3cpp::services::impl::EcsService>(logger);
|
||||
auto sceneService = std::make_shared<sdl3cpp::services::impl::SceneService>(sceneScriptService, ecsService, logger);
|
||||
sceneService->LoadScene(objects);
|
||||
|
||||
auto device = backend.CreateDevice();
|
||||
const int testFrames = 5;
|
||||
std::cout << "GPU render test: Rendering " << testFrames << " frames to validate pipeline\n";
|
||||
|
||||
for (int frame = 0; frame < testFrames; ++frame) {
|
||||
float elapsedTime = frame * 0.016f; // ~60 FPS
|
||||
auto renderCommands = sceneService->GetRenderCommands(elapsedTime);
|
||||
|
||||
if (renderCommands.size() != objects.size()) {
|
||||
std::cerr << "GPU render test: Frame " << frame << " produced "
|
||||
<< renderCommands.size() << " commands, expected " << objects.size() << '\n';
|
||||
++failures;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate all geometry is present
|
||||
bool hasFloor = false;
|
||||
bool hasCeiling = false;
|
||||
int wallCount = 0;
|
||||
int lanternCount = 0;
|
||||
bool hasCube = false;
|
||||
|
||||
for (const auto& obj : objects) {
|
||||
const auto& type = obj.objectType;
|
||||
|
||||
if (type == "floor") {
|
||||
hasFloor = true;
|
||||
} else if (type == "ceiling") {
|
||||
hasCeiling = true;
|
||||
} else if (type == "wall") {
|
||||
wallCount++;
|
||||
} else if (type == "lantern") {
|
||||
lanternCount++;
|
||||
} else if (type == "physics_cube" || type == "spinning_cube") {
|
||||
hasCube = true;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(hasFloor, "GPU render test: Missing floor geometry", failures);
|
||||
Assert(hasCeiling, "GPU render test: Missing ceiling geometry", failures);
|
||||
Assert(wallCount == 4, "GPU render test: Expected 4 walls, got " + std::to_string(wallCount), failures);
|
||||
Assert(lanternCount == 8, "GPU render test: Expected 8 lanterns, got " + std::to_string(lanternCount), failures);
|
||||
Assert(hasCube, "GPU render test: Missing physics cube geometry", failures);
|
||||
|
||||
// Validate all scene objects have valid shader keys (critical for rendering)
|
||||
for (size_t i = 0; i < objects.size(); ++i) {
|
||||
const auto& obj = objects[i];
|
||||
Assert(!obj.shaderKeys.empty(),
|
||||
"GPU render test: Object " + std::to_string(i) + " (" + obj.objectType + ") has no shader keys",
|
||||
failures);
|
||||
|
||||
// Validate room geometry (floor, ceiling, walls) has expected shader keys
|
||||
if (obj.objectType == "floor") {
|
||||
Assert(!obj.shaderKeys.empty() && obj.shaderKeys.front() == "floor",
|
||||
"GPU render test: Floor should have shader key 'floor'", failures);
|
||||
Assert(obj.vertices.size() >= 100,
|
||||
"GPU render test: Floor should have tessellated geometry (expected >= 100 vertices, got " +
|
||||
std::to_string(obj.vertices.size()) + ")", failures);
|
||||
} else if (obj.objectType == "ceiling") {
|
||||
Assert(!obj.shaderKeys.empty() && obj.shaderKeys.front() == "ceiling",
|
||||
"GPU render test: Ceiling should have shader key 'ceiling'", failures);
|
||||
Assert(obj.vertices.size() >= 100,
|
||||
"GPU render test: Ceiling should have tessellated geometry (expected >= 100 vertices, got " +
|
||||
std::to_string(obj.vertices.size()) + ")", failures);
|
||||
} else if (obj.objectType == "wall") {
|
||||
Assert(!obj.shaderKeys.empty() && obj.shaderKeys.front() == "wall",
|
||||
"GPU render test: Wall should have shader key 'wall'", failures);
|
||||
}
|
||||
}
|
||||
|
||||
// Create actual render buffers and render multiple frames to test animation
|
||||
auto ecsService = std::make_shared<sdl3cpp::services::impl::EcsService>(logger);
|
||||
auto sceneService = std::make_shared<sdl3cpp::services::impl::SceneService>(sceneScriptService, ecsService, logger);
|
||||
sceneService->LoadScene(objects);
|
||||
|
||||
auto device = backend.CreateDevice();
|
||||
const int testFrames = 5;
|
||||
std::cout << "GPU render test: Rendering " << testFrames << " frames to validate pipeline\n";
|
||||
|
||||
for (int frame = 0; frame < testFrames; ++frame) {
|
||||
float elapsedTime = frame * 0.016f; // ~60 FPS
|
||||
auto renderCommands = sceneService->GetRenderCommands(elapsedTime);
|
||||
|
||||
if (renderCommands.size() != objects.size()) {
|
||||
std::cerr << "GPU render test: Frame " << frame << " produced "
|
||||
<< renderCommands.size() << " commands, expected " << objects.size() << '\n';
|
||||
++failures;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
backend.BeginFrame(device);
|
||||
|
||||
// Validate cube is animating (matrix should change between frames)
|
||||
if (frame == 0) {
|
||||
for (size_t i = 0; i < renderCommands.size(); ++i) {
|
||||
if (objects[i].shaderKeys.empty()) continue;
|
||||
const auto& key = objects[i].shaderKeys.front();
|
||||
if (key == "floor" && objects[i].vertices.size() < 100) {
|
||||
// This is the cube - verify it has non-identity matrix (spinning)
|
||||
bool hasRotation = false;
|
||||
const auto& matrix = renderCommands[i].modelMatrix;
|
||||
// Check off-diagonal elements for rotation
|
||||
if (std::abs(matrix[1]) > 0.01f || std::abs(matrix[2]) > 0.01f ||
|
||||
std::abs(matrix[4]) > 0.01f || std::abs(matrix[6]) > 0.01f ||
|
||||
std::abs(matrix[8]) > 0.01f || std::abs(matrix[9]) > 0.01f) {
|
||||
hasRotation = true;
|
||||
}
|
||||
if (!hasRotation) {
|
||||
std::cerr << "GPU render test: Cube is not spinning (rotation matrix is identity)\n";
|
||||
++failures;
|
||||
success = false;
|
||||
}
|
||||
break;
|
||||
|
||||
backend.BeginFrame(device);
|
||||
|
||||
// Validate cube is animating (matrix should change between frames)
|
||||
if (frame == 0) {
|
||||
for (size_t i = 0; i < renderCommands.size(); ++i) {
|
||||
if (objects[i].shaderKeys.empty()) continue;
|
||||
const auto& key = objects[i].shaderKeys.front();
|
||||
if (key == "floor" && objects[i].vertices.size() < 100) {
|
||||
// This is the cube - verify it has non-identity matrix (spinning)
|
||||
bool hasRotation = false;
|
||||
const auto& matrix = renderCommands[i].modelMatrix;
|
||||
// Check off-diagonal elements for rotation
|
||||
if (std::abs(matrix[1]) > 0.01f || std::abs(matrix[2]) > 0.01f ||
|
||||
std::abs(matrix[4]) > 0.01f || std::abs(matrix[6]) > 0.01f ||
|
||||
std::abs(matrix[8]) > 0.01f || std::abs(matrix[9]) > 0.01f) {
|
||||
hasRotation = true;
|
||||
}
|
||||
if (!hasRotation) {
|
||||
std::cerr << "GPU render test: Cube is not spinning (rotation matrix is identity)\n";
|
||||
++failures;
|
||||
success = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
backend.EndFrame(device);
|
||||
}
|
||||
|
||||
backend.DestroyDevice(device);
|
||||
sceneService->Shutdown();
|
||||
engineService->Shutdown();
|
||||
|
||||
std::cout << "GPU render test: Successfully rendered and validated scene pipeline\n";
|
||||
|
||||
backend.EndFrame(device);
|
||||
}
|
||||
|
||||
backend.DestroyDevice(device);
|
||||
sceneService->Shutdown();
|
||||
engineService->Shutdown();
|
||||
|
||||
std::cout << "GPU render test: Successfully rendered and validated scene pipeline\n";
|
||||
}
|
||||
|
||||
backend.Shutdown();
|
||||
@@ -552,7 +557,8 @@ void RunCubeDemoSceneTests(int& failures) {
|
||||
}
|
||||
|
||||
auto logger = std::make_shared<sdl3cpp::services::impl::LoggerService>();
|
||||
auto configService = std::make_shared<CubeDemoConfigService>(scriptPath, *configJson, "vulkan");
|
||||
const std::string preferredRenderer = DeterminePreferredRenderer();
|
||||
auto configService = std::make_shared<CubeDemoConfigService>(scriptPath, *configJson, preferredRenderer);
|
||||
auto meshService = std::make_shared<sdl3cpp::services::impl::MeshService>(configService, logger);
|
||||
auto audioService = std::make_shared<StubAudioCommandService>();
|
||||
auto physicsService = std::make_shared<sdl3cpp::services::impl::PhysicsBridgeService>(logger);
|
||||
|
||||
Reference in New Issue
Block a user