This is a pipe dream, I imagine, but would it be possible to get people to
cache their stuff only inside size_allocate(); or draw();, and just do
something dumb like lie to the user and warn if they do it outside of one
of those paths?

I think doing drawing of the DND window outside of the frame clock is a bit
much --  we should be synchronized to that in any case.

I haven't investigated how much breakage something like that would cause.

On Thu, Jan 29, 2015 at 11:51 PM, Benjamin Otte <o...@gnome.org> wrote:

> Hey,
>
> Here's a problem I'm currently thinking about and would like input on. I
> discussed it with Matthias on #gtk+ today and he suggested I'd reach out to
> more people. I've included the log below and formatted it for clarity so
> that it reads like a QA-style interview. I hope it is not too inconvenient
> to read.
>
> <Company> the question we're trying to answer is this: What CSS values
> does GtkStyleContext return?
> <Company> this question is relevant because every time we change the CSS
> tree, we need to (potentially) update the value
>
> <Company> 3.0.0 used the approach of immediately updating everything once
> the css tree changed somehow
> <Company> so if you did: context.add_class("a"); context.add_class("b");
> context.add_class("c");
> <Company> it would recompute the CSS 3x
> <Company> which gets real slow really fast
>
> <Company> in 3.4 or so I changed that to always return a stale value
> <Company> until we validated the style via gtk_container_idle_sizer()
> (when the frame clock runs)
> <Company> so from that point on everything was fast, but the values were
> sometimes incorrect
>
> <mclasen> wouldn't you want to mark the values as stale, and force a
> recompute when somebody asks for a value ?
> <Company> ideally, you would
> <Company> the reason I did not do that was the style-updated signal
> <Company> when should that be emitted?
> <Company> if we emit it once a context gets invalid, we go back to 3.0
> performance
> <Company> because every widget queries values in the style-updated
> handler...
>
> <mclasen> you could do some freeze/thaw to batch the 3 emissions from 3
> add_class calls
> <Company> I don't want to add freeze/thaw calls though, everybody would
> get those wrong
>
> <mclasen> who needs to listen to style-updated anyway ?
> <mclasen> other than the gtk redraw machinery
> <Company> mostly just that
> <Company> GtkWidget needs to update the pango context
> <Company> GtkImage needs to release the cached surface
> <Company> things like that
> <Company> and of course we need to queue_draw() or queue_resize()
>
> <Company> here's another caveat: computing the CSS values requires
> computing the CSS values of the parent
>
> <mclasen> is that on the person changing the style context, or do we do
> that automatically ?
> <Company> GtkWidget::style-updated does that
> <Company> based on the GtkCssAffects settings of the changed style
> properties
>
> <mclasen> my mental model of this is that css values are used when drawing
> happens, so they need to be current at that point
> <mclasen> and drawing happens when the frame clock comes around to tell us
> <Company> yes
> <Company> when the frame clock triggers, we (1) recompute styles, (2)
> size_allocate everything pending, (3) draw
>
> <mclasen> whether css values are up-to-date at other points in the frame
> cycle should not be important
> <mclasen> except for those 3rd party users who randomly poke at style
> contexts...
> <Company> but it is important
> <Company> otherwise you get shrinking gnome terminals and white firefox
> text
> <Company> the gnome terminal case is actually rather complex to get right
> with the "should not be important" assumption
> <Company> because it needs to compute a bunch of values based on multiple
> different widgets
> <Company> and it needs to compute those before size allocation happens
> <Company> in short: It needs to happen in style_updated() at the latest -
> but which style-updated, when it depends on multiple widgets?
>
> <halfline> can't you just give the right value any time the user asks for
> it (updating the cache for that property if it's stale), but also keep
> style-updated where it happens now ?
> <Company> that's one thing we could do
> <Company> there's many solutions to the problem(s)
> <Company> I haven't reached the point yet where want to talk about
> solutions
> <Company> so far I'm trying to list the problem(s) :)
>
> <mclasen> 'before size allocation' sounds like a global thing though ?
> like a point in the frame cycle
> <mclasen> as long as we keep information about which styles are
> up-to-date, doing on-the-spot validation for people who call
> gtk_style_context_get_foo outside of regular frame-based drawing may just
> fine ?
> <mclasen> at least, only the people doing the poking are paying the price
> of extra recomputations then
>
> <Company> lemme do a short jsfiddle to show you how the web does it
> <Company> http://jsfiddle.net/nbt2myzb/1/
> <Company> I hope that example is simple enough
> <Company> you wanna understand why uncommenting that 1 line of JS makes
> the animation restart
> <Company> (disclaimer: I'm using firefox, no idea if webkit does things
> differently somehow)
>
> <mclasen> does it ?
> <mclasen> I seem to get blue when I click, which slowly fades to yellow
> <mclasen> no restarts that I can observe
> <Company> uncomment the line in the JS
> <Company> then press the "run" button
> <mclasen> ah, ok
> <mclasen> now it restarts
>
> <Company> you understand why it does that now?
> <mclasen> no, why does it do that ?
> <mclasen> I see that you are causing it to get the computed value of the
> color when I click
> <mclasen> but how that relates to restarting the animation is not clear to
> me
> <Company> right
> <Company> getting the color causes a recompute of the CSS
> <Company> but the "animate" class isn't set, so the style has no animation
> <Company> so all running animations get deleted
> <Company> then we add the animate class again, so the animation is created
> again
> <Company> of course, recreation means restart
> <mclasen> a bit surprising
> <Company> yes
> <Company> getting a value can have side effects
> <Company> otoh: You always get the correct CSS value for the current state
> of the CSS tree
>
> <mclasen> why is it correct,though ?
> <mclasen> wouldn't you expect to get the value of the color at the point
> you happen to be in in the aninmation ?
> <mclasen> ah no, you animate background, not color
> <Company> no, because you removed the animate class right above
> <Company> so the value is not animated when you query it
> <Company> (you can use x.backgroundColor if you want to)
> <mclasen> right
>
> <mclasen> so, whats the upshot for our situation - discourage poking at
> css values, because recomputing them will have ugly side effects ?
> <mclasen> where 'discourage' could include dropping ::style-updated
> <Company> our situation gets even more funky
> <Company> because our situation also has the question: "when do we emit
> style-updated"?
> <Company> we cannot drop it
> <mclasen> or only emit it at strategic times
> <Company> we have to emit queue_resize() at some point ;)
> <mclasen> clearly
>
> <Company> here's an updated version: http://jsfiddle.net/nbt2myzb/2/
>
> <mclasen> on the web, you don't have a ::style-updated equivalent, right ?
> <Company> I haven't found it
> <mclasen> I mean, we need to connect style changes to our size allocate
> machinery, but do we have to let apps hook onto that ?
> <Company> if we want to report correct values (and I think we want to) we
> can't do anything about restarting animations or other such side effects
> <Company> the only thing we can influence is at what point we emit
> style_updated
> <Company> we clearly do not want to emit it when the style gets invalidated
> <Company> we might want to emit it when the style gets validated
> <Company> or we might want to emit it only when the next frame happens
>
> <Company> if we do it when the style gets validated, getting a property
> might actually have the side effect of emitting a signal - or multiple
> signals, because validating a style requires validating the parent style
> <mclasen> is that so unusual ?
> <mclasen> you are getting a _computed_ value, so you shouldn't be too
> surprised that some computation happens...
> <mclasen> maybe call it compute_value() instead of get_computed_value() ?
> <Company> that a getter emits a signal? That is very unusual I think
> <Company> especially because those getters are called from render
> functions and other places
> <Company> say, if you start a dnd operation, you render text, which will
> update the labels's style which will update the toplevel's style which will
> cause I don't know what GtkWindow::style-updated does
> <Company> all just because we rendered a drag icon for text
> <Company> now granted, all of this would have run with the next frame
> clock update anyway, but the fact that it's happening earlier might be scary
>
> <Company> the 2nd option is problematic, too
> <Company> because if we only emit style-updated at frame boundaries
> <Company> the widget will not invalidate its own caches until that point
> <Company> so you will start the drag operation with old fonts (because we
> only update the pango context in style-updated)
>
> <mclasen> why does that drag operation have to render out-of-frame ?
> because we pass the rendered icon to the compositor ?
> <Company> we render the icon in the dnd event handler
> <Company> this is all mostly hypothetical btw, because nobody sane changes
> the widget font when dnd starts
> <Company> it's just the most realistic example i can come up with that
> illustrates potential problems
> <mclasen> not so sure, actually - we had proposals to do cut/paste in
> nautilus by keeping grayed out 'ghosts' of the selected files in the list
> until they get dropped elsewhere
>
> <Company> here's another possibile idea:
> <Company> We could add a GtkWidget::style-invalidated signal
> <Company> and then we have 2 signals: One when styles go invalid and one
> when styles go valid again
> <Company> not sure if this buys us anything, but we could add it
> <mclasen> and if you react to the invalidated signal by getting some
> value, you'll cause the updated signal to be emitted ?
> <Company> yeah
> <Company> and you kill performance
>
> <mclasen> who would use the one vs the other, and what would it buy us ?
> <Company> the idea would be to use the invalidate signal to drop caches
> <Company> so that say GtkImage drops the cached surface
> <Company> the "rendering dnd images" example again
> <Company> you can't just render the cached image, you need to first make
> sure the CSS values didn't change
>
> <Company> of, course you could have an emit_style_updated_if_necessary()
> function, too
> <mclasen> gtk_style_context_validate () ?
> <Company> yeah, like that
> <Company> depending on when we emit style-updated,
> gtk_style_context_get_color() might be good enough for that purpose
>
> <Company> of course, if someone does context.remove_class ("foo");
> context.add_class ("foo"); you don't wanna drop caches
> <Company> or rather: you'd like if you didn't do that
> <Company> and GtkWidget::style-invalidated would not handle that
> <Company> and that happening is a common theme
> <Company> with my_widget_update_style() { remove_all_styles ();
> apply_relevant_styles(); }
> <Company> and then call my_widget_update_style() from various setters that
> may or may not change the actual styles
>
> <Company> I'll leave you with that for now - if you have more questions or
> an opinion into which solution you prefer, please get back to me
> <Company> otherwise I'll make up my mind at some point (I don't have an
> opinion yet)
>
> _______________________________________________
> gtk-devel-list mailing list
> gtk-devel-list@gnome.org
> https://mail.gnome.org/mailman/listinfo/gtk-devel-list
>
>


-- 
  Jasper
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@gnome.org
https://mail.gnome.org/mailman/listinfo/gtk-devel-list

Reply via email to