如何在使用OpenTK GameWindow时将事件调度回主UI线程?

时间:2012-03-03 18:22:47

标签: c# windows multithreading opengl opentk

看完this video后,我开始考虑如何在当前项目中实现类似的功能。我认为让我的大部分代码实时可编辑是很困难的,但我想我至少可以在玩游戏时使我的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. 在主UI线程上禁用OpenGL上下文,在另一个线程上启用它,执行我的操作,然后重置它
  2. 将事件发送回主UI线程
  3. 我不确定如何实现(1)因为主线程充满了OpenGL调用...我不知道在哪里启用和禁用它。

    所以我留下了选项(2),除了我无法弄清楚如何将文件更改事件发送回主线程。

    This article说:

      

    GLControl提供GLControl.BeginInvoke()方法,以简化从辅助线程到主System.Windows.Forms.Application线程的异步方法调用。 GameWindow没有提供类似的API。

    不幸的是我使用的是GameWindow,因此我不确定如何访问该功能。

    那么将我的事件发送回主UI线程的最简单方法是什么?是使用OpenTK库还是其他优选非Windows专用库?

1 个答案:

答案 0 :(得分:3)

想通了我可以使用一个队列来解决问题:

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