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

Introduce a glyph cache thread. #565

Merged
merged 1 commit into from Nov 28, 2016
Merged

Introduce a glyph cache thread. #565

merged 1 commit into from Nov 28, 2016

Conversation

@glennw
Copy link
Member

glennw commented Nov 17, 2016

This is the first step towards making the glyph cache more efficient
and also run asynchronously to allow rendering to continue if the
glyph rasterizing becomes a bottleneck (e.g. during animated zooms).

Previously, we collected the list of glyphs that were required for
a frame, and ran a rayon loop to rasterize any that weren't in the
glyph cache. However, this meant that the glyph rasterizion could
not start until we knew the list of all required glyphs.

Now, all requests for text run glyphs are sent immediately to the
glyph cache thread. The glyph cache thread uses a worker thread
pool to immediately begin rasterizing any uncached glyphs as soon
as they are requested.

In the future, the idea is that instead of waiting at the block()
method for all pending glyphs to be rasterized, the backend thread
will instead receive small numbers of glyphs as they are rasterized.
This will allow the backend thread to make a decision that glyph
rasterizing is taking too long for this frame, and that we should
just draw a frame with the currently cached glyph sizes available.
When this occurs, the glyph cache thread will signal that a new frame
should be rendered when it completes rasterizing any pending glyphs.
This will allow us to keep rendering at 60fps during zoom, even
if the glyph rasterizing threads can't keep up (at a quality
tradeoff of using lower resolution glyphs).


This change is Reviewable

@glennw
Copy link
Member Author

glennw commented Nov 17, 2016

@pcwalton
Copy link
Collaborator

pcwalton commented Nov 17, 2016

Does rayon not have a task API (like a job queuing kind of thing)? I thought it did.

@pcwalton
Copy link
Collaborator

pcwalton commented Nov 17, 2016

@pcwalton
Copy link
Collaborator

pcwalton commented Nov 17, 2016

Niko says we do offer that, but it requires --feature unstable in Rayon. How about using that?

@emilio
Copy link
Member

emilio commented Nov 17, 2016

We use that in servo and stylo, so presumably that's ok for WR too.

@nikomatsakis
Copy link

nikomatsakis commented Nov 17, 2016

You would just do this:

rayon::scope(|s| {
    s.spawn(|s| /* parallel task */)
});

Each task gets a s argument that can be used to spawn further tasks into the same scope.

@glennw
Copy link
Member Author

glennw commented Nov 17, 2016

I'm not sure that will work for the future use case described in the commit, since from what I understand the thread creating that scope will be blocked until all those job(s) complete? I'll have a play with it today, maybe I'm misunderstanding how the API works.

@bors-servo
Copy link
Contributor

bors-servo commented Nov 18, 2016

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

@glennw
Copy link
Member Author

glennw commented Nov 18, 2016

@pcwalton @nikomatsakis I don't really see how that API fits the design of this PR.

I have a thread that handles the event loop (glyph requests etc) and I want that to be able to fire and forget about jobs, and then go back to the event loop and be able to do other work in the meantime. The idea is that at some point later in the frame, the event loop gets a message to block until those jobs are complete, but until that time its free to do other work.

But with the rayon scope, that event loop thread would be blocked until all child jobs complete, right? Is there an easy way to express that behaviour with rayon?

@glennw glennw force-pushed the glennw:atc3 branch 2 times, most recently from 085d3ab to ab0bd9d Nov 18, 2016
@nikomatsakis
Copy link

nikomatsakis commented Nov 18, 2016

@glennw

The scope API doesn't seem like exactly what you want, I agree. That said, it would be pretty easy to add an API that lets you "fork" a job outside of a scope. You could then have this job send a signal to a port or something when it is done (one could easily build a future etc atop this, if you wanted).

Another option would be to run the event loop itself as the scope, but the current APIs aren't really designed for that. I've been wanting to explore integrating as an "executor" in the futures library -- and then it might be a better fit -- but I haven't had time for that yet. (Similarly, I've been wanting to explore things like TBB's 'graph' abstraction, though I think there is a lot of overlap between that and futures.)

Anyway, if you would like me to add a "fork" API like the one I described I could do that quite readily. All the machinery is there, it's just not wrapped in a public, safe API.

Caveat: I've not read this current PR. =)

(I've also been wanting to explore integrating Rayon with the futures library, but haven't had time to do that.)

@nikomatsakis
Copy link

nikomatsakis commented Nov 18, 2016

I should add: one advantage of using Rayon uniformly across Servo is that you have the option all sharing a global thread pool, which tends to be a win, since you don't have multiple threadpools competing for resources. (Though it could cause longer latency, so you may not want that.)

@nikomatsakis
Copy link

nikomatsakis commented Nov 18, 2016

OK, I skimmed the PR. It seems like the API I proposed would act more-or-less precisely the same as the thread_pool crate.

@glennw
Copy link
Member Author

glennw commented Nov 18, 2016

@nikomatsakis Yep, if we had a rayon API that could work that way, it'd be great to be able to switch this back to rayon and make use of the shared thread pool.

@bors-servo
Copy link
Contributor

bors-servo commented Nov 18, 2016

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

@nikomatsakis
Copy link

nikomatsakis commented Nov 18, 2016

@glennw can definitely do that...

@glennw
Copy link
Member Author

glennw commented Nov 20, 2016

@nikomatsakis Would it make sense to land this PR now and switch it over to use rayon once that PR lands? (I guess what I'm really asking is how long will it be until that API is available in rayon for use?).

@nikomatsakis
Copy link

nikomatsakis commented Nov 21, 2016

@glennw Either way really. I'd probably be happy to land that patch as is, though I would expect to update the API slightly in the future (which might or might not be a breaking change for you). But if you wanted to land this PR and then compare a rayon-based impl when it becomes available, that's fine too.

I'm curious, is it easy for you to do some benchmarks now? (Or would it be, if I landed that PR?)

@glennw
Copy link
Member Author

glennw commented Nov 21, 2016

@nikomatsakis My preference would probably be to get this reviewed and landed, and then switch it to rayon as a follow up (then you won't need to rush to merge your PR either). Benchmarking between the two implementations should be easy to do. Sound ok, @pcwalton ?

@pcwalton
Copy link
Collaborator

pcwalton commented Nov 21, 2016

Yup, sounds good!

@glennw glennw force-pushed the glennw:atc3 branch from ab0bd9d to 964d2d4 Nov 21, 2016
@glennw
Copy link
Member Author

glennw commented Nov 21, 2016

Rebased and pushed. r? @pcwalton

@kvark
Copy link
Member

kvark commented Nov 21, 2016

There seem to be quite a few thread barriers on the glyph way to rasterization: RenderApi -> RenderBackend -> GlyphCacheThread -> WorkerThread. We might want to look (at some point later on) into reducing this depth.

@glennw
Copy link
Member Author

glennw commented Nov 22, 2016

enum GlyphCacheMsg {
/// Begin the frame - pass ownership of the glyph cache to the thread.
BeginFrame(FrameId, GlyphCache),
/// Add a new font

This comment has been minimized.

@mrobinson

mrobinson Nov 24, 2016

Member

Super-super-nit: this comment doesn't have a period, but all the others do.

@mrobinson
Copy link
Member

mrobinson commented Nov 24, 2016

This looks good to me as far as I understand it.

@glennw glennw force-pushed the glennw:atc3 branch from 964d2d4 to a7a3a04 Nov 24, 2016
@glennw
Copy link
Member Author

glennw commented Nov 24, 2016

@mrobinson Thanks, fixed up the nit and rebased.

@pcwalton
Copy link
Collaborator

pcwalton commented Nov 28, 2016

@bors-servo
Copy link
Contributor

bors-servo commented Nov 28, 2016

📌 Commit a7a3a04 has been approved by pcwalton

@bors-servo
Copy link
Contributor

bors-servo commented Nov 28, 2016

🔒 Merge conflict

@bors-servo
Copy link
Contributor

bors-servo commented Nov 28, 2016

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

This is the first step towards making the glyph cache more efficient
and also run asynchronously to allow rendering to continue if the
glyph rasterizing becomes a bottleneck (e.g. during animated zooms).

Previously, we collected the list of glyphs that were required for
a frame, and ran a rayon loop to rasterize any that weren't in the
glyph cache. However, this meant that the glyph rasterizion could
not start until we knew the list of all required glyphs.

Now, all requests for text run glyphs are sent immediately to the
glyph cache thread. The glyph cache thread uses a worker thread
pool to immediately begin rasterizing any uncached glyphs as soon
as they are requested.

In the future, the idea is that instead of waiting at the block()
method for all pending glyphs to be rasterized, the backend thread
will instead receive small numbers of glyphs as they are rasterized.
This will allow the backend thread to make a decision that glyph
rasterizing is taking too long for this frame, and that we should
just draw a frame with the currently cached glyph sizes available.
When this occurs, the glyph cache thread will signal that a new frame
should be rendered when it completes rasterizing any pending glyphs.
This will allow us to keep rendering at 60fps during zoom, even
if the glyph rasterizing threads can't keep up (at a quality
tradeoff of using lower resolution glyphs).
@glennw glennw force-pushed the glennw:atc3 branch from a7a3a04 to f1c81fe Nov 28, 2016
@glennw
Copy link
Member Author

glennw commented Nov 28, 2016

@bors-servo r=pcwalton

@bors-servo
Copy link
Contributor

bors-servo commented Nov 28, 2016

📌 Commit f1c81fe has been approved by pcwalton

@bors-servo
Copy link
Contributor

bors-servo commented Nov 28, 2016

Testing commit f1c81fe with merge 3e911b1...

bors-servo added a commit that referenced this pull request Nov 28, 2016
Introduce a glyph cache thread.

This is the first step towards making the glyph cache more efficient
and also run asynchronously to allow rendering to continue if the
glyph rasterizing becomes a bottleneck (e.g. during animated zooms).

Previously, we collected the list of glyphs that were required for
a frame, and ran a rayon loop to rasterize any that weren't in the
glyph cache. However, this meant that the glyph rasterizion could
not start until we knew the list of all required glyphs.

Now, all requests for text run glyphs are sent immediately to the
glyph cache thread. The glyph cache thread uses a worker thread
pool to immediately begin rasterizing any uncached glyphs as soon
as they are requested.

In the future, the idea is that instead of waiting at the block()
method for all pending glyphs to be rasterized, the backend thread
will instead receive small numbers of glyphs as they are rasterized.
This will allow the backend thread to make a decision that glyph
rasterizing is taking too long for this frame, and that we should
just draw a frame with the currently cached glyph sizes available.
When this occurs, the glyph cache thread will signal that a new frame
should be rendered when it completes rasterizing any pending glyphs.
This will allow us to keep rendering at 60fps during zoom, even
if the glyph rasterizing threads can't keep up (at a quality
tradeoff of using lower resolution glyphs).

<!-- 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/565)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented Nov 28, 2016

☀️ Test successful - status-travis

@bors-servo bors-servo merged commit f1c81fe into servo:master Nov 28, 2016
2 checks passed
2 checks passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
@glennw glennw deleted the glennw:atc3 branch Nov 28, 2016
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

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