2012-04-23 2 views
4

Я собираюсь реализовать класс управляемых оболочек вокруг текстуры OpenGL, и я хочу, чтобы финализатор объекта вызывал glDeleteTextures.Правильный способ вызова glDeleteTextures в финализаторе объекта .net

Итак, поток, вызывающий финализатор (поток GC?), Должен быть привязан к контексту рендеринга OpenGL, в котором текстура принадлежит, вызывая wglMakeCurrent.

В документации wglMakeCurrent четко указано, что контекст рендеринга OpenGL не может быть текущим контекстом визуализации нескольких потоков одновременно.

Если GC может запускаться в любое время, я не могу гарантировать, что ни один другой поток не использует контекст, когда это произойдет.

Каков правильный способ позвонить glDeleteTextures в финализаторе .net объекта?

Редактировать

Этот класс обертка будет использоваться в комплексной системе графа сцены «загрузка по требованию», стратегии кэширования, реализуемой WeakReference и тому подобное. Итак, «ручное удаление» - это не вариант, который я хочу рассмотреть: я действительно хочу, чтобы GC справился с этим.

+0

Без ответа еще два варианта: 1. Используйте UI 'SynchronizationContext', чтобы запланировать удаление в потоке OpenGL. 2. Создайте вторичный контекст с помощью 'wglShareLists', единственной целью которого является выпуск дескрипторов OpenGL. Я не думаю, что есть решение, не заставляющее вас съеживаться ... –

ответ

0

Хорошо, вот что я сделал.

Я применил интерфейс IDisposable к объекту ресурса (базовый класс для текстуры, массив вершин, шейдеры и т. Д.).

Когда расположен (или завершен, если не утилизировать вручную), ресурс добавить свой идентификатор и типа в синхронном список принадлежит его контексту OpenGL класса-оболочки и в конечном итоге просыпается основной поток, установив событие.

Я изменил свое сообщение применение насоса (теперь я использую MsgWaitForMultipleObjects вместо GetMessage) и в цикле, когда приобрела контекст OpenGL, нить первый «свободные» неиспользованные ресурсы перед обработкой сообщения (если таковые имеются).

Он работает как прелесть до сих пор.

Спасибо Stefan Hanke за советы относительно этого!

4

У вас нет.

Используйте интерфейс IDisposable, чтобы сделать дезактивацию дезактивации. Освободив объект текстуры, вызовите метод dispose. Внутри удалите текстуру.

Это значение сохраняется для всех объектов, которые выделяют неуправляемые ресурсы; в этом случае OpenGL обрабатывает.


Редактировать: Путь Dispose, похоже, не применяется здесь. Ресурс должен быть завершен в создании потока, и стандартный способ сделать это не обрабатывает этот случай.

+0

Я не знаю, когда я могу избавиться от текстур. Я хочу, чтобы GC обработал это для меня :) –

+3

Вы можете ** не ** позволить GC handle утилизировать объекты OpenGL (потоки, fbo, vbo, ...). Вы должны вручную вызывать удаление объектов OpenGL в потоке, где вы их создали. Легкий способ сделать это - использовать «использование» на объектах IDisposable. –

+0

В основном, это та же проблема с COM-объектами, которые необходимо завершить в потоке STA. Интересно, как .NET может планировать финализатор в другом потоке ... –

1

Stefan Hanke как alreadt заявленный, вы этого не делаете.

Адрес: glDeleteTextures Факс: glDeleteTextures? Когда контекст OpenGL, который создал основную текстуру (ы) (точнее, он sharesobject name space) является текущим в вызывающем потоке.

Финализатор (деструктор класса) работает с потоком GC, но на самом деле я не знаю, указано ли, как выполняется GC (это ответственность .NET JIT); Я думаю, что наиболее очевидной реализацией является выделенный поток. В самом деле, не стоит называть glDeleteTextures в финализаторе, так как вы не знаете, является ли контекст OpenGL текущим в этом потоке.

Реализация IDisposable может быть идеей, но проблема остается, так как реализация Dispose должна знать, действительно ли контекст OpenGL является актуальным. Для этой цели вы можете использовать подпрограммы, зависящие от платформы, такие как wglGetCurrentContext.


Я столкнулся с той же проблемой, и у меня получилось следующее решение.

Контекстная абстракция (RenderContext), которая сопоставляет контекст OpenGL с потоком. Вот реализация MakeCurrent:

public void MakeCurrent(IDeviceContext deviceContext, bool flag) 
{ 
    if (deviceContext == null) 
     throw new ArgumentNullException("deviceContext"); 
    if (mDeviceContext == null) 
     throw new ObjectDisposedException("no context associated with this RenderContext"); 

    int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; 

    if (flag) { 
     // Make this context current on device 
     if (Gl.MakeContextCurrent(deviceContext, mRenderContext) == false) 
      throw new InvalidOperationException("context cannot be current because error " + Marshal.GetLastWin32Error()); 

     // Cache current device context 
     mCurrentDeviceContext = deviceContext; 
     // Set current context on this thread (only on success) 
     lock (sRenderThreadsLock) { 
      sRenderThreads[threadId] = this; 
     } 
    } else { 
     // Make this context uncurrent on device 
     bool res = Gl.MakeContextCurrent(deviceContext, mRenderContext); 

     // Reset current context on this thread (even on error) 
     lock (sRenderThreadsLock) { 
      sRenderThreads[threadId] = null; 
     } 

     if (res == false) 
      throw new InvalidOperationException("context cannot be uncurrent because error " + Marshal.GetLastWin32Error()); 
    } 
} 

public static RenderContext GetCurrentContext() 
{ 
    int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; 

    lock (sRenderThreadsLock) { 
     RenderContext currentThreadContext; 

     if (sRenderThreads.TryGetValue(threadId, out currentThreadContext) == false) 
      return (null); 

     return (currentThreadContext); 
    } 
} 

private static readonly object sRenderThreadsLock = new object(); 

private static readonly Dictionary<int, RenderContext> sRenderThreads = new Dictionary<int,RenderContext>(); 

Если (и только если) контекст валюты осуществляется с использованием MakeCurrent метод, класс RenderContext может знать, является ли это ток к вызывающему потоку. В заключение, реализация Dispose может использовать класс RenderContext, чтобы действительно удалить объекты OpenGL.

Но что, если вызывающий поток не имеет контекста контекста OpenGL? Я решил эту проблему, представив GraphicGarbageCollector, которые собирают список ресурсов (имена текстур, ...), которые освобождаются в соответствующем потоке (при правильном контексте OpenGL).

По сути, у каждого ресурса есть пространство имен объектов (OpenGL контекстный список, я определил с помощью GUID). Используя пространство имен объектов, экземпляр ресурса может получить соответствующий GraphicGarbageCollector, присвоить имя ресурса (например, идентификатор текстуры); когда это уместно, GraphicGarbageCollector освобождает выделенные ресурсы, используя базовый контекст.

Тот же механизм может использоваться с системой отсчета: когда счетчик ссылок достигает 0, он распоряжается ресурсом, собирая его для сбора мусора. Это последовательная реализация: вы можете найти шахту here.

+0

Слишком длинный ответ :(... все может быть подведено «Соберите вручную ресурсы OpenGL». – Luca