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

Handle big size image #1564

Merged
merged 3 commits into from
Oct 16, 2017
Merged

Conversation

JerryShih
Copy link
Contributor

@JerryShih JerryShih commented Aug 9, 2017

@nical @glennw @kvark @sotaroikeda
r?

This patch try to handle the #1405.
If we hit the maximum texture size, turn to use a 1x1 rgba(255,255,255,255) dummy texture.


This change is Reviewable

@glennw
Copy link
Member

glennw commented Aug 9, 2017

I'm working on a large refactoring of the texture cache at the moment - if possible, I'd like to hold off on merging this until that's done. Is that going to be OK? (I would expect it to be done early next week). Handling this case should be much simpler with the changes I'm working on, too.

@bors-servo
Copy link
Contributor

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

@JerryShih
Copy link
Contributor Author

@glennw
That's fine. If the refactor is done, please show the note in this thread.

@glennw
Copy link
Member

glennw commented Aug 17, 2017

@JerryShih The texture cache refactor has landed now. Instead of trying to use a dummy texture for this case, could we just not add the item to any batches when we find an invalid texture?

@JerryShih
Copy link
Contributor Author

@JerryShih The texture cache refactor has landed now. Instead of trying to use a dummy texture for this case, could we just not add the item to any batches when we find an invalid texture?

@glennw
That could be another approach. I think I will still use a special id for the big size allocation(or failed allocation). Then, we could skip the item with that id in batch list or draw with a special color texture for debugging(with a 1x1 black dummy texture).

@glennw
Copy link
Member

glennw commented Aug 17, 2017

I think the simplest approach will be to early exit from request_image in resource cache, before even getting to texture cache. Then, get_cached_image could just return SourceTexture::Invalid. But let's see how this looks after rebasing to the new texture cache and decide from there :)

@JerryShih JerryShih force-pushed the handle-big-size-image branch 2 times, most recently from 56d88b5 to d58c2ff Compare August 17, 2017 09:43
@JerryShih
Copy link
Contributor Author

@glennw r?

And I need the help for the wrench reftest. I got the different result from the standalone yaml replay and the reftest result.

standalone:
I could see a black Rect. That's what I want.
cargo run show reftests/image/very-big-tile-size.yaml

reftest:
I got a gray Rect.
./headless.py reftest reftests/image/very-big-tile-size.yaml

I don't know the difference between them.

@bors-servo
Copy link
Contributor

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

@JerryShih JerryShih force-pushed the handle-big-size-image branch 3 times, most recently from 45c30a8 to 09a5c21 Compare August 18, 2017 03:35
@JerryShih
Copy link
Contributor Author

@glennw r? again.
rebase.
I still hit the reftest problem with wrench. Maybe you have some hints for this problem.

@glennw
Copy link
Member

glennw commented Aug 18, 2017

@JerryShih I'm not really sure why it would be different between standalone and reftest. Perhaps the wrong texture is getting bound or something like that?

I wrote up a prototype of what I was discussing above here glennw@9ca1214

I think if we use a solution like this, where it doesn't involve the texture cache at all, the code is quite a bit simpler. And when we want to display the bad texture, we could do that as a follow up in the renderer (handle the case where a texture resolves to SourceTexture::Invalid during batching). What do you think?

@JerryShih
Copy link
Contributor Author

@JerryShih I'm not really sure why it would be different between standalone and reftest. Perhaps the wrong texture is getting bound or something like that?

I wrote up a prototype of what I was discussing above here glennw/webrender@9ca1214

I think if we use a solution like this, where it doesn't involve the texture cache at all, the code is quite a bit simpler. And when we want to display the bad texture, we could do that as a follow up in the renderer (handle the case where a texture resolves to SourceTexture::Invalid during batching). What do you think?

Your prototype is much simpler than me. So, yours is a better solution. I add a comment in glennw/webrender@9ca1214. I will create another follow up if we need to show the skipped image.

@glennw
Copy link
Member

glennw commented Aug 18, 2017

@JerryShih Sounds good, thanks! Do you want me to follow up your comments and open a PR, or should I leave that for you?

@JerryShih
Copy link
Contributor Author

@glennw
I would like to do that!

@JerryShih
Copy link
Contributor Author

@glennw has a better solution. Close this pr.

@JerryShih JerryShih closed this Aug 18, 2017
@JerryShih JerryShih reopened this Sep 5, 2017
@JerryShih JerryShih force-pushed the handle-big-size-image branch from 09a5c21 to 152401b Compare September 5, 2017 09:14
@JerryShih
Copy link
Contributor Author

@glennw @kvark r? again.
This version just skip the image request and return a invalid CacheItem for the big size image request.

// just early out and drop the image.
if tile_size as u32 > self.texture_cache.max_texture_size() {
warn!("Dropping image, tile size:{} is too big for hardware!", tile_size);
return;
Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit uncomfortable with these return statements as they are different from other return code paths from this method in that they are failures, while others are successes.
How about we return true/false (at least), and then also modify prepare_prim_for_render to return Option<Metadata> so that a primitive that failed to prepare can be discarded in time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kvark
I'm not familiar with the frame_builder::build process. The prepare_prim_for_render() is called by frame_builder::handle_primitive_run()[1]. And the handle_primitive_run() looks like to handle the mask and clip related thing. Are you thinking about skipping the render-task creation in handle_primitive_run() for the case of None Option return value?

[1]
https://dxr.mozilla.org/mozilla-central/rev/c959327c6b75cd4930a6ea087583c38b805e7524/gfx/webrender/src/frame_builder.rs#1996

Copy link
Member

Choose a reason for hiding this comment

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

Yes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The prepare_prim_for_render() is changed. The "Metadata" can't be remove now.

From the first comment:

I'm a bit uncomfortable with these return statements as they are different from other return code paths from this method in that they are failures, while others are successes.

We still have a case with a failure at [1] in request_image(). So, the new return statements are not just the new failed case. If they are still not looked good, I need to check the prepare_prim_for_render() for the "Metadata" removing.

[1]

debug_assert!(texture_id == None ||
texture_id == Some(cache_item.texture_id));
texture_id = Some(cache_item.texture_id);
if let Some(glyph) = glyph_key_cache.get(key) {
Copy link
Member

Choose a reason for hiding this comment

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

We need to handle the case of failure here. Does it make sense to return as usual if some of the glyphs failed to be obtained?

//
// TODO(Jerry): add a debug option to fill the corresponding area for
// that invalid CacheItem.
image_info.map_or(CacheItem::invalid(), |image_info| {
Copy link
Member

Choose a reason for hiding this comment

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

we call get_glyphs when populating batches. Wouldn't it be simpler to just return an error here, so that a corresponding instance can be discarded?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We call get_glyphs at [1]. If there is any error inside get_glyphs()[2], the lambda expression at [3] will not be called(which will push the instance to draw list).

[1]

ctx.resource_cache.fetch_glyphs(

[2]
https://github.com/servo/webrender/pull/1564/files#diff-77cbdf7ba9ebae81feb38a64c21b8454R630
[3]
|texture_id, glyphs| {

@bors-servo
Copy link
Contributor

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

@glennw
Copy link
Member

glennw commented Oct 2, 2017

@JerryShih @kvark What's the status of this?

@JerryShih
Copy link
Contributor Author

@glennw I'm check an animation artifact problem with WR. I will go back to finish this soon.

@JerryShih JerryShih force-pushed the handle-big-size-image branch from 152401b to 58a136f Compare October 11, 2017 09:05
@@ -813,6 +817,11 @@ impl ClipBatcher {
ClipSource::Image(ref mask) => {
let cache_item =
resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
// Skip the invalid image mask.
if cache_item.texture_id == SourceTexture::Invalid {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This image mask will be rendered at [1]. So, I think we should skip this image if the texture id is not valid.

[1]

for (mask_texture_id, items) in target.clip_batcher.images.iter() {

@JerryShih
Copy link
Contributor Author

@glennw @kvark r?

Copy link
Member

@kvark kvark left a comment

Choose a reason for hiding this comment

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

Thanks for updating the PR!
I think my only concern is that we still need the key to be present in the resources map, while turning value into Result.

self.resources
.get(key)
.expect("Didn't find a cached resource with that ID!")
/// In some cases, we can't handle the resource request(e.g. the user
Copy link
Member

Choose a reason for hiding this comment

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

I think we should still differentiate between keys that didn't end up in the map for some other logical reasons, and keys that were just failing to be added. Perhaps, the resources map should contain Result<V> instead of V, so that we can handle those failures in a more explicit way?

@JerryShih JerryShih force-pushed the handle-big-size-image branch from 58a136f to 867da17 Compare October 12, 2017 12:50
@@ -1057,7 +1057,6 @@ impl PrimitiveStore {
}
}

/// Returns true if the bounding box needs to be updated.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This comment is not correct now.

pub struct ResourceClassCache<K, V> {
resources: FastHashMap<K, V>,
resources: FastHashMap<K, ResourceCacheCacheResult<V>>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Use Result instead of V.

// just early out and drop the image.
if tile_size as u32 > self.texture_cache.max_texture_size() {
warn!("Dropping image, tile size:{} is too big for hardware!", tile_size);
self.cached_images.insert(request, Err(ResourceClassCacheError::OverLimitSize));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we hit the size limitation, just insert an Err() entry.

@JerryShih
Copy link
Contributor Author

@kvark r?
Try to use Result in ResourceClassCache.

@JerryShih
Copy link
Contributor Author

I haven't handled the Err() case for each get/get_mut() call, but I will just add some logs for these paths if we need to have these logs.

Copy link
Member

@kvark kvark left a comment

Choose a reason for hiding this comment

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

Thanks @JerryShih ! A few more things to fix before we proceed.

OverLimitSize,
}

pub type ResourceCacheCacheResult<V> = Result<V, ResourceClassCacheError>;
Copy link
Member

Choose a reason for hiding this comment

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

perhaps you meant ResourceClassCacheResult?

@@ -159,7 +174,7 @@ where
.cloned()
.collect::<Vec<_>>();
for key in resources_to_destroy {
self.resources.remove(&key).unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

why is this removed? I assume we get a warning about unused Result or Option

}
} else {
// The image is too big for hardware texture size.
if template.descriptor.width > self.texture_cache.max_texture_size() ||
Copy link
Member

Choose a reason for hiding this comment

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

should probably merge this block with the one above, using let side_size = image.tiling.unwrap_or(cmp::max(template.descriptor.width, template.descriptor.height));

// If this image exists in the texture cache, *and* the epoch
// in the cache matches that of the template, then it is
// valid to use as-is.
let (entry, needs_update) = match self.cached_images.entry(request) {
Occupied(entry) => {
let needs_update = entry.get().epoch != template.epoch;
let needs_update = entry.get().as_ref().ok().unwrap().epoch != template.epoch;
Copy link
Member

Choose a reason for hiding this comment

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

no need for ok() here

true,
),
};

let needs_upload = self.texture_cache
.request(&mut entry.texture_cache_handle, gpu_cache);
.request(&mut entry.as_mut().ok().unwrap().texture_cache_handle, gpu_cache);
Copy link
Member

Choose a reason for hiding this comment

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

same here - no need for ok()

@@ -656,7 +692,15 @@ impl ResourceCache {
tile,
};
let image_info = &self.cached_images.get(&key);
Copy link
Member

Choose a reason for hiding this comment

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

would probably be cleaner with a simple match statement

@@ -816,7 +860,7 @@ impl ResourceCache {
image_template.descriptor.clone()
};

let entry = self.cached_images.get_mut(&request).unwrap();
let entry = self.cached_images.get_mut(&request).as_mut().ok().unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

no need for ok()

@@ -516,6 +517,8 @@ impl AlphaRenderItem {
glyph_fetch_buffer,
gpu_cache,
|texture_id, glyphs| {
debug_assert!(texture_id != SourceTexture::Invalid);
Copy link
Member

Choose a reason for hiding this comment

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

nit: debug_assert_ne!()

//
// TODO(Jerry): add a debug option to fill the corresponding area for
// that invalid CacheItem.
image_info.as_ref().ok().map_or(CacheItem::invalid(), |image_info| {
Copy link
Member

Choose a reason for hiding this comment

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

let's just return Result<CacheItem, _> from this function and match it on the other side, there is no need for nullable CacheItem semantic (that invalid() does here)

@JerryShih JerryShih force-pushed the handle-big-size-image branch from 867da17 to d68cf51 Compare October 13, 2017 08:06
@@ -174,7 +167,7 @@ where
.cloned()
.collect::<Vec<_>>();
for key in resources_to_destroy {
self.resources.remove(&key);
self.resources.remove(&key).unwrap();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I revert the change, but the CI test is failed.

error: unused std::result::Result which must be used
--> src/resource_cache.rs:170:13
|
170 | self.resources.remove(&key).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: -D unused-must-use implied by -D warnings

@JerryShih
Copy link
Contributor Author

@kvark r? for d68cf51

@kvark
Copy link
Member

kvark commented Oct 13, 2017

Thank you for addressing my comments!

self.resources.remove(&key).unwrap();

Let's do let _ = self.resources.remove(&key).unwrap();

Please squash the commits together, and we can merge once CI is happy.

@JerryShih JerryShih force-pushed the handle-big-size-image branch from d68cf51 to f903161 Compare October 16, 2017 06:04
…it image request.

WR can't handle the image request if it's bigger than the hardware limit. Please check texture_cache::max_texture_size().

Skip the image request for a big size image in resource_cache::request_image().
Return an Err() for a big size image in resource_cache::get_cached_image().
@JerryShih
Copy link
Contributor Author

@kvark
Update for CI "self.resources.remove(&key).unwrap();" failed, and squash the patch set.

@kvark
Copy link
Member

kvark commented Oct 16, 2017

@JerryShih awesome, :shipit: ! 👍
@bors-servo r+

@bors-servo
Copy link
Contributor

📌 Commit f903161 has been approved by kvark

@bors-servo
Copy link
Contributor

⌛ Testing commit f903161 with merge e4cca26...

bors-servo pushed a commit that referenced this pull request Oct 16, 2017
Handle big size image

@nical @glennw @kvark @sotaroikeda
r?

This patch try to handle the #1405.
If we hit the maximum texture size, turn to use a 1x1 rgba(255,255,255,255) dummy texture.

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants