Как отправить событие обратно в основной поток пользовательского интерфейса при использовании OpenTK GameWindow? - PullRequest
0 голосов
/ 03 марта 2012

После просмотра этого видео я начал думать о том, как мне реализовать что-то подобное в моем текущем проекте. Я думаю, что было бы слишком сложно сделать большую часть моего кода редактируемой в реальном времени, но я думал, что по крайней мере смогу сделать свои шейдеры 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, библиотеку?

1 Ответ

3 голосов
/ 04 марта 2012

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

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();
    }
}
...