feat: switch default backend to Silk.NET OpenGL

- OpenGLRenderContext: Initialize() + CreateOpenGL() for non-blocking
- OpenGLWindow: Initialize() instead of Run(), CreateGL() method
- Program.cs: default backend is now 'opengl', SwapBuffers per frame
- ImGui guarded: only initialized for RaylibRenderer, ObjectManipulator
  checks _imGuiAvailable before calling ImGui.GetIO()
- 66/66 tests, 145 FPS with OpenGL backend
This commit is contained in:
emil28092005
2026-06-17 20:14:15 +03:00
parent cc9a1a4602
commit 417915e861
6 changed files with 40 additions and 23 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ Size=250,398
Collapsed=0 Collapsed=0
[Window][Inspector] [Window][Inspector]
Pos=36,302 Pos=112,297
Size=300,425 Size=300,425
Collapsed=0 Collapsed=0
@@ -26,6 +26,7 @@
<ProjectReference Include="..\Engine.Graphics.Vulkan\Engine.Graphics.Vulkan.csproj" /> <ProjectReference Include="..\Engine.Graphics.Vulkan\Engine.Graphics.Vulkan.csproj" />
<ProjectReference Include="..\Engine.AI\Engine.AI.csproj" /> <ProjectReference Include="..\Engine.AI\Engine.AI.csproj" />
<ProjectReference Include="..\Engine.Physics\Engine.Physics.csproj" /> <ProjectReference Include="..\Engine.Physics\Engine.Physics.csproj" />
<ProjectReference Include="..\Engine.Graphics.OpenGL\Engine.Graphics.OpenGL.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+5 -1
View File
@@ -28,10 +28,14 @@ public sealed class ObjectManipulator
/// Process input for object picking and dragging. /// Process input for object picking and dragging.
/// Returns true if input was consumed (ImGui should be ignored). /// Returns true if input was consumed (ImGui should be ignored).
/// </summary> /// </summary>
private bool _imGuiAvailable;
public void SetImGuiAvailable(bool available) => _imGuiAvailable = available;
public bool ProcessInput(World world, Camera camera, IInputState input) public bool ProcessInput(World world, Camera camera, IInputState input)
{ {
// Skip if ImGui is capturing mouse // Skip if ImGui is capturing mouse
if (ImGuiNET.ImGui.GetIO().WantCaptureMouse) if (_imGuiAvailable && ImGuiNET.ImGui.GetIO().WantCaptureMouse)
{ {
_isDragging = false; _isDragging = false;
return false; return false;
+18 -11
View File
@@ -11,6 +11,7 @@ using Engine.Core;
using Engine.Core.Components; using Engine.Core.Components;
using Engine.Graphics; using Engine.Graphics;
using Engine.Graphics.Loaders; using Engine.Graphics.Loaders;
using Engine.Graphics.OpenGL;
using Engine.Graphics.RaylibBackend; using Engine.Graphics.RaylibBackend;
using Engine.Graphics.Vulkan; using Engine.Graphics.Vulkan;
using Engine.Physics; using Engine.Physics;
@@ -43,19 +44,22 @@ class Program
RaylibBackendRegistrar.EnsureRegistered(); RaylibBackendRegistrar.EnsureRegistered();
VulkanBackendRegistrar.EnsureRegistered(); VulkanBackendRegistrar.EnsureRegistered();
using var renderContext = RenderBackendFactory.Create("raylib", 1280, 720, enableValidation: false); OpenGLBackendRegistrar.EnsureRegistered();
using var renderContext = RenderBackendFactory.Create("opengl", 1280, 720, enableValidation: false);
var window = renderContext.Window; var window = renderContext.Window;
var input = window.Input; var input = window.Input;
using var renderer = renderContext.CreateRenderer(); using var renderer = renderContext.CreateRenderer();
// ImGui editor layer // ImGui editor layer (Raylib only)
var imGuiLayer = new ImGuiLayer(); ImGuiLayer? imGuiLayer = null;
var objectManipulator = new ObjectManipulator(); var objectManipulator = new ObjectManipulator();
if (!cameraTour) objectManipulator.SetImGuiAvailable(false);
if (!cameraTour && renderer is RaylibRenderer rlRenderer)
{ {
imGuiLayer = new ImGuiLayer();
imGuiLayer.Initialize(); imGuiLayer.Initialize();
if (renderer is RaylibRenderer rlRenderer) rlRenderer.ImGuiLayer = imGuiLayer;
rlRenderer.ImGuiLayer = imGuiLayer; objectManipulator.SetImGuiAvailable(true);
} }
var (modelPath, mcpPort) = ParseArgs(args); var (modelPath, mcpPort) = ParseArgs(args);
@@ -283,15 +287,18 @@ class Program
} }
// Feed frame data to ImGui before rendering. // Feed frame data to ImGui before rendering.
imGuiLayer.SetFrameData(world, timing, currentFps); if (imGuiLayer != null)
imGuiLayer.SetFrameData(world, timing, currentFps);
// Sync selection between manipulator and ImGui objectManipulator.SetImGuiAvailable(imGuiLayer != null);
if (objectManipulator.IsDragging && !string.IsNullOrEmpty(objectManipulator.SelectedEntityName))
imGuiLayer.SetSelectedEntity(objectManipulator.SelectedEntityName);
renderer.RenderWorld(world); renderer.RenderWorld(world);
queue.CompletePendingScreenshots(); queue.CompletePendingScreenshots();
// Swap buffers for OpenGL backend
if (renderContext is OpenGLRenderContext glCtx2)
glCtx2.SwapBuffers();
frames++; frames++;
if (timing.TotalTime - lastFpsTime >= 1.0) if (timing.TotalTime - lastFpsTime >= 1.0)
{ {
@@ -303,7 +310,7 @@ class Program
} }
Console.WriteLine("Shutting down..."); Console.WriteLine("Shutting down...");
imGuiLayer.Dispose(); imGuiLayer?.Dispose();
#if !RELEASE_AOT #if !RELEASE_AOT
if (mcpApp != null) if (mcpApp != null)
await mcpApp.StopAsync(); await mcpApp.StopAsync();
@@ -1,7 +1,9 @@
using Engine.Core; using Engine.Core;
using Engine.Graphics; using Engine.Graphics;
using Silk.NET.OpenGL; using Silk.NET.OpenGL;
using Silk.NET.Windowing;
using SilkWindow = Silk.NET.Windowing.IWindow; using SilkWindow = Silk.NET.Windowing.IWindow;
using CoreIWindow = Engine.Core.IWindow;
namespace Engine.Graphics.OpenGL; namespace Engine.Graphics.OpenGL;
@@ -12,16 +14,15 @@ public sealed class OpenGLRenderContext : IRenderContext
private OpenGLRenderer? _renderer; private OpenGLRenderer? _renderer;
private bool _disposed; private bool _disposed;
public IWindow Window => _window; public CoreIWindow Window => _window;
public OpenGLRenderContext(int width, int height, bool enableValidation = false) public OpenGLRenderContext(int width, int height, bool enableValidation = false)
{ {
_window = new OpenGLWindow("Cortex Engine (OpenGL)", width, height); _window = new OpenGLWindow("Cortex Engine (OpenGL)", width, height);
} // Initialize() triggers the Load callback which creates the GL context
_window.SilkView.Initialize();
public void OnLoad() // After Initialize, CreateOpenGL should work
{ _gl = _window.CreateGL();
_gl = _window.SilkView.CreateOpenGL();
_window.OnLoad(); _window.OnLoad();
} }
@@ -31,10 +32,9 @@ public sealed class OpenGLRenderContext : IRenderContext
return _renderer; return _renderer;
} }
public void Resize(int width, int height) public void Resize(int width, int height) => _renderer?.SetScreenSize(width, height);
{
_renderer?.SetScreenSize(width, height); public void SwapBuffers() => _window.SilkView.SwapBuffers();
}
public void Dispose() public void Dispose()
{ {
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Engine.Core; using Engine.Core;
using Silk.NET.Input; using Silk.NET.Input;
using Silk.NET.OpenGL;
using Silk.NET.Windowing; using Silk.NET.Windowing;
using SilkWindow = Silk.NET.Windowing.IWindow; using SilkWindow = Silk.NET.Windowing.IWindow;
@@ -31,6 +32,8 @@ public sealed class OpenGLWindow : Engine.Core.IWindow, IDisposable
_silkWindow = Window.Create(options); _silkWindow = Window.Create(options);
_silkWindow.Closing += () => _shouldClose = true; _silkWindow.Closing += () => _shouldClose = true;
// Initialize without Run() — enables non-blocking event loop
_silkWindow.Initialize();
} }
public void OnLoad() public void OnLoad()
@@ -39,6 +42,8 @@ public sealed class OpenGLWindow : Engine.Core.IWindow, IDisposable
_input.Initialize(inputContext); _input.Initialize(inputContext);
} }
public GL CreateGL() => _silkWindow.CreateOpenGL();
public void PumpEvents() => _silkWindow.DoEvents(); public void PumpEvents() => _silkWindow.DoEvents();
public void Close() => _shouldClose = true; public void Close() => _shouldClose = true;
public string[] GetRequiredVulkanExtensions() => Array.Empty<string>(); public string[] GetRequiredVulkanExtensions() => Array.Empty<string>();