0

看完这个视频后,我开始思考如何在我当前的项目中实现类似的东西。我认为让我的大部分代码实时可编辑太难了,但我认为我至少可以在玩游戏时让我的 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,有两种解决方法:

  1. 在主 UI 线程上禁用 OpenGL 上下文,在另一个线程上启用它,做我的事情,然后重置它
  2. 将事件分派回主 UI 线程

我不确定我将如何实现 (1),因为主线程充满了 OpenGL 调用......我不知道在哪里启用和禁用它。

所以我只剩下选项(2),除了我不知道如何将文件更改事件分派回主线程。

这篇文章说:

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

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

那么将我的事件分派回主 UI 线程的最简单方法是什么?使用 OpenTK 库还是另一个最好的非 Windows 库?

4

1 回答 1

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();
    }
}
于 2012-03-03T20:21:28.347 回答