Files
MicrosoftIris/UIX.RenderApi.OpenGL/Engine/GLRenderEngine.cs
T

147 lines
5.4 KiB
C#
Raw Normal View History

2026-07-25 15:24:59 -05:00
using System;
using System.Threading;
2026-07-25 19:11:36 -05:00
using Silk.NET.Input;
2026-07-25 15:24:59 -05:00
using Silk.NET.Maths;
using Silk.NET.OpenGL;
using Silk.NET.Windowing;
// Disambiguate from Microsoft.Iris.Render.WindowOptions (enum) and this class's Window property.
using SilkWindowOptions = Silk.NET.Windowing.WindowOptions;
using SilkWindowFactory = Silk.NET.Windowing.Window;
namespace Microsoft.Iris.Render.OpenGL
{
/// <summary>
/// In-process, OpenGL-backed render engine. Owns a single Silk.NET window and its GL
/// context, pumps native events on demand and renders the visual tree each frame.
/// </summary>
public sealed class GLRenderEngine : IRenderEngine
{
private readonly IRenderHost m_host;
private readonly IWindow m_silkWindow;
private readonly GLRenderSession m_session;
private readonly GLRenderWindow m_window;
private GL? m_gl;
private SceneRenderer? m_renderer;
private GLDisplayManager? m_displayManager;
2026-07-25 19:11:36 -05:00
private IInputContext? m_inputContext;
private GLInputTranslator? m_inputTranslator;
2026-07-25 15:24:59 -05:00
private GraphicsRenderingQuality m_quality;
private SoundDeviceType m_soundType;
private volatile bool m_wakeRequested;
private readonly ManualResetEventSlim m_windowLoaded = new(false);
2026-07-25 15:24:59 -05:00
public GLRenderEngine(EngineInfo engineInfo, IRenderHost renderHost)
2026-07-25 15:24:59 -05:00
{
m_host = renderHost;
var options = SilkWindowOptions.Default;
options.Title = "Iris";
options.API = new GraphicsAPI(
ContextAPI.OpenGL, ContextProfile.Core, ContextFlags.Default, new APIVersion(3, 3));
options.IsVisible = true;
options.ShouldSwapAutomatically = true;
m_silkWindow = SilkWindowFactory.Create(options);
m_session = new GLRenderSession();
m_window = new GLRenderWindow(m_silkWindow, m_session);
m_silkWindow.Load += OnLoad;
m_silkWindow.Initialize();
m_windowLoaded.Wait();
2026-07-25 15:24:59 -05:00
m_silkWindow.Render += OnRender;
m_silkWindow.Resize += _ => m_window.RaiseResize();
m_silkWindow.Move += _ => m_window.RaiseMove();
m_silkWindow.Closing += m_window.RaiseClose;
m_silkWindow.FocusChanged += m_window.RaiseActivation;
}
public IRenderSession Session => m_session;
public IRenderWindow Window => m_window;
public IDisplayManager DisplayManager => m_displayManager ?? throw new InvalidOperationException("Engine not initialized");
public void Initialize(
GraphicsDeviceType typeGraphics,
GraphicsRenderingQuality renderingQuality,
SoundDeviceType typeSound)
{
m_quality = renderingQuality;
m_soundType = typeSound;
// Window/GL creation is completed when GLRenderWindow.Initialize() runs and
// Silk raises Load (see OnLoad); we only latch the requested settings here.
}
private void OnLoad()
{
m_gl = m_silkWindow.CreateOpenGL();
2026-07-25 15:24:59 -05:00
m_renderer = new SceneRenderer(m_gl);
m_displayManager = new GLDisplayManager(m_silkWindow);
m_session.GraphicsDevice = new GLGraphicsDevice(m_gl, m_quality, RenderNow);
m_session.SoundDevice = new GLSoundDevice(m_soundType);
2026-07-25 19:11:36 -05:00
m_inputContext = m_silkWindow.CreateInput();
m_inputTranslator = new GLInputTranslator(m_inputContext, (GLInputSystem)m_session.InputSystem, m_window);
m_windowLoaded.Set();
2026-07-25 15:24:59 -05:00
m_window.RaiseLoad();
}
private void OnRender(double deltaSeconds)
{
if (m_renderer == null)
return;
m_renderer.BeginFrame(m_window.Width, m_window.Height, m_window.BackgroundColor);
m_window.Root.Render(m_renderer, Matrix4X4<float>.Identity, 1f);
}
private void RenderNow()
{
if (m_window.IsLoaded && !m_silkWindow.IsClosing)
m_silkWindow.DoRender();
}
private bool m_didWork = false;
2026-07-25 15:24:59 -05:00
public bool ProcessNativeEvents()
{
m_silkWindow.DoEvents();
m_didWork = !m_didWork;
return m_didWork;
2026-07-25 15:24:59 -05:00
}
public void WaitForWork(uint nTimeoutInMsecs)
{
// Cooperative wait: return promptly if another thread requested a wake.
uint waited = 0;
const uint slice = 5;
// while (waited < nTimeoutInMsecs && !m_wakeRequested)
// {
// Thread.Sleep((int)Math.Min(slice, nTimeoutInMsecs - waited));
// waited += slice;
// }
2026-07-25 15:24:59 -05:00
m_wakeRequested = false;
}
public void InterThreadWake() => m_wakeRequested = true;
public void FlushBatch() => RenderNow();
public bool IsGraphicsDeviceAvailable(GraphicsDeviceType type, bool fFilterRecommended)
=> type == GraphicsDeviceType.Direct3D9 || type == GraphicsDeviceType.Gdi;
public bool IsSoundDeviceAvailable(SoundDeviceType type)
=> type == SoundDeviceType.WaveAudio || type == SoundDeviceType.DirectSound8;
public void Dispose()
{
2026-07-25 19:11:36 -05:00
m_inputTranslator?.Dispose();
2026-07-25 15:24:59 -05:00
m_renderer?.Dispose();
m_session.Dispose();
if (!m_silkWindow.IsClosing)
m_silkWindow.Close();
m_silkWindow.Dispose();
}
}
}