Composite redraw speedup?
Carsten Haitzler (The Rasterman)
raster at rasterman.com
Wed Feb 12 10:21:02 UTC 2020
On Sat, 8 Feb 2020 15:46:36 +0100 Egil Möller <egil at innovationgarage.no> said:
> Hi!
>
> I have for a time now been working on a new composing window manager to try
> out a few UX ideas (https://redhog.github.io/InfiniteGlass videos:
> https://www.youtube.com/watch?v=vbt7qtwiLiM
> https://www.youtube.com/watch?v=E8f2KwgvxK4).
>
> However, I'm having a performance problem in my redraw loop: When a lot is
> going on, e.g. during continuous stream of mouse events and/or PropertyNotify
> events + property gets, DamageNotify events for windows are often queued up
> and e.g. animations or video appear choppy.
>
> To solve that, I start a redraw loop with a certain framerate when the first
> damage event comes in, to run for a set time. During this redraw loop, any
> window that has had DamageNotify events also on every frame reloads their
> contents to their textures, using () / glXBindTexImageEXT().
> This works fairly well, but when windows are large, this does mean that my
> renderer process starts eating a lot of CPU. Worse, it does this for a short
> amount of time _after_ all window changes have ceased.
hint: don't glXCreatePixmap() every time you render. do it only when you first
start compositing a window (i.e. on map) and if the window resizes. it'sa slow
path to keep doing this every time. the bind is needed for changing gl state to
tell it that that pixmap is the texture source. also don't re-get the pixmap
every frame/damage. get it once as well at the same times you do
glXCreatePixmap(). the pixmap id may change as the xserver may allocate a new
pixmap for the newly sized window, but no need to keep doing round trips to the
server to get it every damage.
also... don't handle every damage event as a draw. accumulate them for some
time (until the next frame tick/draw) and then draw it all in one go. also
consider using the buffer age extension to limit redraws only to updated
regions where you can assume the gl back buffer has content from N frames ago
(the buffer age tells you). use a scissor clip and just update the regions you
need (do the usual of merge nearby regions into single larger super-regions to
do a trade-off of number of render passes vs a bit more overdraw).
also just avoid doing any xlib calls that require round trips unless you have
to. e.g. only get properties when you have been notified by events that they
changed, then store the properties locally and use your local versions until
you're told that they have changed ... etc.
follow the model of "i shadow copy/store all the server state locally whenever i
can and use what i have stored locally on my side until i am told it has changed
or circumstances are that what i have is invalid and needs to be thrown out or
updated".
> Is there a way around this? Is the real solution to not use the X protocol
> (or properties) for e.g. controlling animations?
get the fd for the xlib server connection and use select() to listen on it for
input available to read. when there is then fetch events until no more events
exist (while xpending() { xnextevent()....}). and use select timeouts as your
animation trigger. if you get your timeouts right you can set it to wake up
fairly smoothly e.g. at 60hz. ... if you don't want to animate any frames,
don't have a timeout and select "forever" until something comes in from the
server. adjust the timeout based on how much time you spent processing events
since the last time you went into select. even better - if the /dev/dri/card0
device exists, dlopen libdrm and get some symbols from it and ... use it to
request the drm device sent you vsync events so you can use the vsync interrupt
as your frame event. this will be another fd to listen on in select() and of
course you can turn this vblank event stream on and off.
this way your single select loop can multiplex all your i/o with timeouts for
animation or vsync events for screen refresh rate bound animation etc.
> Would it help to have multiple connections to the X server, and subscribe
> only to damage events on one of them?
>
> I know that damages are not for the whole window but for a certain region,
> but I don't think there's a way to update parts of a texture in OpenGL?
you don't have to. if you stop creating a pixmap every time... it's zero-copy.
or should be. you want to follow my above advice to minimize redraw regions.
> Thanks in advance,
> Egil
just an aside. you want to make a "minimal compositing wm" and also want it
fast and efficient and also want to do smooth animation and so on. this is why
toolkits exist that have already solved these problems for you. :) they have
gone and implemented these select based main loops and have glued in xlib for
you and have implemented all the pixmap compositing glue to do it efficiently
and have handled "only redraw the regions that update" and glued in vsync if
available (or otherwise use the clock-based select timeouts) for animation. as
you do more and more you will re-implement more and more of this. you might want
to look at how toolkits do this or just use one. :)
--
------------- Codito, ergo sum - "I code, therefore I am" --------------
Carsten Haitzler - raster at rasterman.com
More information about the xorg-devel
mailing list