Hallo,
ich habe ein simples X11-Programm unter Linux geschrieben.
Ist das Fenster verdeckt und wird dann voll sichtbar, wird es etwa 10x
ganz schnell hintereinander gezeichnet.
Das kann nicht Sinn der Sache sein.
1
while(1)
2
{
3
4
// pa ist ein Pointer auf eine Struktur, die wichtige IDs
5
// enthaelt (Windows-Handles, GC, ...)
6
XNextEvent(pa->dis,&event);
7
8
switch(event.type)
9
{
10
11
caseButtonPress:
12
...
13
break;
14
15
caseExpose:
16
17
XGetWindowAttributes(pa->dis,pa->win,&attr);
18
draw(pa,attr.width,attr.height);
19
break;
20
21
caseKeyPress:
22
23
if(XK_q==XLookupKeysym(&event.xkey,0))
24
{
25
26
exit(EXIT_SUCCESS);
27
28
}
29
30
break;
31
32
//case ResizeRequest:
33
34
default:
35
36
fprintf(stderr,"eventtype %d\n",event.type);
37
38
}
39
40
}
Bei draw wird ziemlich viel gemalt, was ein bisschen dauert (weniger als
1/2 Millisek.).
Ich kann mir das so erklaeren, dass nach dem 1. Expose-Event es zu lange
dauert, bis draw() fertig ist und XFlush(dis); aufruft. In der
Zwischenzeit schickt der X-Server weitere Expose-Events, weil der denkt,
der 1. ist nicht angekommen.
Aber wie sollte man das anders machen? Es gibt halt viel zu zeichnen.
Andere Erklaerungen?
Gruss, Juergen
Juergen schrieb:> Andere Erklaerungen?
Wie machst du das Fenster den sichtbar? Schiebst du ein z.B. anderes
davor weg? Dann gibt es natürlich mehrere Expose-Events, weil ja nach
und nach immer mehr vom Fenster sichtbar wird.
Übrigens: Woher weiß dein Draw eigentlich, welcher Teil vom Fenster
gerde sichtbar geworden ist? Oder zeichnet das jedesmal alles komplett
neu, auch wenn nur zwei Pixel mehr sichtbar geworden sind?
> Aber wie sollte man das anders machen? Es gibt halt viel zu zeichnen.
Double buffering. Außerdem kannst du, wenn schon mehrere Expose-Events
in der Queue stecken, die erstmal aalle sammeln und dann nur einmal draw
aufrufen.
>Wie machst du das Fenster den sichtbar?
Ich betaetige ALT-TAB. (Damit kann man alle Fenster der Reihe nach in
den Vordergrund bringen. Das mache ich schon seit Windows3.1 so :-) )
Es wird auf einen Schlag sichtbar und ich zeichne alles neu.
Juergen schrieb:>>Wie machst du das Fenster den sichtbar?>> Ich betaetige ALT-TAB. (Damit kann man alle Fenster der Reihe nach in> den Vordergrund bringen. Das mache ich schon seit Windows3.1 so :-) )> Es wird auf einen Schlag sichtbar und ich zeichne alles neu.
Worauf ich hinauswollte, war, daß es mehrere Expose-Events geben kann,
wenn Teile des Fensters erst später sichtbar werden. Das wäre z.B. auch
der Fall, wenn dein Windowmanager bei Alt+Tab ein Popup-Fenster
aufmacht, das erst kurz nach dem Hervorholen deines Fensters
verschwindet und damit einen Teil davon freisetzt. Wenn's daran nicht
liegt, kann ich mir auch nur noch den Window-Manager vorstellen. Der
X-Server sendet keine wiederholten Expose-Events, nur weil den Programm
noch nicht darauf reagiert hat.
Du kannst dir auch mal die Inhalte des Events ausgeben (also die
Bereiche, die da angegeben sind).
Hast du eigentlich compsoite an? Vielleicht requestet da der
composite-Manager das Fenster nicht am Stück, sondern in Blöcken oder
so.
Aber wie gesagt: Wenn das Zeichnen lange dauert, mach double buffering,
und du hast keinen Ärger damit.
>Schonmal mit einem anderen Windowmanager ausprobiert?
Mit gtk/gdk kenne ich mich zwar aus, aber fuer diese App will ich mich
auf einen kleinen WM beschraenken, da es fuer den EEEPC 4GB gedacht ist,
und da habe ich keine grossen HD-Resourcen mehr frei.
>Du kannst dir auch mal die Inhalte des Events ausgeben (also die>Bereiche, die da angegeben sind).
1
caseExpose:
2
3
printf("Redrawing from Expose, (%d,%d) (%d,%d).\n",
4
event.xexpose.x,event.xexpose.y,
5
event.xexpose.width,event.xexpose.height);
6
7
8
9
RedrawingfromExpose,(293,0)(689,176).
10
RedrawingfromExpose,(293,176)(77,113).
11
RedrawingfromExpose,(646,176)(336,113).
12
RedrawingfromExpose,(293,289)(689,73).
13
RedrawingfromExpose,(370,176)(276,113).
Tatsaechlich. Es sind verschiedene Bereiche angeben.
(Es sind hier auch nur 5 Expose Events.)
Vielleicht registriert Xlib gar nicht, dass ich beim ersten Expose
bereits das ganze Window gezeichnet habe und denkt, ich haette nur den
im event angebenen Auschnitt gezeichnet.
Juergen schrieb:>>Schonmal mit einem anderen Windowmanager ausprobiert?>> Mit gtk/gdk kenne ich mich zwar aus, aber fuer diese App will ich mich> auf einen kleinen WM beschraenken, da es fuer den EEEPC 4GB gedacht ist,> und da habe ich keine grossen HD-Resourcen mehr frei.
gtk ist ein API, kein Windowmanager. Und bist du sicher, daß auf dem
EEE-PC nicht eh schon gtk drauf ist?
Juergen schrieb:> Tatsaechlich. Es sind verschiedene Bereiche angeben.> (Es sind hier auch nur 5 Expose Events.)> Vielleicht registriert Xlib gar nicht, dass ich beim ersten Expose> bereits das ganze Window gezeichnet habe und denkt, ich haette nur den> im event angebenen Auschnitt gezeichnet.
Xlib "denkt" da gar nix. Sie gibt dir nur die Informationen, wenn Teile
des Fensters exponiert werden. Was du als Reaktion darauf dann tust, ist
deine Sache.
>Xlib "denkt" da gar nix.
Ist mir schon klar.
Aber ich braeuchte die Moeglichkeit, dass der X-Server mir nur einen
expose schickt unabhaengig von den aufgedeckten Fensterflaechen. D.h.
selbst wenn nur 2 kleine Miniflaechen neugezeichnet werden muessen, soll
er mir nur einen expose schicken fuer das ganze Fenster. (anstatt 2
expose fuer die jeweiligen Miniflaechen)
>gtk ist ein API, kein Windowmanager. Und bist du sicher, daß auf dem>EEE-PC nicht eh schon gtk drauf ist?
Es soll auf dem EEEPC auch modifiziert werden koennen. Die GCC-Suite ist
schon drauf, aber ich brauechte noch libgtk2.0-dev mit den ganzen
Abhaengigkeiten. Schaetz ich mal auf 12-20 MB (Installed Size).
Und da wird es eng.
Juergen schrieb:>>Xlib "denkt" da gar nix.> Ist mir schon klar.> Aber ich braeuchte die Moeglichkeit, dass der X-Server mir nur einen> expose schickt unabhaengig von den aufgedeckten Fensterflaechen. D.h.> selbst wenn nur 2 kleine Miniflaechen neugezeichnet werden muessen, soll> er mir nur einen expose schicken fuer das ganze Fenster. (anstatt 2> expose fuer die jeweiligen Miniflaechen)
Da wüßte ich keinen Weg. Ich kann da nur nochmals double buffering
empfehlen. Damit macht es dann nichts weiter aus, wenn mehrere
Anforderungen ankommen. Das ist auch allegmein sinnvoll. Deshalb macht
z.B. Qt seit Version 4 grundsätzlich bei allen Widgets double buffering.
Auch bei direkter Benutzung der Xlib sollte das recht einfach umsetzbar
sein.
> Es soll auf dem EEEPC auch modifiziert werden koennen. Die GCC-Suite ist> schon drauf, aber ich brauechte noch libgtk2.0-dev mit den ganzen> Abhaengigkeiten. Schaetz ich mal auf 12-20 MB (Installed Size).> Und da wird es eng.
Ja, ok, wenn du die ganzen dev-Pakete brauchst, ist der Platzbedarf
natürlich erheblich größer.
>Da wüßte ich keinen Weg. Ich kann da nur nochmals double buffering>empfehlen.
double buffering ist auf jeden Fall eine gute Loesung.
Ich denke auch daran, beim Empfang eines Expose-Event einen Timer zu
starten, der z.B. 10ms nachher die Flaechen neu zeichnet.
Kommen mehrere Expose-Events danach, wird der Timer nicht neu gestartet.
Die XExposeEvent-Struct hat ein Feld count, dieses ist 0, wenn es sich
um das letzte Expose-Event in einer derartigen Folge von
zusammenhängenden Events handelt, und ungleich 0 bei den anderen.
Andreas
Super. Das hat einen Erfolg gebracht.
Es wird beim obigen Szenerario jetzt nur 2x mal gezeichnet.
Die Count-Werte bei den 5 Expose-Events haben die Werte 3,2,1,0,0
Das ist ein enorme Verbesserung, wenn auch nicht das Optimum.
Danke.
Juergen schrieb:> Die Count-Werte bei den 5 Expose-Events haben die Werte 3,2,1,0,0
Wenn man sich die Areas genau ansieht, wird bei den ersten 4 Events ein
Rechteck mitten im Fenster ausgespart, das dann mit dem 5. Event
gezeichnet wird. Wahrscheinlich wirklich ein WM-Popup, halt mal die
Alt-Taste länger fest, dann siehst du das vermutlich auch. Insofern also
völlig korrektes Verhalten, das Rechteck in der Mitte wird eben wirklich
erst später als der Rest dargestellt, und deshalb auch nicht in die
Sequenz einbezogen (bzw. ohne die Aussparung gäbe es dann wohl auch nur
ein Event).
Andreas
Was auch noch eine schnelle Lösung mit vielleicht schon grossem Effekt
sein könnte ist die Aktivierung von Backing Store für das Fenster, dazu
muss nur ein Attribut gesetzt werden. Damit hängt es dann vom X-Server
ab. ob er verdeckte Fensterbereiche selbst buffert, moderne X-Server
sollten das aber eigentlich machen, solange der dazu nötige
Speicherbedarf nicht zu groß wird.
http://tronche.com/gui/x/xlib/window/attributes/backing-store.html
Andreas
Juergen schrieb:> Bei draw wird ziemlich viel gemalt, was ein bisschen dauert (weniger als> 1/2 Millisek.).
Wenn es tatsächlich nur 1/2 ms dauert das neuzuzeichnen, dann ist es
doch nicht schlimm dies 5x hintereinander zu tun ;)
Ich halte 1/2 ms fuer ziemlich lange bei einem PC.
Vielleicht sind es auch 2ms, war nur geschaetzt. Man kann das Flickern
beim Neu-zeichnen deutlich bemerken.
Und das Flickern ist das, was stoert.
Juergen schrieb:> Ich halte 1/2 ms fuer ziemlich lange bei einem PC.> Vielleicht sind es auch 2ms, war nur geschaetzt. Man kann das Flickern> beim Neu-zeichnen deutlich bemerken.
Dann dauert das Zeichnen aber erheblich länger als 2ms. 2ms
entsprächen 500Hz, alleine schon ein einzelner Durchgang bei der
Darstellung auf dem Monitor dauert mit 16,7ms (60Hz bei TFT angenommen)
mehr als achtmal so lange.
Andreas
Juergen schrieb:>>Da wüßte ich keinen Weg. Ich kann da nur nochmals double buffering>>empfehlen.>> double buffering ist auf jeden Fall eine gute Loesung.
Dazu müßtest du eigentlich nur eine X-Pixmap anlegen und dein draw() da
reinrendern lassen statt ins Fenster, dann im Event-Handler nur noch die
freigelegten Teile des Fensters aus der Pixmap rüber-"blitten" und draw
nur noch da aufrufen, wo sich der Inhalt ändert.