Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Media queries #169

Merged
merged 34 commits into from
Feb 7, 2021
Merged

Media queries #169

merged 34 commits into from
Feb 7, 2021

Conversation

Dakror
Copy link
Contributor

@Dakror Dakror commented Jan 26, 2021

This is a big one.

This PR adds support for basic media queries in RCSS.

Usage & Features

  • RCSS media-blocks to apply properties conditional to context properties:
    • viewport width & height given in pixels, supports range (min-/max-)
    • resolution, aka dpi (using the density_independent_pixel_ratio value), supports range (min-/max-)
    • aspect ratio, using ratio notation like 16/9, internally using it as a float, supports range (min-/max-)
    • orientation "landscape" / "portrait", comparing viewport width against height
  • Creating composite queries using "and" operator

Example usage:

div {
	height: 48px;
	width: 48px;
}

@media (min-width: 640px) {
	div {
		height: 32px;
		width: 32px;
	}
}

@media (orientation: landscape) and (min-resolution: 90dpi) {
	div {
		height: 32px;
		width: 32px;
	}
}

@media (max-aspect-ratio: 4/3) {
	div {
		height: 64px;
		width: 64px;
	}
}

I've tried to adhere as closely as possible to the early CSS media query standards, by for instance inforcing fixed units like dpi for resolution, and pixels for width & height and reusing the CSS names for the various properties. The CSS feature of media distinction like "screen" etc. does not really apply in RCSS, therefore it was omitted. The current approach matches media blocks with an all or nothing approach, since only conjunctive chaining ("and") is allowed.

Internals

In order to assemble the correct style sheet for a given context configuration, a new top-level class above StyleSheet was introduced: the StyleSheetContainer. It maps a PropertyDictionary of the query-list properties to the contained sub-stylesheet.

A function UpdateMediaFeatures creates a new combined StyleSheet and stores it inside the container, giving external access through a non-owning pointer.

For parsing, new parsers needed to be introduced, namely for the aspect ratio notation, and others for the unit enforcing.

The style sheet parser received a new parsing method with its own state machine parsing the custom syntax of the media query list

The document re-compiles the stylesheet when it detects a context property change.

A unit test suite checking all properties is provided.

@Dakror Dakror marked this pull request as draft January 26, 2021 10:37
@Dakror
Copy link
Contributor Author

Dakror commented Jan 26, 2021

Hey @mikke89 can you tell me why the context parameter inside ReloadStyleSheet for InstanceDocumentStream is nullptr? If i pass the context that the method is receiving anyway, no issue seems apparent but i resolve the additional error that i reported in #170 regarding the missing sprites.

EDIT: Ah i seem to have found a warning appear when the document gets destructed, "Could not retrieve element in view, was it destroyed?" related to some data element getting released with UB state perhaps?

@Dakror
Copy link
Contributor Author

Dakror commented Jan 26, 2021

The issue with #170 and this, is that we need the context in the tmp document to compile a stylesheet.. what to do though..

@mikke89 mikke89 added the enhancement New feature or request label Jan 26, 2021
@mikke89
Copy link
Owner

mikke89 commented Jan 26, 2021

First of all, awesome and great work! You're learning the library fast :)
It's great to have this feature, I know other user have requested it as well.

I'll go through it in more detail later on.

Hey @mikke89 can you tell me why the context parameter inside ReloadStyleSheet for InstanceDocumentStream is nullptr?

Yeah, the main reason for this is that I wanted to ensure that the state of the current context is not changed when ReloadStyleSheet occurs. This can happen due to eg. events, and any state change from this function would be quite unexpected. Is there still an issue here after #171?

@Dakror
Copy link
Contributor Author

Dakror commented Jan 26, 2021

Yeah since we can't compile the stylesheet in the context-less temporary document, some warnings arise, particularly when images look for sprites (found it in my game as a test case :D ) . While these warnings don't cause any real problem since only the stylesheetcontainer is used from the temporary document, we still need to quench those warnings somehow..

@mikke89
Copy link
Owner

mikke89 commented Jan 26, 2021

Oh, I see. I'll have to think about that some more.

Copy link
Owner

@mikke89 mikke89 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken a detailed look at this now. Thanks a lot, looking forward to merging this :) I'm also pleased to see the very nice PR description.

Please see the detailed review comments, as well as the following remarks.

  • The dpi unit for resolution doesn't really make sense as dpi vs pixels are fixed in RmlUi. The unit is actually more like the CSS dppx (dots per pixel), but this also doesn't make sense in RmlUi, because one dot is always one pixel. What we really want is dp-ratio dpr, but this again may be confusing to someone coming from CSS. However, I see that CSS adds an x unit alias, and this unit would actually also make sense for us. So I suggest that we call it that. More long-term, we might consider adopting the CSS approach to pixels, so that one RCSS pixel can be scaled relative to device pixels.

  • I can see several RCSS problems introduced with this change. Eg. the 'demo' sample is not able to override the 'max-width' property. The VisualTests demo looks very broken. For some strange reaon, warnings are often given when tables are present, see eg. table_01 in VisualTests. Some of these problems may be due to specificity, eg. make sure all the rules are applied in the order they appear. Would be nice to have some unit tests for this.

  • There is a substantial performance regression when loading documents, more than double the loading time compared to master. Haven't looked into it in any more detail, but we need to figure this out, it should not be measurably slower when not using media queries. I suspect there is a lot of duplicate work going on, the merging in particular should be looked at.

Here are some more notes for future changes. We can merge this PR before we work on any of this, but leaving these notes here for now.

  • Need to test keyframes and sprite sheets in media block. I suspect eg. sprites won't properly updated as is. Any element/function that uses Element::GetStyleSheet should be investigated in particular.

  • Other logic operations: Comma (OR), not... Maybe Media Queries level 4 syntax eg. (100px < width <= 1200px)? If we do implement that, then we may discard the min/max-width properties as the other operators are so much nicer.

  • Instead of dirtying style sheets and definitions every time context dimensions and dp-ratio change, add flags to style sheet containers that tell whether they need to be dirtied when that value changes. So that we don't need to do anything if media queries are not used.

Source/Core/Element.cpp Outdated Show resolved Hide resolved
Source/Core/ElementStyle.cpp Outdated Show resolved Hide resolved
Include/RmlUi/Core/Element.h Outdated Show resolved Hide resolved
Include/RmlUi/Core/ID.h Show resolved Hide resolved
Include/RmlUi/Core/Property.h Outdated Show resolved Hide resolved
Source/Core/StyleSheetParser.cpp Show resolved Hide resolved
Source/Core/StyleSheetParser.cpp Show resolved Hide resolved
Source/Core/StyleSheetParser.cpp Outdated Show resolved Hide resolved
Source/Core/StyleSheetParser.cpp Outdated Show resolved Hide resolved
Source/Core/StyleSheetParser.cpp Show resolved Hide resolved
@Dakror
Copy link
Contributor Author

Dakror commented Feb 2, 2021

Visual tests are fixed in 98bcc50

@Dakror
Copy link
Contributor Author

Dakror commented Feb 2, 2021

Do you have any heavy hitting test for the performance issues you mentioned? I can't seem to detect a noticeable difference, looking for instance at unit test run duration.

@mikke89
Copy link
Owner

mikke89 commented Feb 2, 2021

Do you have any heavy hitting test for the performance issues you mentioned? I can't seem to detect a noticeable difference, looking for instance at unit test run duration.

Yeah, if you run the benchmarks sample, take a look at the ElementDocument:LoadDocument benchmark, that is what I used.

@Dakror
Copy link
Contributor Author

Dakror commented Feb 2, 2021

I've investigated the performance increase, and it purely stems from the heavy hitting DirtyDefinition call in SetStyleSheetContainer upon ProcessHeader call in ElementDocument. This adds a solid 5-10ms where normally only 5ms total were needed (on my system). Mitigating this would require adding all the relevant flags for invalidating only what's necessary and i think this is beyond me or this PR.

@mikke89
Copy link
Owner

mikke89 commented Feb 2, 2021

Alright, thanks for looking into it. As long as we can fix the performance later on, I'm good with it for now.

@Dakror
Copy link
Contributor Author

Dakror commented Feb 5, 2021

Working on a unit test i discovered that yeah indeed sprite updates are not updated, specifically i tested wether an ElementImage would update, but there is no method reacting to a change of that sort in that class. What do you think is the best approach to notify every element of relevant changes of this sort? I feel like this PR is getting a bit too big...

@Dakror
Copy link
Contributor Author

Dakror commented Feb 5, 2021

I would say sprites + keyframes get a second PR? I would appreciate some help

@mikke89
Copy link
Owner

mikke89 commented Feb 5, 2021

Yeah, I had the same issue in the high_dpi branch, which is why I expected it here as well. I had it half-working there, but now that we have media queries we probably want to make changes to the approach taken there.

Anyway, I'm fine with leaving that part out of this PR, we can work on that later.

@Dakror Dakror requested a review from mikke89 February 6, 2021 18:32
Copy link
Owner

@mikke89 mikke89 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looking good, just a few minor comments here.

So, after we merge this PR, we have the following high priority things to solve:

  • The lifetime issue / potential crashes described here.
  • The performance regression when loading documents.

I consider these two issues release blocking.

Additional things to work on, but lower priority (taken from previous post).

  • Support for keyframes and sprite sheets.
  • Expanded query syntax.
  • Dirty flags.

Source/Core/StyleSheetContainer.cpp Outdated Show resolved Hide resolved
Source/Core/StyleSheetParser.h Outdated Show resolved Hide resolved
Source/Core/StyleSheetSpecification.cpp Outdated Show resolved Hide resolved
Source/Core/PropertyParserRatio.h Outdated Show resolved Hide resolved
@Dakror
Copy link
Contributor Author

Dakror commented Feb 7, 2021

From my point of view, these issues would all be solvable using dirty flags because:

  • Performance regression comes from the blunt DirtyDefinition which is super expensive
  • Sprites and Keyframes aren't properly invalidated currently anyway
  • Lifetimes could be monitored more closely and also tested against using dirty flags

@mikke89
Copy link
Owner

mikke89 commented Feb 7, 2021

Yeah, dirty flags sounds like a good idea. I had a closer look at the lifetime issue, and it looks like we're not storing any related references around from the style sheet. So it should be all good, at least for now, and I feel a lot more comfortable merging it as is. But we should try to make it future proof as well.

Thank you for the hard work, cheers!

@mikke89 mikke89 merged commit dd99820 into mikke89:master Feb 7, 2021
@Dakror Dakror deleted the media-queries branch February 7, 2021 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants