2016-04-12 15 views
0

Ich bin ziemlich neu in GLFW und OpenGL im Allgemeinen, und ich arbeite an einem kleinen Modell-Renderer. Ich arbeite gerade an den Eingängen, und ich bin mit einem Problem konfrontiert, wie GLFW Eingaben verarbeitet, lassen Sie mich erklären: Jede Tutorials sagen, um glfwGetKey und eine "if-Wald" zu verwenden, um zu sehen, ob der und der Schlüssel gedrückt wurde. Das Problem, das ich damit habe, ist, dass es langsam werden könnte, wenn ich viele Schlüssel abdecke, und es ist hässlich. Also verwende ich Funktionszeiger Tabellen und glfwSetKeyCallback, um dies zu beschleunigen und einen saubereren Code zu haben. Das Problem, das ich habe, ist, dass ich sehe, was aussieht wie eine Race Condition, die Kamera scheint zu stottern. Ich verwende eine Delta-Zeit für jeden Frame berechnet, um konstante Geschwindigkeit zu haben. Von dem, was ich sehen konnte, scheint es, dass die Key-Callback-Funktion ab und zu und nicht jedes Mal an jedem Frame aufgerufen wird, wenn ein Schlüssel wiederholt wird ... Ich benutze die neueste Version von GLFW3 von ihrem Github, ich tausche der Puffer am Anfang jeder Schleife und am Ende glfwPollEvents() verwenden.GLFW Key Callback Synchronisation

Meine Frage ist die folgende: Gibt es eine Möglichkeit, den glfwPollEvents-Aufruf und das Rendering zu synchronisieren, um das Stottern und den Differenzzeitunterschied zwischen Rendering-Schleife und der Rückruffunktion zu vermeiden? Vielen Dank im Voraus für Ihre Hilfe!

+0

Wie gehen Sie davon aus, dass "if forest" Ihnen Leistungsprobleme bereiten würde? Hast du es tatsächlich gemessen? Ja, es sieht hässlich aus und das ist schwer zu pflegen ist der berechtigte Grund, es zu vermeiden. Aber Leistung ist kein Grund. – datenwolf

+0

In der Tat, der Leistungsunterschied wird nicht groß mit modernen CPU sein, aber stell dir vor, du hast 1000 wenn ... sonst wenn Aussagen und der aktuelle Fall der letzte ist, es bedeutet, dass dein Programm alle 999 anderen Möglichkeiten vorher prüfen wird etwas tun, gegen einen einfachen Funktionsaufruf im Falle der Funktionszeiger-Tabelle. Auch, wie Sie sagten, es ist hässlich und schwierig zu pflegen, so "wenn Wälder" zu jedem Preis Leistung Kosten oder nicht IMHO vermeiden sollen. – Tab

+0

Dort gibt es eine partielle Antwort: http://StackOverflow.com/Questions/6805026/is-Switch-Faster-Than-if Obwohl ich nicht wirklich weiß, wie Compiler interpretieren wenn sonst Wälder, nehme ich vernünftigerweise an Tonnen von bedingten Prüfungen wäre logisch langsamer als eine einfache Funktion Zeiger Tabelle ... – Tab

Antwort

2

Im Allgemeinen sollten Sie die Eingabe so handhaben, dass Sie eine Liste von Schlüsseln behalten und ihren letzten Eingabezustand aufzeichnen.

struct key_event { 
    int key, code, action, modifiers; 
    std::chrono::steady_clock::time_point time_of_event; 
} 

std::map<int, bool> keys; 
std::queue<key_event> unhandled_keys; 
void handle_key(GLFWwindow* window, int key, int code, int action, int modifiers) { 
    unhandled_keys.emplace_back(key, code, action, modifiers, std::chrono::steady_clock::now()); 
} 

Dann in der Render-Schleife (oder Sie können es in eine andere Schleife trennen, wenn Sie mit Ihrem Multithreading + Synchronisation Fähigkeiten sicher sind) Sie Code wie folgt schreiben:

float now = glfwGetTime(); 
static float last_update = now; 
float delta_time = now - last_update; 
last_update = now; 
handle_input(delta_time); 

Wo handle_input würde wie folgt aussehen:

float external_position[2]; 
std::map<int, std::function<void(/*args*/)>> key_functions; 
void handle_input(float delta_time) { 
    //Anything that should happen "when the users presses the key" should happen here 
    while(!unhandled_keys.is_empty()) { 
     key_event event = unhandled_keys.front(); 
     unhandled_keys.pop(); 
     key_functions[event.key](/*args*/); 
     bool pressed = event.action == GLFW_PRESS || event.action == GLFW_REPEAT; 
     keys[event.key] = pressed; 
    } 
    //Anything that should happen "while the key is held down" should happen here. 
    float movement[2] = {0,0}; 
    if(keys[GLFW_KEY_W]) movement[0] += delta_time; 
    if(keys[GLFW_KEY_S]) movement[0] -= delta_time; 
    if(keys[GLFW_KEY_A]) movement[1] -= delta_time; 
    if(keys[GLFW_KEY_D]) movement[1] += delta_time; 
    external_position[0] += movement[0]; 
    external_position[1] += movement[1]; 
} 

EDIT: ich habe Logik hinzugefügt behandeln „auf die Taste“/„auf Freigabe“ Typ Funktionen. Wenn also zum Beispiel dieser Code waren in den Renderer:

key_functions[GLFW_KEY_SPACE] = [&renderer] {renderer.pause();}; 

Dann Drücken der Taste [Space] den Renderer würde pausieren.

+0

Hallo und vielen Dank für Ihre Antwort! Das Problem mit diesem Code ist, dass es ein "if forest" gibt, was bedeutet, dass es jeden Schlüssel einmal überprüft ... Was ich suche, ist eine Möglichkeit zu wissen, welcher Schlüssel gedrückt wird und den Schlüsselcode (welcher glfwSetKeyCallback Damit kann ich die Callback-Funktionen meines Schlüssels wie folgt aufrufen: key [keycode] [action] .function (keycode, action, mods, key [keycode] [action] .arg); Das ist viel schneller und sauberer IMO ... Das Problem ist, dass wenn ich glfwSetKeyCallback verwende, bekomme ich Synchronisationsprobleme ... – Tab

+0

@Tab Der Punkt, den ich mache, ist, dass der Rückruf extrem leicht sein sollte. Es ist schlecht, eine Callback-Funktion zu haben, die Aufgaben ausführen muss, die mehr als ein paar Mikrosekunden dauern können. Ich werde etwas Code hinzufügen, um speziell die "on Press" -Typ-Logik zu handhaben. – Xirema

+0

@Tab Ich habe den Beitrag mit einigen zusätzlichen Informationen aktualisiert. – Xirema

0

Okay, so werde ich meine eigene Frage zu beantworten (und @Xirema ‚s Antwort als gültig verlassen, wie er richtig war)

Ich denke, es verstand nur, warum es nicht zwischen Framerate und Rückruf bei Wiederholung synchronisiert wurde, Es ist wegen der Art und Weise, wie das Betriebssystem Tastenwiederholung behandelt, es wird nicht mehr als 4000 Tastenwiederholungssignale pro Sekunde senden (wie ich 4000 + fps bekomme) und wird sich auf 60 Anrufe/s oder so beschränken! Um dieses Problem zu beheben, werde ich tatsächlich die letzte gedrückte Taste 60 mal/s in der Callback-Funktion registrieren und die Funktion außerhalb der GLFWkeyfun (in der Hauptschleife) einmal pro Frame ausführen, oder einen Weg finden, um die Bewegungen zu normalisieren Stottern vermeiden!

Wie auch immer, noch einmal vielen Dank dafür, dass Sie sich Zeit genommen haben, um @Xirema zu beantworten. Ich hoffe, dass meine Frage für jemanden nützlich sein kann. (nicht wirklich ;-))

S.S: Wenn man weiß, wie oft das Schlüsselwiederholungssignal gesendet wird, kann auch ein fester Deltatimenwert in den key_repeat Callback-Funktionen verwendet werden!

P.P.S: Nun, wenn man bedenkt, dass die Tastaturwiederholung abhängig von den Betriebssystem- oder Benutzereinstellungen oder sogar der CPU-Leistung variieren kann, ist die beste Lösung, eine separate Delta-Zeit für die GLFWkeyfun zu haben, damit Sie sie an Ihre Callback-Funktionen senden können Delta-Zeit für die Taste wiederholen und alles Stottern vermeiden!