[cairo] clipmask of multiple rects in different angles

Theo Veenker theoveenker at gmail.com
Thu Jul 21 07:07:40 UTC 2016

On 07/21/2016 01:22 AM, Enrico Weigelt, metux IT consult wrote:
> On 20.07.2016 23:46, Theo Veenker wrote:
>>> Maybe the damage tracking itself is slowing it down ... I'll now try
>>> to cache the matrices.
> Just implemented it - doesn't seem to have much effect.
> And still, on drm it's much slower than on xcb. The funny part here:
> it's not even deterministic - sometimes mouse movements (which in turn
> trigger two widgets to be repainted in my test program, as they show
> the positions and currently hover'ed widget) lags behind about 0.5..1s,
> but sometimes it's way faster - no idea what could cause that big
> indeterminism.

I suppose such a big lag can only happen if your system is too busy (drawing or something 
else). I use xlib, does that fall back on xcb these days?

>> Are you also saving the bounding boxes of your graphic objects?
> Just the rect (coordinates and angle) relative to the parent, but not
> the absolute positions.

Yeah that's what I mean.

> https://github.com/metux/twtk/blob/master/include/twtk/widget.h#L147
> I'm also caching the widget contents (including their subs's) as an
> cairo_pattern_t, rendered within an group. They get re-rendered when
> the widget gets a dirty flag - in that case the whole parent line also
> needs to be re-rendered.
> Haven't measured yet what's really eating up most of the time, whether
> it's the recursive rendering (within a group) or actual drawing onto
> screen. I'd suspect the latter, as xcb is magnitudes faster than xcb.

Using groups (as per cairo_{push|pop}_group) creates intermediate surfaces. I'd try to 
avoid that. The only situation I use cairo_{push|pop}_group() is when drawing a widget 
with alpha. (still dreaming about global alpha in cairo)

I only use surface patterns in case I need to paint something using an image as a fill or 
stroke pattern. I imagine that a simple case like drawing a rectangle is much faster with 
a color pattern then with a surface pattern. Maybe that's related to your problem?

> By the way: when exactly is the clipping applied (and clipped-out stuff
> dropped out) ? Is that device specific or generic ?

That should be in the docs somewhere I guess, I'm no cairo expert. I belive clipping is 
the last stage.

>> I suppose it is not efficient if you need recalculate the bbox (by drawing
>> onto a dummy surface) before each redraw.
> Actually, I'm not recalculating it by drawing. The (relative) rects
> and the matrices are stored within the widgets. For damage tracking,
> rect and matrix are passed upwards and transformed with the parent
> coordinates.

Sounds good too me.

> As I'm not using angles != 0 right now, I maybe could add a bypass that
> just adds positions w/o using matrices.
>> What also helps *a lot* is drawing only once every vertical retrace.
> hmm, in general, I'm always redrawing if an event caused the screen to
> become dirty (passed up the handler hierarchy as an result code flag)
> and on xcb when the server requests it.
> In any case, when a widget is dirty, it's parent is also marked dirty,
> so finally the root is always repainted when something changes. But in
> parallel the affected (absolute) rects are recorded and used as clip
> when the root's pattern is drawn onto screen.
> Theoretically, the clip should limit the actual draw operations on the
> underlying framebuffer to the dirty regions.

Maybe you could count per widget howmany times per second is gets redrawn. Ideally it 
should not be more than once per video frame.

>> In my setup all graphic objects have a current and a new state (this means
>> every attribute such as x or y, color etc. is doubled). Let's say we're
>> not using a back-buffer then a graphics expose will cause a redraw using
>> the current state of the objects. And only at vertical retrace the
>> drawing is updated to refrect the new state. In this aproach you can
>> change the state of objects as often as you want with nearly zero CPU
>> load. Only at retrace when changes are drawn you will have load. So if
>> you're the only process drawing you'll have almost a full video frame to
>> do the job; that should be plenty of time.
> hmm, how could an actual implementation look like ?
> Have a different thread sleeping on some lock, which is released as soon
> as there's something to repaint and then sleep until vsync event occours
> (sync read() on drm fd ...) ?

 From the top of my head (haven't worked on it for some time): I have a thread monitoring 
DRM vblank. This gives me the refresh interval and a way to tell when a the N'th video 
frame will start. From my main thread a let a timer fire at every vretrace (or even a 
little earlier). At each timer expiration I let the graphics stuff update itself. 
Something along that line.

>> Personally I don't use the cairo region type. I just create my own array
>> of integer rectangles (do floor on x, and y; ceil on w and h) from which
>> the clip mask is then created.
> Does your implementation also allow arbitrary angles ?
> For now, my test program doesn't use angles yet, but I wanna support it
> in the future. I'm also planning to support widget zooming (in that way
> that the widget class itself doesn't have to care about it - everything
> handled by the engine).

Arbitrary angles shouldn't be a problem (as it just causes bigger bounding boxes, that's 
all), but I don't allow them for gui elements. I make a distiction between gui elements 
which are always layed out othogonally and graphic shapes (which can appear in any angle) 
on a drawing canvas. Drawing things on a scaled window of course needs clipping to be 
adjusted too. In my case I sometimes need to show a smaller version of a full-screen 
window; works well.


More information about the cairo mailing list