【问题标题】:How to dispatch an event back to the main UI thread when using an OpenTK GameWindow?使用 OpenTK GameWindow 时如何将事件分派回主 UI 线程?
【发布时间】:2012-03-03 18:22:46
【问题描述】:

看了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,有两种解决方法:

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

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

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

This article 说:

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

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

那么,将我的事件分派回主 UI 线程的最简单方法是什么?使用 OpenTK 库还是其他最好非仅限 Windows 的库?

【问题讨论】:

    标签: c# windows multithreading opengl opentk


    【解决方案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();
        }
    }
    

    【讨论】:

    • 这通常是最好的方法。 UI 回调,例如 GLControl.BeginInvoke 是通过 Windows 消息队列调度的,并且比简单的应用程序队列具有显着更高的开销。
    猜你喜欢
    • 1970-01-01
    • 2022-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多