using Engine.Kernel.Plugins; using Engine.Kernel.Scheduling; using Engine.Kernel.World; using Engine.Windowing.Contracts; using Silk.NET.OpenGL; namespace Engine.Render; /// /// M1's minimal render pipeline: clears the window to a color and swaps /// buffers, once per Render stage. No Contracts assembly — nothing here is /// a type another plugin needs to reference yet (see the null-Contracts /// note on PluginManifest). See M1 in docs/kernel-contract.md §8. /// /// This is the whole point of M1's "done when": change ClearColor, rebuild /// just this plugin, and reload it while the window from engine.windowing /// stays open — the color changes with no app restart. Verified by hand, /// not by an automated test — opening a real window needs a real display, /// which isn't something to assume of every environment this runs in. /// /// engine.windowing alone produces a window that never becomes visible on /// Wayland — unlike X11, a Wayland surface with no committed buffer simply /// isn't shown by the compositor, so an "empty" window isn't even a black /// rectangle, it's nothing at all. This plugin's first Clear+SwapBuffers is /// what actually makes the window appear. /// public sealed class RenderPlugin : IPlugin { private static readonly float[] ClearColor = [0.25f, 0.55f, 0.85f, 1f]; private GL? _gl; private IEngineWindow? _window; public void Configure(IPluginContext ctx) { _window = ctx.Services.Require(); _window.Native.GLContext!.MakeCurrent(); _gl = _window.Native.CreateOpenGL(); ctx.Schedule.Add(Stage.Render, Draw); ctx.Log.Info("GL context created"); } public void Shutdown(IPluginContext ctx) { ctx.Schedule.RemoveAllFrom("engine.render"); _gl?.Dispose(); _gl = null; _window = null; } private void Draw(IWorld world) { _gl!.ClearColor(ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]); _gl.Clear(ClearBufferMask.ColorBufferBit); _window!.Native.GLContext!.SwapBuffers(); } }