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",
|
"type": "luma_range",
|
||||||
"min_luma": 0.01,
|
"min_luma": 0.01,
|
||||||
"max_luma": 0.95
|
"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.
|
Roadmap discipline.
|
||||||
If you run out of ROADMAP.md tasks, stop and report.
|
If you run out of ROADMAP.md tasks, stop and report.
|
||||||
We should reassess priorities before inventing work.
|
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";
|
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::optional<std::string> ReadFileContents(const std::filesystem::path& path) {
|
||||||
std::ifstream input(path);
|
std::ifstream input(path);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
@@ -385,154 +398,146 @@ bool RunGpuRenderTest(int& failures,
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
std::cout << "GPU render test: Validating full render pipeline with scene geometry\n";
|
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
|
// Load and render the actual cube scene to catch color, geometry, and animation issues
|
||||||
auto scriptPath = GetCubeScriptPath();
|
auto scriptPath = GetCubeScriptPath();
|
||||||
auto configPath = GetSeedConfigPath();
|
auto sceneConfigService = configService;
|
||||||
auto configJson = ReadFileContents(configPath);
|
auto meshService = std::make_shared<sdl3cpp::services::impl::MeshService>(sceneConfigService, logger);
|
||||||
if (!configJson) {
|
auto audioService = std::make_shared<StubAudioCommandService>();
|
||||||
std::cerr << "GPU render test failed: could not load scene config\n";
|
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;
|
++failures;
|
||||||
success = false;
|
success = false;
|
||||||
} else {
|
}
|
||||||
auto sceneConfigService = std::make_shared<CubeDemoConfigService>(scriptPath, *configJson, "vulkan");
|
|
||||||
auto meshService = std::make_shared<sdl3cpp::services::impl::MeshService>(sceneConfigService, logger);
|
// Validate all geometry is present
|
||||||
auto audioService = std::make_shared<StubAudioCommandService>();
|
bool hasFloor = false;
|
||||||
auto physicsService = std::make_shared<sdl3cpp::services::impl::PhysicsBridgeService>(logger);
|
bool hasCeiling = false;
|
||||||
|
int wallCount = 0;
|
||||||
auto engineService = std::make_shared<sdl3cpp::services::impl::ScriptEngineService>(
|
int lanternCount = 0;
|
||||||
scriptPath,
|
bool hasCube = false;
|
||||||
logger,
|
|
||||||
meshService,
|
for (const auto& obj : objects) {
|
||||||
audioService,
|
const auto& type = obj.objectType;
|
||||||
physicsService,
|
|
||||||
nullptr,
|
if (type == "floor") {
|
||||||
nullptr,
|
hasFloor = true;
|
||||||
sceneConfigService,
|
} else if (type == "ceiling") {
|
||||||
false);
|
hasCeiling = true;
|
||||||
engineService->Initialize();
|
} else if (type == "wall") {
|
||||||
|
wallCount++;
|
||||||
auto sceneScriptService = std::make_shared<sdl3cpp::services::impl::SceneScriptService>(engineService, logger);
|
} else if (type == "lantern") {
|
||||||
auto objects = sceneScriptService->LoadSceneObjects();
|
lanternCount++;
|
||||||
|
} else if (type == "physics_cube" || type == "spinning_cube") {
|
||||||
if (objects.size() != 15) {
|
hasCube = true;
|
||||||
std::cerr << "GPU render test: Scene loaded " << objects.size() << " objects, expected 15\n";
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
++failures;
|
||||||
success = false;
|
success = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate all geometry is present
|
backend.BeginFrame(device);
|
||||||
bool hasFloor = false;
|
|
||||||
bool hasCeiling = false;
|
// Validate cube is animating (matrix should change between frames)
|
||||||
int wallCount = 0;
|
if (frame == 0) {
|
||||||
int lanternCount = 0;
|
for (size_t i = 0; i < renderCommands.size(); ++i) {
|
||||||
bool hasCube = false;
|
if (objects[i].shaderKeys.empty()) continue;
|
||||||
|
const auto& key = objects[i].shaderKeys.front();
|
||||||
for (const auto& obj : objects) {
|
if (key == "floor" && objects[i].vertices.size() < 100) {
|
||||||
const auto& type = obj.objectType;
|
// This is the cube - verify it has non-identity matrix (spinning)
|
||||||
|
bool hasRotation = false;
|
||||||
if (type == "floor") {
|
const auto& matrix = renderCommands[i].modelMatrix;
|
||||||
hasFloor = true;
|
// Check off-diagonal elements for rotation
|
||||||
} else if (type == "ceiling") {
|
if (std::abs(matrix[1]) > 0.01f || std::abs(matrix[2]) > 0.01f ||
|
||||||
hasCeiling = true;
|
std::abs(matrix[4]) > 0.01f || std::abs(matrix[6]) > 0.01f ||
|
||||||
} else if (type == "wall") {
|
std::abs(matrix[8]) > 0.01f || std::abs(matrix[9]) > 0.01f) {
|
||||||
wallCount++;
|
hasRotation = true;
|
||||||
} 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;
|
|
||||||
}
|
}
|
||||||
|
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);
|
backend.EndFrame(device);
|
||||||
sceneService->Shutdown();
|
|
||||||
engineService->Shutdown();
|
|
||||||
|
|
||||||
std::cout << "GPU render test: Successfully rendered and validated scene pipeline\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend.DestroyDevice(device);
|
||||||
|
sceneService->Shutdown();
|
||||||
|
engineService->Shutdown();
|
||||||
|
|
||||||
|
std::cout << "GPU render test: Successfully rendered and validated scene pipeline\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
backend.Shutdown();
|
backend.Shutdown();
|
||||||
@@ -552,7 +557,8 @@ void RunCubeDemoSceneTests(int& failures) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto logger = std::make_shared<sdl3cpp::services::impl::LoggerService>();
|
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 meshService = std::make_shared<sdl3cpp::services::impl::MeshService>(configService, logger);
|
||||||
auto audioService = std::make_shared<StubAudioCommandService>();
|
auto audioService = std::make_shared<StubAudioCommandService>();
|
||||||
auto physicsService = std::make_shared<sdl3cpp::services::impl::PhysicsBridgeService>(logger);
|
auto physicsService = std::make_shared<sdl3cpp::services::impl::PhysicsBridgeService>(logger);
|
||||||
|
|||||||
Reference in New Issue
Block a user