Введение
Я только что подумал о новом шаблоне дизайна.Мне интересно, существует ли оно, а если нет, то почему (или почему я не должен его использовать).
Я создаю игру с использованием OpenGL.В OpenGL вы часто хотите «связать» вещи - т.е. сделать их текущим контекстом на некоторое время, а затем отменить их привязку.Например, вы можете позвонить glBegin(GL_TRIANGLES)
, затем нарисовать несколько треугольников, а затем позвонить glEnd()
.Мне нравится делать отступы между этими вещами, чтобы было ясно, где они начинаются и заканчиваются, но тогда моя среда разработки любит удалять их, потому что нет скобок.Тогда я подумал, что мы могли бы сделать что-нибудь умное!В основном это работает так:
using(GL.Begin(GL_BeginMode.Triangles)) {
// draw stuff
}
GL.Begin
возвращает специальный объект DrawBind
(с внутренним конструктором) и реализует IDisposable
, так что он автоматически вызывает GL.End()
в конце блока,Таким образом, все остается хорошо выровненным, и вы не можете забыть вызвать end ().
Есть ли имя для этого шаблона?
Обычно, когда я вижу using
используется, вы используетеэто так:
using(var x = new Whatever()) {
// do stuff with `x`
}
Но в этом случае нам не нужно вызывать какие-либо методы для нашего «используемого» объекта, поэтому нам не нужно присваивать его чему-либо, и это не имеет смыслакроме вызова соответствующей конечной функции.
Пример
Для Энтони Пеграм , которому нужен реальный пример кода, над которым я сейчас работаю:
Перед рефакторингом:
public void Render()
{
_vao.Bind();
_ibo.Bind(BufferTarget.ElementArrayBuffer);
GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
BufferObject.Unbind(BufferTarget.ElementArrayBuffer);
VertexArrayObject.Unbind();
}
После рефакторинга:
public void Render()
{
using(_vao.Bind())
using(_ibo.Bind(BufferTarget.ElementArrayBuffer))
{
GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
}
}
Обратите внимание, что объект получает 2-е преимуществовозвращенный _ibo.Bind
также запоминает, какой "BufferTarget" я хочу отменить.Это также привлекает ваше внимание к GL.DrawElements
, который на самом деле является единственным значительным утверждением в этой функции (которое делает что-то заметное), и скрывает эти длинные операторы unbind.
Я предполагаю, что единственным недостатком является то, что я могуне чередуйте цели буфера с этим методом.Я не уверен, когда захочу, но мне нужно было бы сохранить ссылку для привязки объекта и вызвать Dispose вручную или вызвать функцию завершения вручную.
Именование
Если никто не возражает, я дублирую эту идиому Одноразовый объект контекста (DCO) .
Проблемы
JasonTrue поднял хорошую мысль, что в этом сценарии (буферы OpenGL), вложенные с помощью операторов, не будут работать должным образом, так как только один буфер можетбыть связанным за один раз.Однако это можно исправить, расширив «объект связывания», чтобы использовать стеки:
public class BufferContext : IDisposable
{
private readonly BufferTarget _target;
private static readonly Dictionary<BufferTarget, Stack<int>> _handles;
static BufferContext()
{
_handles = new Dictionary<BufferTarget, Stack<int>>();
}
internal BufferContext(BufferTarget target, int handle)
{
_target = target;
if (!_handles.ContainsKey(target)) _handles[target] = new Stack<int>();
_handles[target].Push(handle);
GL.BindBuffer(target, handle);
}
public void Dispose()
{
_handles[_target].Pop();
int handle = _handles[_target].Count > 0 ? _handles[_target].Peek() : 0;
GL.BindBuffer(_target, handle);
}
}
Редактировать: Только что заметил проблему с этим.Раньше, если вы не Dispose()
вашего объекта контекста, на самом деле не было никаких последствий.Контекст просто не переключился бы на то, что было.Теперь, если вы забудете избавиться от него внутри какой-то петли, вы получите stackoverflow .Возможно, мне следует ограничить размер стека ...