Re: [Inkscape-devel] Cairo rendering proposal

On Mon, Apr 26, 2010 at 1:46 PM, Jasper van de Gronde
In any case, you're definitely right that it would add complexity, and it is by no means meant as /the/ solution, but I do think we can and should consider some alternatives other than our way and the Illustrator way.
A simple and relatively cheap method, similar to your proposal, which I for some time was thinking of trying, would be to start by testing, from top to bottom, if any object in the repaint buffer covers it completely and has no opacity and filters; if yes, then discard objects below it and render only from this object up. However, to be of any use, this method must be used with our current buffered redraw, where the probability of an object to cover the entire strip buffer is significant. With the Illustrator method, this optimization would only work if some object covers the entire screen, which is much less probable.

That's the ideal. In practice, "any point in time" will in any case be limited to a limited number of code junctions, and if we space them too densely, we will lose too much time on the constant checks "do we have an interrupting event?" So we need to find a compromise between granularity of interruption points and responsiveness to events.
There are several checks per shape even now (e.g. checking if the shape has a filter, and whether it is transparent), so even many checks likely wouldn't make a difference.
So, it may be that something - like the hardware-accelerated rendering
- forces us to switch to Illustrator repaint method. But in any case
please don't do this switch without careful consideration and testing. Our method has served us very well, and what you call "artefacts" are in fact, after you get used to them, useful hints that enable you to work much faster than what our slow renderer would alow otherwise.
It's not necessary to switch to the Illustrator method (which by the way I find very hard to justify). What I wanted is to redraw everything in one go. It would make the renderer less responsive, but I intended that to be overcome by the speed increase from hardware acceleration.
Obviously there are two conflicting goals: 1. Respond to user actions as quickly as possible. 2. Do not show a partially redrawn screen. I think the best solution is an adaptive strategy. Here's an example of one.
First, I would make the renderer reasonably interruptible. Then I would set a configurable threshold, say 20ms, for what should be considered "instantaneous". On every redraw I set a 20ms timer. If the timer expires before I'm done, I throw away whatever I was doing, split the image into 4 tiles and start over. When drawing each tile I set a timer as well, and I progressively reduce the tile size as the timers expire before I'm done, until I hit some minimum tile size at which expiring timers do not cause a tile shrink. I would keep rendering at that tiling level as long as the redraw of each tile takes more than 1/4 of the time limit.
A simple and relatively cheap method, similar to your proposal, which I for some time was thinking of trying, would be to start by testing, from top to bottom, if any object in the repaint buffer covers it completely and has no opacity and filters; if yes, then discard objects below it and render only from this object up.
The action of pruning invisible objects is useful in itself. If it could be done without rendering, only by considering the geometry and style of the objects, we could add it to "Vacuum defs" (which should be named "Clean up document" and do a few things more than it does at present, for example remove broken references).
Regards, Krzysztof

2010/4/26 Krzysztof Kosiński <tweenk.pl@...400...>:
It's not necessary to switch to the Illustrator method (which by the way I find very hard to justify). What I wanted is to redraw everything in one go. It would make the renderer less responsive, but I intended that to be overcome by the speed increase from hardware acceleration.
Please don't do that. No matter how fast is renderer, there will always be documents for which it's not fast enough. Especially since our format is SVG, where it takes just minutes to write a script generating a document with thousands of objects.
Obviously there are two conflicting goals:
- Respond to user actions as quickly as possible.
- Do not show a partially redrawn screen.
I really don't see why 2 is a goal at all. If the renderer becomes as fast as you seem to imply, then this partial render will be simply impossible to notice! On the other hand, if it _can_ be noticed, it means the rendering is not fast enough, and we need to spend all effort on achieving goal 1.
First, I would make the renderer reasonably interruptible. Then I would set a configurable threshold, say 20ms, for what should be considered "instantaneous". On every redraw I set a 20ms timer. If the timer expires before I'm done, I throw away whatever I was doing, split the image into 4 tiles and start over. When drawing each tile I set a timer as well, and I progressively reduce the tile size as the timers expire before I'm done, until I hit some minimum tile size at which expiring timers do not cause a tile shrink. I would keep rendering at that tiling level as long as the redraw of each tile takes more than 1/4 of the time limit.
Oh. Are you really suggesting to do all this during interactive work? Did you make an estimate of how much time this would waste in the worst case? And why all this waste - just for the sake of "all at once" redraw?
On the other hand, some kind of adaptive buffer sizing may be beneficial indeed. But it must go in the opposite direction - and never discard anything. Namely: time the render of each buffer; if it was rendered fast enough, double the size of the next buffer (i.e. render two buffers at once); if it was rendered slow, halve the next buffer until some fixed minimum is reached. This strategy may reduce the overall number of buffer strips on the screen and perhaps speed it up somewhat without damaging interactivity too much.
Another reason this approach is better than yours is that for your proposal to work, you need a way to be able to stop the actual render at a given time. There's no guarantee Cairo will allow you to do that. If it _were_ possible to "pause" rendering at a given time, retrieve the partial results and then resume the render from this place _without_ the renderer losing its setup and path data, then it would be the ideal solution for us - we would have fine-grained interactive response and no time wasted on setting up buffers, re-tesselating paths, etc. But I really doubt Cairo will be that nice :)
The action of pruning invisible objects is useful in itself. If it could be done without rendering, only by considering the geometry and style of the objects, we could add it to "Vacuum defs" (which should be named "Clean up document" and do a few things more than it does at present, for example remove broken references).
That might be a useful function, but it is not very relevant to "vacuum defs", and even more offtopic in this discussion :)

bulia byak wrote:
... On the other hand, some kind of adaptive buffer sizing may be beneficial indeed. But it must go in the opposite direction - and never discard anything. Namely: time the render of each buffer; if it was rendered fast enough, double the size of the next buffer (i.e. render two buffers at once); if it was rendered slow, halve the next buffer until some fixed minimum is reached. This strategy may reduce the overall number of buffer strips on the screen and perhaps speed it up somewhat without damaging interactivity too much.
This sounds like a good idea, except for drawings with regions of wildly varying complexity. (And/or when zooming.) Then the "feel" of the renderer would vary quite a lot depending on where you came from. I guess we'd have to try it to see how irritating this is.

Hi. My background is in video compression and OpenGL (3D stuff) but perhaps I have something useful to contribute to this discussion. Also, I am an old SodiPodi user and have been using Inkscape since shortly after it forked. Currently I use Inkscape to do all my drawings for school. Anything which would help improve the responsiveness of Inkscape would be a boon to me personally.
bulia byak wrote:
2010/4/26 Krzysztof Kosiński <tweenk.pl@...400...>:
It's not necessary to switch to the Illustrator method (which by the way I find very hard to justify). What I wanted is to redraw everything in one go. It would make the renderer less responsive, but I intended that to be overcome by the speed increase from hardware acceleration.
Please don't do that. No matter how fast is renderer, there will always be documents for which it's not fast enough. Especially since our format is SVG, where it takes just minutes to write a script generating a document with thousands of objects.
Obviously there are two conflicting goals:
- Respond to user actions as quickly as possible.
- Do not show a partially redrawn screen.
I really don't see why 2 is a goal at all. If the renderer becomes as fast as you seem to imply, then this partial render will be simply impossible to notice! On the other hand, if it _can_ be noticed, it means the rendering is not fast enough, and we need to spend all effort on achieving goal 1.
I agree that 2 should not be a goal. -- Yet, I do not know why things should be rendered on screen. I am not familure with Cairo and the choices available probably differ, but in OpenGL rendering in an off screen buffer and then calling swapbuffers() is about an order of magnitude faster than rendering on screen. You could ever do it iteratively -- render first slice off screen, dump to current screen, render next slice off screen, compose with first, dump updated buffer to screen buffer, repeat until complete. Mmmmmuch faster.
First, I would make the renderer reasonably interruptible. Then I would set a configurable threshold, say 20ms, for what should be considered "instantaneous". On every redraw I set a 20ms timer. If the timer expires before I'm done, I throw away whatever I was doing, split the image into 4 tiles and start over. When drawing each tile I set a timer as well, and I progressively reduce the tile size as the timers expire before I'm done, until I hit some minimum tile size at which expiring timers do not cause a tile shrink. I would keep rendering at that tiling level as long as the redraw of each tile takes more than 1/4 of the time limit.
Oh. Are you really suggesting to do all this during interactive work? Did you make an estimate of how much time this would waste in the worst case? And why all this waste - just for the sake of "all at once" redraw?
Let's say it was off by a factor of 1000. 4^5 = 1024 so 20ms * 5 iterations = 100ms. Not bad.
On the other hand, some kind of adaptive buffer sizing may be beneficial indeed. But it must go in the opposite direction - and never discard anything. Namely: time the render of each buffer; if it was rendered fast enough, double the size of the next buffer (i.e. render two buffers at once); if it was rendered slow, halve the next buffer until some fixed minimum is reached. This strategy may reduce the overall number of buffer strips on the screen and perhaps speed it up somewhat without damaging interactivity too much.
This is better though. Don't throw away rendered restate which has not been invalidated. If it is necessary to render on screen then altering the size of the slices is a good way to keep the updates flowing so the user knows Inkscape is still hard at work. Also, depending on what the performance hit associated with doing slices is... (if there isn't one, render line by line) we will want to increase the slice size if it falls below some value, say 5ms. This will help keep the render preforming at a visually consistent rate. See Jasper's email.
This is basically the same trick I used in the SV3 codex for quicktime. If it's taking too long/over size, reduce the next frame/slice, but keep the current one. Everyone seemed to like it in quicktime. Because the stream was buffered for video, if one frame was oversize that was alright, as long as we adjusted the size of future frames to keep the overall stream size on target. -- Then we did the two pass compressor... (not appropriate for Inkscape)
Another reason this approach is better than yours is that for your proposal to work, you need a way to be able to stop the actual render at a given time. There's no guarantee Cairo will allow you to do that. If it _were_ possible to "pause" rendering at a given time, retrieve the partial results and then resume the render from this place _without_ the renderer losing its setup and path data, then it would be the ideal solution for us - we would have fine-grained interactive response and no time wasted on setting up buffers, re-tesselating paths, etc. But I really doubt Cairo will be that nice :)
Humm, I don't know enough about Cairo...
*snip*
From a user perspective the Illustrator render method is a real downer, please don't do that. Objects poping up (or under), one is never quite sure it's finished, or if the piece I'm looking at is done, yuck.
Also, if all invalidated screen areas can't be updated in say 200ms, drop back to a faster rendering mode (outline) and then progressively fill in the finished render, while fielding user events (keystrokes). Think something similar to Vectorworks. Doing this would be a great improvement for scroll, zoom, interactive rotate, scale, etc!
Any way, sorry for butting in uninvited. I wish everyone the best in helping improve Inkscape.
-Sam George (ciradrak)

Jon Cruz wrote:
On Apr 26, 2010, at 3:31 PM, Krzysztof Kosiński wrote:
First, I would make the renderer reasonably interruptible. Then I would set a configurable threshold, say 20ms,
Why 20ms?
It's probably arbitrary, but it's reasonable.
30 frames / second = 33.33 ms For movies it's ok. You're only there for a few hours.
For all day use it was once thought that 60 frames / second would be enough, but some of us get headaches from all day use because we can see the flicker. yuck. :p
It takes around 70 - 80 frames / second to get past flicker fatigue. That is 12.5 ms. So this is the ideal. A 'perfect' refresh would be 12.5 ms or less, but I don't think it necessary to be /that/ fast in this context.
ciradrak
participants (5)
-
bulia byak
-
ciradrak
-
Jasper van de Gronde
-
Jon Cruz
-
Krzysztof Kosiński