2016-08-01 7 views
4

Ich habe kürzlich foglemans ausgezeichnete Demo "Minecraft in 500 Zeilen" von https://github.com/fogleman/Craft heruntergeladen. Ich habe das 2to3-Tool benutzt und einige Details manuell korrigiert, um es unter Python3 lauffähig zu machen. Ich wundere mich jetzt über eine Sache mit dem Aufruf von self.clear() in der Render-Methode. Dies ist meine modifizierte Rendering-Methode, die jeden Rahmen von Pyglet genannt wird:Python minecraft pyglet glClear() skipping frames

def on_draw(self): 
    """ Called by pyglet to draw the canvas. 

    """ 
    frameStart = time.time() 
    self.clear() 
    clearTime = time.time() 
    self.set_3d() 
    glColor3d(1, 1, 1) 
    self.model.batch.draw() 
    self.draw_focused_block() 
    self.set_2d() 
    self.draw_label() 
    self.draw_reticle() 
    renderTime = time.time() 

    self.clearBuffer.append(str(clearTime - frameStart)) 
    self.renderBuffer.append(str(renderTime - clearTime)) 

Wie Sie sehen können, habe ich die Ausführungszeiten von self.clear() und der Rest der Rendering-Methode. Der Aufruf von self.clear() ruft diese Methode von Pyglet, die bei .../Pyglet/Fenster/__ init__.py gefunden werden können:

def clear(self): 
    '''Clear the window. 

    This is a convenience method for clearing the color and depth 
    buffer. The window must be the active context (see `switch_to`). 
    ''' 
    gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) 

Also im Grunde ich einen Anruf zu glClear() machen.
Ich bemerkte einige Frame Drops während des Spiels (mit 60 FPS), also habe ich den obigen Code hinzugefügt, um die Ausführungszeit der Befehle zu messen, und insbesondere die von glClear(). Ich fand heraus, dass das Rendern selbst nie länger als 10 ms dauert. Aber die Dauer der glClear() ist ein bisschen eine andere Geschichte, hier ist die Verteilung für 3 Messungen unter verschiedenen Bedingungen:
Duration of glClear() under different conditions.

Die Magenta-Linien zeigen die zeitliche Begrenzung eines Rahmens. Alles hinter der ersten Zeile bedeutet also einen Frame Drop.
Die Ausführungszeit von glClear() scheint nach dem Ablauf des ersten Frames eine Art "Echo" zu haben. Kannst du mir erklären warum? Und wie kann ich den Anruf schneller machen?
Leider bin ich kein OpenGL Experte, also bin ich dankbar für jeden Rat. ;)

+1

Es könnte sein, dass Ihre Timerauflösung einfach 16 ms beträgt, was das "Echo" erklären würde. Siehe https://docs.python.org/2/library/time.html#time.time, das sagt "Nicht alle Systeme bieten Zeit mit einer besseren Genauigkeit als 1 Sekunde.". Versuchen Sie einen anderen Timer und sehen Sie, ob es wichtig ist. – TWT

Antwort

0

Ihre Grafik ist falsch. Nun, zumindest ist es kein geeigneter Graph zum Zweck der Leistungsmessung. Vertrauen Sie niemals einer gl*-Funktion, die ausgeführt werden soll, wenn Sie sie angeben, und vertrauen Sie niemals darauf, dass sie so schnell ausgeführt wird, wie Sie es erwarten würden.

Die meisten gl* Funktionen werden nicht sofort ausgeführt, sie könnten nicht sein. Denken Sie daran, wir haben es mit der GPU zu tun und sagen, dass es Dinge tun soll, die langsam sind. Stattdessen schreiben wir eine To-Do-Liste (eine Befehlswarteschlange) für die GPU und legen sie in VRAM ab, wenn wir die GPU benötigen, um etwas auszugeben, wirklich. Dieser "Dump" ist Teil eines Prozesses namens Synchronisation und wir können einen mit glFlush auslösen. Obwohl OpenGL benutzerfreundlich ist (im Vergleich zu beispielsweise Vulkan), ist es nicht darauf angewiesen, dass wir die Befehlswarteschlange explizit leeren. Viele gl* Funktionen, die genau auf Ihren Grafiktreiber abgestimmt sind, synchronisieren implizit den CPU- und den GPU-Status, der einen Flush enthält.

Da glClear normalerweise einen Frame initiiert, ist es möglich, dass Ihr Treiber es für sinnvoll hält, eine solche implizite Synchronisation durchzuführen. Wie Sie sich vorstellen können, ist die Synchronisation ein sehr langsamer Prozess und blockiert die CPU-Ausführung bis zum Ende.

Und das ist wahrscheinlich, was hier vor sich geht. Ein Teil des Synchronisationsprozesses besteht darin, Speichertransaktionen durchzuführen (wie glBufferData, glTexImage*), die wahrscheinlich in die Warteschlange eingereiht werden, bis sie mit Ihrem glClear-Aufruf gelöscht werden. Was in Ihrem Beispiel Sinn macht; Die Spikes, die wir beobachten können, sind wahrscheinlich die Frames, nachdem Sie viele Blockdaten hochgeladen haben.

Aber denken Sie daran, dies ist nur reine Spekulation, und ich bin auch kein totaler Experte für diese Art von Sachen, also vertraue mir nicht auf die genauen Details.This page auf dem OpenGL-Wiki ist eine gute Ressource für diese Art von Dingen.

Obwohl eine Sache sicher ist, dauert Ihr Anruf glClear nicht so lange, wie Ihr Profiler sagt, dass es tut. Sie sollten einen Profiler verwenden, der für das Profiling von Grafiken vorgesehen ist.