feat: AI screenshot capture for visual analysis

- Add ScreenshotCapture class to Engine.Graphics using Vulkan image readback.
- Integrate screenshot capture into MeshRenderer; request triggers readback on next frame.
- Add SixLabors.ImageSharp 3.1.11 for PNG encoding (patched for CVE-2025-54575).
- Add capture_screenshot AI command and MCP tool.
- Wire screenshot request into Program.cs with demo command.
- Expose swapchain surface format and image accessor for readback.
- Add Screenshots/ to .gitignore.
- Update CORTEX_ENGINE_ARCHITECTURE.md with MCP, Silk.NET.Vulkan, ImageSharp, and screenshot capture.
This commit is contained in:
emil28092005
2026-06-16 20:09:57 +03:00
parent a9c783d204
commit 9312810f0c
11 changed files with 405 additions and 37 deletions
+11 -1
View File
@@ -16,13 +16,15 @@ public sealed class AiCommandProcessor
{
private readonly World _world;
private readonly Func<string, Mesh> _modelLoader;
private readonly Action<string> _requestScreenshot;
public JsonSerializerOptions JsonOptions { get; }
public AiCommandProcessor(World world, Func<string, Mesh> modelLoader)
public AiCommandProcessor(World world, Func<string, Mesh> modelLoader, Action<string> requestScreenshot)
{
_world = world;
_modelLoader = modelLoader;
_requestScreenshot = requestScreenshot;
JsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
@@ -52,6 +54,7 @@ public sealed class AiCommandProcessor
SetTransformCommand c => SetTransform(c),
DeleteEntityCommand c => DeleteEntity(c),
ListEntitiesCommand => ListEntities(),
CaptureScreenshotCommand c => CaptureScreenshot(c),
_ => AiCommandResult.Error($"Unknown command type: {command.Type}")
};
}
@@ -122,4 +125,11 @@ public sealed class AiCommandProcessor
var json = JsonSerializer.Serialize(names, JsonOptions);
return AiCommandResult.Ok(json);
}
private AiCommandResult CaptureScreenshot(CaptureScreenshotCommand command)
{
var path = command.OutputPath ?? $"screenshot_{DateTime.UtcNow:yyyyMMdd_HHmmss_fff}.png";
_requestScreenshot(path);
return AiCommandResult.Ok($"Screenshot requested: {path}");
}
}
+1
View File
@@ -11,6 +11,7 @@ namespace Engine.AI.Commands;
[JsonDerivedType(typeof(SetTransformCommand), "set_transform")]
[JsonDerivedType(typeof(DeleteEntityCommand), "delete_entity")]
[JsonDerivedType(typeof(ListEntitiesCommand), "list_entities")]
[JsonDerivedType(typeof(CaptureScreenshotCommand), "capture_screenshot")]
public abstract record AiCommand
{
public string Type => GetType().Name.Replace("Command", "").ToLowerInvariant();
@@ -0,0 +1,12 @@
namespace Engine.AI.Commands;
/// <summary>
/// Request a screenshot of the current rendered frame for AI visual analysis.
/// </summary>
public sealed record CaptureScreenshotCommand : AiCommand
{
/// <summary>
/// Output file path. If omitted, the engine chooses a default path.
/// </summary>
public string? OutputPath { get; init; }
}
+7
View File
@@ -70,6 +70,13 @@ public sealed class EngineMcpTools
return EnqueueAndReturnMessage(cmd);
}
[McpServerTool, Description("Capture a screenshot of the current rendered frame and save it to disk.")]
public Task<string> CaptureScreenshot([Description("Optional output file path (default: screenshot_<timestamp>.png)")] string? outputPath = null)
{
var cmd = new CaptureScreenshotCommand { OutputPath = outputPath };
return EnqueueAndReturnMessage(cmd);
}
private async Task<string> EnqueueAndReturnMessage(AiCommand command)
{
var result = await _queue.EnqueueAsync(command).ConfigureAwait(false);