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

Store ClipScrollNodes in a vector #2449

Merged
merged 1 commit into from Feb 23, 2018

Conversation

@mrobinson
Copy link
Member

mrobinson commented Feb 21, 2018

Instead of storing ClipScrollNodes in a HashMap, store them in a vector.
This removes the CPU cost of accessing any particular ClipScrollNode. In
its place is a somewhat clunky system of translating a ClipId into a
ClipScrollIndex. These are allocated based on the total number of
ClipIds per display list, so the translation should be fairly cheap. A
per-pipeline offset and cache is stored, so the number of HashMap
lookups for display list flattening should be the same order as the
number of pipelines.

The big trade off here is that when descending into iframes, we need to
allocate space in the ClipScrollNode array ahead of time. If these
memory allocations turn out to be a problem, we can modify the code to
try to mutate existing nodes or to use a more complicated system of sub
vectors or slices.


This change is Reviewable

@mrobinson mrobinson requested review from kvark and glennw Feb 21, 2018
@mrobinson
Copy link
Member Author

mrobinson commented Feb 21, 2018

Sorry for the size of this PR. It's difficult to make such a deep change without changing a lot of code.

Here is the Gecko try job for this change: https://treeherder.mozilla.org/#/jobs?repo=try&revision=8950bf1f433b3fd3b71b5cb526d9cb71fea98770&selectedJob=163434876

@mrobinson
Copy link
Member Author

mrobinson commented Feb 21, 2018

@staktrace Heads up about the Gecko changes. I had to modify gfx/webrender_bindings/webrender_ffi_generated.h manually because it seems that cbindgen has trouble with the current WebRender source tree.

@staktrace
Copy link
Contributor

staktrace commented Feb 21, 2018

I feel like the gecko-side changes should either assert that u64 and usize have the same number of bits (which might not actually be true on all platforms) or better, we should replace uint64_t with size_t wherever we're keeping WR clipids in C++. This is probably hard to do at the moment but will be a lot simpler if I land my changes for using ClipChains (but that's blocked on #2388).

@kvark
Copy link
Member

kvark commented Feb 21, 2018

Looks like a decent optimization, at the cost of increased re-direction complexity of the code.


Reviewed 12 of 12 files at r1.
Review status: all files reviewed at latest revision, 13 unresolved discussions.


webrender/src/clip_scroll_tree.rs, line 135 at r1 (raw file):

        // TODO(mrobinson): We should eventually make this impossible to misuse.
        debug_assert!(self.nodes.len() >= 1);
        ClipScrollNodeIndex(1)

didn't we have constants defined for those special indices somewhere?


webrender/src/clip_scroll_tree.rs, line 466 at r1 (raw file):

            if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {

why is this removed?


webrender/src/clip_scroll_tree.rs, line 505 at r1 (raw file):

        }

        if index.0 == self.nodes.len() {

I'd refactor that code into:

if let Some(ref mut empty_node) = self.nodes.get_mut(index.0) {
    *empty_node = node;
    return
}
self.nodes.reserve_exact(index.0 + 1 - self.nodes.len());
self.nodes.extend((self.nodes.len() .. index.0).map(|_| ClipScrollNode::empty());
self.nodes.push(node);

webrender/src/clip_scroll_tree.rs, line 511 at r1 (raw file):

        if index.0 > self.nodes.len() {
            self.nodes.reserve_exact(index.0);

this is incorrect usage of reserve_exact. The parameter is meant to be the additional count, not total count


webrender/src/frame.rs, line 49 at r1 (raw file):

    cached_index: Option<(ClipId, ClipChainIndex)>,
    pipeline_offsets: FastHashMap<PipelineId, usize>,
    cached_pipeline_offset: Option<(PipelineId, usize)>,

need to comment cleanly the difference between cached_pipeline_offset and pipeline_offsets


webrender/src/frame.rs, line 54 at r1 (raw file):

impl ClipIdToIndexMapper {
    fn new() -> ClipIdToIndexMapper {

nit: Self ftw!


webrender/src/frame.rs, line 58 at r1 (raw file):

            clip_chain_map: FastHashMap::default(),
            cached_index: None,
            pipeline_offsets: FastHashMap::default(),

the whole implementation can probably be replaced by derive(Default)


webrender/src/frame.rs, line 60 at r1 (raw file):

            pipeline_offsets: FastHashMap::default(),
            cached_pipeline_offset: None,
            next_available_offset: 0,

what's the lifetime of those fields? I don't see them being reset


webrender/src/frame.rs, line 83 at r1 (raw file):

    }

    pub fn get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {

nit: maybe cache_clip_chain_index? to indicate that it's mutating


webrender/src/frame.rs, line 109 at r1 (raw file):

        let (index, pipeline_id) = match id {
            ClipId::Clip(index, pipeline_id) => (index, pipeline_id),
            ClipId::ClipChain(_) => unreachable!("Tried to use ClipChain as scroll node."),

not sure if this is truly unreachable. Sounds more like a panic to me


webrender/src/hit_test.rs, line 130 at r1 (raw file):

            let index = ClipScrollNodeIndex(index);
            if !self.pipeline_root_nodes.contains_key(&node.pipeline_id) {
                self.pipeline_root_nodes.insert(node.pipeline_id, index);

nit: use Entry API


webrender_api/src/display_item.rs, line 771 at r1 (raw file):

#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum ClipId {

This is significantly less used now. We should take another look at the type and see if it's still needed, or maybe rename it to something more verbose


webrender_api/src/display_list.rs, line 518 at r1 (raw file):

                    },
                    ClipChain(specific_item, clip_chain_ids) => {
                        DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);

clip chain doesn't count for total clips?


Comments from Reviewable

@mrobinson
Copy link
Member Author

mrobinson commented Feb 22, 2018

Review status: all files reviewed at latest revision, 13 unresolved discussions.


webrender/src/clip_scroll_tree.rs, line 135 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

didn't we have constants defined for those special indices somewhere?

We have constants for ClipId, but they are semantically different. This is a good idea though, so I've added constants for these ones in clip_scroll_tree.rs.


webrender/src/clip_scroll_tree.rs, line 466 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

why is this removed?

I had fixed this, but the fix was sitting uncommitted in my working tree.


webrender/src/clip_scroll_tree.rs, line 505 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

I'd refactor that code into:

if let Some(ref mut empty_node) = self.nodes.get_mut(index.0) {
    *empty_node = node;
    return
}
self.nodes.reserve_exact(index.0 + 1 - self.nodes.len());
self.nodes.extend((self.nodes.len() .. index.0).map(|_| ClipScrollNode::empty());
self.nodes.push(node);

Okay. This seems fine to me as well. I had to make a couple minor changes to make the borrow checker happy. I've also added a comment here explaining why we have to do this instead of Vec::resize.


webrender/src/clip_scroll_tree.rs, line 511 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

this is incorrect usage of reserve_exact. The parameter is meant to be the additional count, not total count

Nice catch.


webrender/src/frame.rs, line 49 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

need to comment cleanly the difference between cached_pipeline_offset and pipeline_offsets

I added comments for all the members and renamed cached_index to cached_clip_chain_index.


webrender/src/frame.rs, line 54 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

nit: Self ftw!

Done.


webrender/src/frame.rs, line 58 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

the whole implementation can probably be replaced by derive(Default)

Done.


webrender/src/frame.rs, line 60 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

what's the lifetime of those fields? I don't see them being reset

This lives as long as the FlattenContext which is only alive as long as display list flattening is happening. This struct is used only be used once and then discarded.


webrender/src/frame.rs, line 83 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

nit: maybe cache_clip_chain_index? to indicate that it's mutating

Hrm. I'm not sure because this is doing two things getting the index and caching it. I'm not sure if get_and_cache_clip_chain_index is any clearer, but maybe that's another option.


webrender/src/frame.rs, line 109 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

not sure if this is truly unreachable. Sounds more like a panic to me

Done.


webrender/src/hit_test.rs, line 130 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

nit: use Entry API

Done.


webrender_api/src/display_item.rs, line 771 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

This is significantly less used now. We should take another look at the type and see if it's still needed, or maybe rename it to something more verbose

I confirmed that these are all still necessary.


webrender_api/src/display_list.rs, line 518 at r1 (raw file):

Previously, kvark (Dzmitry Malyshau) wrote…

clip chain doesn't count for total clips?

ClipChains don't have corresponding ClipScrollNodes so they don't increase the count.


Comments from Reviewable

@mrobinson
Copy link
Member Author

mrobinson commented Feb 22, 2018

@staktrace I modified the Gecko-side patch to store size_t for ids that correspond to ClipScrollNodes. It wasn't a lot of work (though did require me to edit more of the auto-generated bindings by hand).

The Gecko try for this modified version is here: https://treeherder.mozilla.org/#/jobs?repo=try&revision=acdbaae3df471bd15eb355520144af790a5e9b13

I still plan to look at #2388 today, but maybe it doesn't need to block this change?

@mrobinson mrobinson force-pushed the mrobinson:linear-scroll-node-id branch from 4df8a29 to b76ae01 Feb 22, 2018
@staktrace
Copy link
Contributor

staktrace commented Feb 22, 2018

Looks like your try push has compilation failures so the patch might need some tweaking. Also FYI we have a new cbindgen version posted which should allow you to regenerate the bindings without manual tinkering. You'll need to install/run it with rust nightly though:

cargo +nightly install --force cbindgen
cd $ROOT_OF_MOZILLA_TREE
rustup run nightly cbindgen toolkit/library/rust/ --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h

Agreed that #2388 doesn't need to block this - but I'd like to review the final gecko-side changes for this PR once you have the compilation failures sorted out. Assuming it's a straightforward replacement of uint64_t to size_t in the relevant places it should be fine.

@mrobinson
Copy link
Member Author

mrobinson commented Feb 22, 2018

@staktrace Thanks for the information about cbindgen. I've kicked off another Gecko try that should hopefully fix the build failures on 32-bit Linux: https://treeherder.mozilla.org/#/jobs?repo=try&revision=cd9b9ede957425da328fe7dd38fc215dc7475fbd

@mrobinson
Copy link
Member Author

mrobinson commented Feb 22, 2018

I also confirmed that cbindgen didn't materially change code related to my change other than correcting a comment and fixing the implementation of the == operator override for BuiltDisplayList.

@kvark
Copy link
Member

kvark commented Feb 22, 2018

:lgtm:


Reviewed 4 of 4 files at r2.
Review status: all files reviewed at latest revision, all discussions resolved.


Comments from Reviewable

@kvark
Copy link
Member

kvark commented Feb 22, 2018

@bors-servo
Copy link
Contributor

bors-servo commented Feb 22, 2018

📌 Commit b76ae01 has been approved by kvark

@bors-servo
Copy link
Contributor

bors-servo commented Feb 22, 2018

🔒 Merge conflict

@bors-servo
Copy link
Contributor

bors-servo commented Feb 22, 2018

The latest upstream changes (presumably #2443) made this pull request unmergeable. Please resolve the merge conflicts.

Instead of storing ClipScrollNodes in a HashMap, store them in a vector.
This removes the CPU cost of accessing any particular ClipScrollNode. In
its place is a somewhat clunky system of translating a ClipId into a
ClipScrollIndex. These are allocated based on the total number of
ClipIds per display list, so the translation should be fairly cheap. A
per-pipeline offset and cache is stored, so the number of HashMap
lookups for display list flattening should be the same order as the
number of pipelines.

The big trade off here is that when descending into iframes, we need to
allocate space in the ClipScrollNode array ahead of time. If these
memory allocations turn out to be a problem, we can modify the code to
try to mutate existing nodes or to use a more complicated system of sub
vectors or slices.
@mrobinson mrobinson force-pushed the mrobinson:linear-scroll-node-id branch from b76ae01 to b548082 Feb 23, 2018
@mrobinson
Copy link
Member Author

mrobinson commented Feb 23, 2018

@kvark @staktrace Thanks for looking at this PR. I've rebased and will land it now.

@mrobinson
Copy link
Member Author

mrobinson commented Feb 23, 2018

@bors-servo r=kvark

@bors-servo
Copy link
Contributor

bors-servo commented Feb 23, 2018

📌 Commit b548082 has been approved by kvark

@bors-servo
Copy link
Contributor

bors-servo commented Feb 23, 2018

Testing commit b548082 with merge 8e2672e...

bors-servo added a commit that referenced this pull request Feb 23, 2018
Store ClipScrollNodes in a vector

Instead of storing ClipScrollNodes in a HashMap, store them in a vector.
This removes the CPU cost of accessing any particular ClipScrollNode. In
its place is a somewhat clunky system of translating a ClipId into a
ClipScrollIndex. These are allocated based on the total number of
ClipIds per display list, so the translation should be fairly cheap. A
per-pipeline offset and cache is stored, so the number of HashMap
lookups for display list flattening should be the same order as the
number of pipelines.

The big trade off here is that when descending into iframes, we need to
allocate space in the ClipScrollNode array ahead of time. If these
memory allocations turn out to be a problem, we can modify the code to
try to mutate existing nodes or to use a more complicated system of sub
vectors or slices.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/2449)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented Feb 23, 2018

☀️ Test successful - status-appveyor, status-taskcluster, status-travis
Approved by: kvark
Pushing 8e2672e to master...

@bors-servo bors-servo merged commit b548082 into servo:master Feb 23, 2018
3 of 4 checks passed
3 of 4 checks passed
code-review/reviewable 5 files left (kvark)
Details
Taskcluster (pull_request) TaskGroup: success
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
homu Test successful
Details
@mrobinson mrobinson deleted the mrobinson:linear-scroll-node-id branch Feb 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants
You can’t perform that action at this time.