Рендер-пайплайны
Интерфейс IGraphicsPipeline
Каждый рендер-бэкэнд (OpenGL, Vulkan, D3D12) реализует IGraphicsPipeline:
public interface IGraphicsPipeline
{
IRenderContext Context { get; }
void OnInit(ref Silk.NET.Windowing.WindowOptions options);
void OnLoad(GameWindow window);
void Cleanup();
}
Реализация пайплайна
public class MyGraphicsPipeline : IGraphicsPipeline
{
public IRenderContext Context { get; private set; }
private readonly List<GraphicsLoadingTask> _loaders;
public MyGraphicsPipeline(List<GraphicsLoadingTask> loaders)
{
_loaders = loaders;
// не создавайте контекст здесь — это до создания окна
}
public void OnInit(ref Silk.NET.Windowing.WindowOptions options)
{
// Опционально: изменить опции Silk.NET перед созданием окна
// options.PreferredStencilBufferBits = 8;
// options.Samples = 4;
}
public void OnLoad(GameWindow window)
{
// Создаём контекст графики (окно уже создано)
Context = new MyRenderContext(window.Window);
// Загружаем все задачи из очереди
foreach (var task in _loaders)
{
try
{
task.Load();
}
catch (Exception ex)
{
Igdrasil.Logger.Error("Failed to load task {0}: {1}", task, ex.Message);
}
}
}
public void Cleanup()
{
// Выгружаем все задачи в обратном порядке
for (int i = _loaders.Count - 1; i >= 0; i--)
{
try
{
_loaders[i].Unload();
}
catch (Exception ex)
{
Igdrasil.Logger.Error("Failed to unload task: {0}", ex.Message);
}
}
Context?.Dispose();
Context = null;
}
}
Порядок инициализации
Igdrasil.InitializeWindow(options)
↓
GameWindow создаётся с выбранным пайплайном
↓
Пайплайн.OnInit (может изменить Silk.NET опции)
↓
Silk.NET окно создаётся
↓
Пайплайн.OnLoad (загрузка GPU-ресурсов)
↓
Igdrasil.Start()
↓
Igdrasil.OnLoad (пользовательский код)
↓
Основной цикл:
- Igdrasil.OnUpdate
- Пайплайн.OnRender (через GameWindow.OnRender)
- SwapBuffers
IRenderContext
Контекст содержит низкоуровневый доступ к API (OpenGL, Vulkan и т.д.):
// Получение контекста в пользовательском коде
var context = Igdrasil.Context;
// Типичное использование в пайплайне рендера
Igdrasil.OnRender += (ctx, dt) =>
{
ctx.Clear(Colors.Black);
// рисование сцены
ctx.Present();
};
Реальные методы IRenderContext определены в конкретной реализации (например, OpenGL context), не в интерфейсе.
Регистрация и переключение
// Регистрируем несколько движков
GameWindow.AddRenderEngine("OpenGL", loaders => new OpenGLPipeline(loaders));
GameWindow.AddRenderEngine("Vulkan", loaders => new VulkanPipeline(loaders));
// Выбираем при создании окна
var options = new WindowOptions { Engine = "OpenGL" };
Igdrasil.InitializeWindow(options);
// Переключаемся во время работы
Igdrasil.Window.Engine = "Vulkan"; // старый очищен, новый инициализирован
Обработка ошибок
public class SafePipeline : IGraphicsPipeline
{
public IRenderContext Context { get; private set; }
private readonly List<GraphicsLoadingTask> _loaders;
private readonly ILogger _logger;
public SafePipeline(List<GraphicsLoadingTask> loaders)
{
_loaders = loaders;
_logger = Igdrasil.GetLogger("Pipeline");
}
public void OnLoad(GameWindow window)
{
try
{
Context = CreateContext(window.Window);
_logger.Info("Context created");
}
catch (Exception ex)
{
_logger.Fatal("Failed to create context: {0}", ex.Message);
throw;
}
// Загружаем с обработкой ошибок
foreach (var task in _loaders)
{
try
{
task.Load();
_logger.Debug("Loaded: {0}", task);
}
catch (Exception ex)
{
_logger.Warning("Failed to load {0}: {1}", task, ex.Message);
// решить: отменить всё или продолжить?
}
}
}
// ... остаток реализации
}
Версия: 1.0