Как отправить событие обратно в основной поток пользовательского интерфейса при использовании OpenTK GameWindow?

После просмотра этого видео я начал думать о том, как я могу реализовать что-то подобное в моем текущем проекте. Я думаю, что было бы слишком сложно сделать большую часть моего кода редактируемой в реальном времени, но я думал, что смогу хотя бы сделать свои шейдеры OpenGL редактируемыми, когда я играю в игру.

Итак, я создал FileSystemWatcher:

protected void WatchShaders()
{
    _uiDispatcher = Dispatcher.CurrentDispatcher;
    const string shaderDir = @"path\to\my\shaders";
    _shaderFileWatcher = new FileSystemWatcher(shaderDir);
    _shaderFileWatcher.NotifyFilter = NotifyFilters.LastWrite;
    //fw.Filter = "*.frag;*.vert";
    _shaderFileWatcher.Changed += ShaderChanged;
    _shaderFileWatcher.EnableRaisingEvents = true;
}

И теперь я хочу обновлять шейдер всякий раз, когда файл изменяется:

void ShaderChanged(object sender, FileSystemEventArgs e)
{
    _shaderFileWatcher.EnableRaisingEvents = false; // prevent more/duplicate events from firing before we've finished processing the current one

    lock (_bfShader)
    {
        _bfShader.AttachShader(Shader.FromFile(e.FullPath));
        _bfShader.Link();
        _bfShader.Use();

        _bfProjUniform = new Uniform(_bfShader, "ProjectionMatrix");
        _bfSampler = new Uniform(_bfShader, "TexSampler");
        _bfSampler.Set1(0);
    }
    _shaderFileWatcher.EnableRaisingEvents = true;
}

Проблема в том, что как только я редактирую свой файл шейдера, возникает исключение:

В вызывающем потоке нет текущего контекста

Итак, я немного покопался и обнаружил, что контекст OpenGL по существу привязан к одному потоку. AFAIK, для этого есть 2 обходных пути:

  1. Отключите контекст OpenGL в основном потоке пользовательского интерфейса, включите его в другом потоке, сделайте мои вещи, а затем сбросьте его
  2. Отправьте событие обратно в основной поток пользовательского интерфейса

Я не уверен, как бы реализовать (1), потому что основной поток пронизан вызовами OpenGL ... Я бы не знал, где его включить или отключить.

Итак, у меня остался вариант (2), за исключением того, что я не могу понять, как отправить событие изменения файла обратно в основной поток.

В этой статье говорится:

GLControl предоставляет метод GLControl.BeginInvoke () для упрощения вызовов асинхронных методов из вторичных потоков в основной поток System.Windows.Forms.Application. GameWindow не предоставляет аналогичный API.

К сожалению, я использую GameWindow, поэтому не знаю, как получить доступ к этой функции.

Итак, какой самый простой способ отправить мое событие обратно в основной поток пользовательского интерфейса? Используете ли вы библиотеку OpenTK или другую предпочтительно библиотеку, не предназначенную только для Windows?


person mpen    schedule 03.03.2012    source источник


Ответы (1)


Выяснилось, что я могу просто использовать очередь и снимать с нее вещи:

void ShaderChanged(object sender, FileSystemEventArgs e)
{
    _shaderFileWatcher.EnableRaisingEvents = false; // prevent more/duplicate events from firing before we've finished processing the current one

    lock (_renderQueue)
    {
        _renderQueue.Enqueue(() =>
            {
                switch(e.Name)
                {
                    case "block.frag":
                        _bfShader.DetachShader(_blockFragShader);
                        _blockFragShader = Shader.FromFile(e.FullPath);
                        _bfShader.AttachShader(_blockFragShader);
                        break;
                    default:
                        return;
                }

                Trace.TraceInformation("Updating shader '{0}'", e.Name);

                _bfShader.Link();
                _bfShader.Use();

                _bfProjUniform = new Uniform(_bfShader, "ProjectionMatrix");
                _bfSampler = new Uniform(_bfShader, "TexSampler");
                _bfSampler.Set1(0);
            });
    }

    _shaderFileWatcher.EnableRaisingEvents = true;
}

Затем я немного модифицирую цикл рендеринга:

lock(_renderQueue)
{
    while(_renderQueue.Count > 0)
    {
        _renderQueue.Dequeue().Invoke();
    }
}
person mpen    schedule 03.03.2012
comment
Часто это лучший подход. Обратные вызовы пользовательского интерфейса, такие как GLControl.BeginInvoke, отправляются через очередь сообщений Windows и имеют значительно более высокие накладные расходы, чем простая очередь приложений. - person The Fiddler; 20.10.2014