c # - Sådan sendes en begivenhed tilbage til hovedbrugergrænsefladen, når du bruger en OpenTK GameWindow?

Indlæg af Hanne Mølgaard Plasc

Problem



Efter at have set denne video begyndte jeg at tænke på, hvordan jeg kunne implementere sådan noget i mit nuværende projekt. Jeg synes, det ville være for svært at gøre hovedparten af ​​min kode redigerbar i realtid, men jeg troede, jeg kunne i det mindste gøre mine OpenGL shaders redigerbare, når jeg spiller spillet. [7]


Så jeg satte op en FileSystemWatcher:


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


Og nu vil jeg opdatere shader når en fil ændres:


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


Problemet er, så snart jeg redigerer min shader-fil, kaster en undtagelse sig og siger:



  Ingen kontekst er aktuelt i kaldetråden



Så jeg gjorde noget grave og fandt ud af, at OpenGL konteksten i det væsentlige er bundet til en enkelt tråd. AFAIK, der er 2 løsninger på dette:



  1. Deaktiver OpenGL-konteksten på hovedgrænsefladen, aktiver den på den anden tråd, gør mine ting, og nulstil derefter

  2. Send begivenheden tilbage til hovedbrugergruppen



Jeg er ikke sikker på, hvordan jeg ville implementere (1), fordi hovedtråden er riddled med OpenGL-opkald ... Jeg ville ikke vide, hvor du skal aktivere og deaktivere den.


Så jeg er tilbage med valgmulighed (2), bortset fra jeg kan ikke regne ud, hvordan man sender filen ændret begivenhed tilbage til hovedtråden.


Denne artikel siger: [8]



  GLControl leverer GLControl.BeginInvoke () -metoden til at forenkle asynkronmetodeopkald fra sekundære tråde til de vigtigste System.Windows.Forms.Application-tråd. GameWindow giver ikke en lignende API.



Desværre bruger jeg en GameWindow, så jeg er ikke sikker på, hvordan jeg får adgang til denne funktionalitet.


Så hvad er den nemmeste måde at sende min begivenhed tilbage til hovedbrugergruppen? Eller ved at bruge OpenTK-biblioteket eller et andet helst bibliotek uden for Windows?

Bedste reference


Fik ud, jeg kunne bare bruge en kø og trække ting ud af det:


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


Og så ændrer jeg min renderingssløjfe lidt:


lock(\_renderQueue)
{
    while(\_renderQueue.Count > 0)
    {
        \_renderQueue.Dequeue().Invoke();
    }
}