GNOME Bugzilla – Bug 314790
gnometris cairoification
Last modified: 2012-01-31 23:22:21 UTC
This afternoon I played a bit with cairo and tried to port gnometris to it. I'm attaching a patch with the start of some cairoification of gnometris. let's make it clear right away: the patch is far from complete... in fact it's mostly completetely wrong and needs redoing from scratch. The reasons I am attaching it here are: - I played with the same thing a couple of weeks ago and obviously I wiped it away from my hard disk by mistake - this started as a quick hack to try cairo and I don't know if I'll have time to look at it in the forseable future, so someone else may find it useful as a starting point... or as an example on how not to do it :) - I need some advices on how things should be done and since I never have the chance to catch Callum on irc I'll post my doubts here. - I'll try to summarise some of the problems and implementation ideas so that someone else (or even me in the future) doesn't have to find them out from scratch again [so to make it short, feel free to close this if it gets in your way, it's just an excuse to write down some notes :) ] Major problems/doubts: - gnometris currently uses gnome-canvas, which is able to hold the state and redraw itself. Thus, currently code is split in two classes blockops (holds the state of the field and handle operations on blocks) and field (which does the drawing). However when moving away from canvas the following problem arises: Blockops needs Field to draw the changes, but in turn field needs blockops to know the current state of the field when redrawing itself. This means that major refactoring is needed: either the two classes are merged into one or the classes are made one the specializtion of the other (e.g. Field and FieldDrawer inheriting from field and drawing with cairo) or something else. Suggestion on the preferred way to follow are welcome. - We must decide if continue to draw blocks from bitmaps or go completely vectorial - drawing moving blocks with cairo as I did leaves around garbage due to AA, I have no clue on how to fix that - the drawing model I went with its probably slow as hell, but I need someone with more experience to suggest a better way. Currently it works like this: we have three cairo_surfaces: buffer, background and foreground. Background holds the background color/image and its cached so that it's redrawn only when the drawing area size changes. Foreground is where the blocks are drawed on. Buffer is the backing buffer: when drawing, the relevant part of background is painted on buffer, then foreground is painted on top and if needed the "Pause" and "GameOver" messages are painted on it too. When expose is received the relevant part of buffer is blitted on the drawing area. [drawing on the buffer currently repaints it all, but this is easy to improve] pieces of code probably worth saving: it's probably worth saving how the "pause" and "gameover" messages are drawn since calculating the size is not totally trivial.
Created attachment 51522 [details] [review] bits and pieces of cairoification
I have also been experimenting with Cairo stuff using Ataxx. Its a very nice library for drawing but performance is a definite issue. To response to some of your points: The class hierarchy in gnometris is shagged. The fact that most of the code is in one class (Tetris) is a good indication of that. There should be one object with a canonical view of the field and that provides the data to the drawing code. I can think of several ways to do it, any of them are better than what's there now. On the drawing artifacts issue: you can't do things with Cairo the way you can with pixmaps. It seems that the best way to do things is a complete redraw each time (unless you are certain there is no overlap between bits of the drawing). The performance implications of this aren't as bad as you might expect - at least with Ataxx where the things that change are the most complex. Speed: Cairo is blindingly fast for lines, polygons and similar things with solid-color fills. It is terrible when using pixmaps for fills and when using radial gradients. Your three-buffer method is a sane way to do things, but won't fix some of the underlying problems. Vector vs. bitmaps. Cairo is well set-up for vector stuff, but doesn't handle images well. Wonderful things could be done with vector-based drawing code, but at the expense of easy-to-make themes. I'm still not sure what the best way to go is, although I am seriously considering moving the Ataxx drawing engine back to being non-cairo just because of performance issues.
Hi Callum, can you elaborate on what you mean with "canonical view of the field"? The way I'd like to see it done is split in three objects (MVC), the field holding the state of the board, the view implementing the drawing and the controller implementing the blockops operations... anyway this would be pretty much a rewrite for little gain, so I don't plan to try it any time soon :) In the slightly updated[1] patch below I choose the way which required less changes: I made Field inherit from Blockops so that it has access to the field state, the subclass overrides redraw() from the base class and implements it throgh cairo [redraw() should probably be marked virtual, I need to check my C++]. Apart from that, what the patch does is simply go on and redraw the whole thing every time (the foreground surface is not needed anymore) and also fixups some minor things so that the patch is now playable for testing (the blocks are just red rectangles). The sad result is that the game is very unresponsive :( However from a bunch of timers sprinkled there the time doesn't seem to be spent in drawing... ideas? [1] I know I said I wasn't going to touch it soon, but I had half an hour to kill and I did just read your comment about redrawing the whole field maybe being acceptable so I went on and tried it.
Created attachment 51590 [details] [review] update (still just a test)
by the way, maybe the cairoification of the preview widget (actually just the background and the lines, not the block itself) should be split out and applied since it doesn't seem a performance problem and makes the disabled preview slightly less ugly.
"Canonical" was a very bad choice of words given that I didn't explain the model I was thinking of. I was taking MVC as a given, but was thinking of several objects acting together to get the model - hence the idea of one canonical object for the view to treat as the model. The details are really irrelevant since I'm not sure what was going through my head that night was a good idea. On the timing issue: you have to be very careful measuring Cairo stuff. It caches the drawing operations and then does the actual rendering in one block once it knows what it has to draw with. I'm not sure of the exact details, but measuring the time spent in small groups of Cairo calls can be *very* misleading. I tend to measure functional blocks (e.g. background, pieces and grid for Ataxx) and an overall time. I have frequently seen vast improvements in one block only to find the time has just moved to a later block. I have not yet found an efficient way to handle images/pixbufs in Cairo. This is probably where your speed problem lies. I suspect that anything which involves the large tranformation matrices needed by the interpolation routines causes trouble. I haven't tried turning down the interpolation quality (I tried a non-image based approach instead) but I expect that will help. Note that I haven't asked for advice on this yet either, so it may not be real problem.
Created attachment 51609 [details] [review] getting there I'm such an idiot sometimes... while at lunch it occurred to me that it wasn't a real performance problem but it was just me forgetting to redraw each time a key was pressed :/ With this version the game seems to playable without problems on my system even when using a bacground image. The blocks are still just semitransparent red rectangles because I have no clue from an artistic point of view on how to draw something nicer. Personally I feel that we should experiment with completely vectorial blocks since they can become very nice (e.g translucent etc), but that doesn't exclude the possibility of having pixamepped alternative thems: my idea was just the recache the blocks pango_surfaces on configure and use them as sources to draw; such surfaces can either be completely hand drawn or got from a loaded pixmap. Screenie attached below
Created attachment 51610 [details] screenie
I posted a handful of svg block themes to #300503 . FWIW, librsvg's cairo work might be ready in time for release with GNOME 2.14, but at this stage it's too early to know for certain. I'd probably suggest a fork of graphics stuff to work with it until we know for certain that Caleb, Dom, and Carl will pull it off, but I've been on a bit of a GNOME hiatus, and will be for at least another month.
Since this seems to have been sitting here for a while, I have applied Paolo's patch and started playing with it. I have the foreground drawing going to the point where it looks OK, but it doesn't use external theme files and is a bit slower. I will probably commit what I have tonight since it is at least playable.
Hey Callum, it's great to see that you have picked things up where I left! I really had no time to work on this and I was also waiting for the ability to draw cairo paths from svg to appear. I also had some pending stuff in my tree that I didn't attach here, but I have not been touching it for such a long time that I really don't recall what it did, oh well probably you fixed that up already. One changeset that it's easy to extract, but not very important was moving the Block and SlotType structs from tetris.h to blockops.h. The preview still uses the old graphics (well, you probably know this) and has an ugly yellow border: that was just me trying to draw my first cairo recangle and can surely go away. Minor nitpick - feel free to tell me to fsck off, you are the one who maintains it - any chance you could use the same coding style as the surrounding code when adding functions?
We should be able to use librsvg with the cairo backend sometime during this cycle, but since we won't have the ability to carve up the theme with subpixbuf, they plan to add API to grab graphics out of a single SVG by passing in an xlink handle like "king_of_hearts" or "gnometris_block_one". I'll probably patch it up by the time it's ready.
Paolo: I figured that if you had anything else significant you would have posted it and since I was "in the mood" even if I did duplicate your work I would enjoy myself. From here, I want to abstract parts of the drawing code so that "theme engines" can be used. One would obviously imitate the traditional pixmap scheme, but I have a couple of other ideas that can't be done that way. Once I have something approximating themes running again I'll close this bug since all regressions I know of will then be fixed.
Callum: cool, as I said I am really happy you picked this up, my pending changes were just small stuff and anyway I totally forgot what exactly they were. I tried cvs out and it's really nice, just some small observations: - with the default black backround the blocks look weird since you cannot see the think border - when there is just a 1x1 'hole' it's really hard to distinguish it from a block - when you start having many blocks on the field, moving laterally becomes noticeably slower Keep up the good work!
The drawing style is definitely not final - it is meant to be something that is representative of a reasonable drawing-load for cairo. Having said that, I'd forgotten about testing against the default background - I'm using a pixmap to give the code the worst-case for speed (although background detail is only an issue at resizing time). I know about the speed issues, on my machine there is no slowdown, but the CPU activity indicates that it is borderline. I hope to get it only drawing changes in the near future. For Ataxx that made a vast difference in responsiveness.
Right, the code in CVS is now in a tolerable state and has no feature regressions. So I am closing the bug. One note: themes are now code rather than images. In principle a theme could be written that could read images off disk, but that hasn't been written yet and probably won't be until the cairo back-end to librsvg is stable. Also, the existing themes are not very pretty. The default "plain" theme is OK and reasonably fast, but the "joined" theme is currently just a demo of what you can do if you aren't restricted to a set of square bitmaps. See the file renderer.cpp for details (themes are C++ classes so that you can change only the bits you want to when writing a new theme).
This bug is being reassigned to the "quadrapassel" component so we can close the gnometris bugzilla component. Apologies for the mass email!