Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upHandle external image life time in api.delete_image() call #636
Conversation
| @@ -1451,7 +1472,17 @@ pub struct ExternalImage { | |||
| /// Interface that an application can implement | |||
| /// to support providing external image buffers. | |||
| pub trait ExternalImageHandler { | |||
| // Get the associate external image from the id. It also implies that the | |||
| // image is held by WR. The WR client will keep the life time for this | |||
| // image until the release() call. | |||
This comment has been minimized.
This comment has been minimized.
|
I'm not convinced this handling needs to be integrated into WR. Can't we move this responsibility to the user by doing the following scheme:
In this scheme, the only APIs that WR needs to provide are:
@glennw @JerryShih does it make sense? |
|
Looks good, just a few minor issues. |
| } | ||
| ResultMsg::NewFrame(frame, profile_counters) => { | ||
| // When a new frame is ready, we could start to update all pending external | ||
| // image request here. | ||
| if !self.pending_external_image_updates.is_empty() { |
This comment has been minimized.
This comment has been minimized.
glennw
Dec 15, 2016
Member
I think it's probably more "correct" to move this loop to the end of the render() call.
This comment has been minimized.
This comment has been minimized.
JerryShih
Dec 15, 2016
Author
Contributor
@glennw
We could release the unused external image asap here. These external images will not be used because we set a new frame. If we move the deletion to the end of render() call, we just extend the life-time of these images.
What do you think for this reason?
This comment has been minimized.
This comment has been minimized.
glennw
Dec 15, 2016
Member
I think what could happen is this sequence:
dl_builder.add_image(ext_image);
api.add_dl(dl_builder).
api.delete_image(blah)
In this case, it seems possible to me that you could have an image in the current frame's display list, which is also in the pending release list? That's why it seems safer to me to do it at the end of render(). The update() and render() are called directly after each other, so I don't think it will have any major impact on extra lifetimes. Does that seem possible to you? (I might be over-thinking it).
This comment has been minimized.
This comment has been minimized.
JerryShih
Dec 15, 2016
Author
Contributor
api.add_image(A)
dl.push_image() with A
set_root_dl(frame 1)
update()
render() <= the frame 1 is not ready, draw frame 0 instead
api.delete_image(A)
api.add_image(B)
dl.push_image with B
update()
render() <= the frame 1 is ready now, draw with A
So, we the image A will still be used until frame 2 ready. That's why I put the release() call when we receives the new frame.
This comment has been minimized.
This comment has been minimized.
JerryShih
Dec 15, 2016
Author
Contributor
dl_builder.add_image(ext_image);
api.add_dl(dl_builder).
api.delete_image(blah)
If we have this usage, it will have a panic in block_until_all_resources_added(). So, that problem is not related to this patch.
| let handler = self.external_image_handler | ||
| .as_mut() | ||
| .expect("Found external image updates, but no handler set!"); | ||
| let mut pending_external_image_updates = mem::replace(&mut self.pending_external_image_updates, Vec::new()); |
This comment has been minimized.
This comment has been minimized.
glennw
Dec 15, 2016
Member
This line shouldn't be necessary - the loop could just be self.pending_external_image_updates.drain(..) I think.
This comment has been minimized.
This comment has been minimized.
| let value = self.image_templates.remove(&image_key); | ||
|
|
||
| // If the key is associate to an external image, pass the external id to render for cleanup. | ||
| if let Some(image) = value { |
This comment has been minimized.
This comment has been minimized.
glennw
Dec 15, 2016
Member
Let's print a error if image is not Some(..) here as well, since that indicates a bug (such as double deletion of an image key).
This comment has been minimized.
This comment has been minimized.
If we call Render::update() and Render::render() multiple times without update the display_list, when should we release the resources? The user will receive several callback with the same frame id. |
|
@kvark I think it's probably fine to have this code in WR. Even though it's probably possible for the user to handle it externally, it seems like we could make it a safer interface by managing this internally. Thoughts? |
|
@glennw |
|
@kvark , could you please review my updates? Glenn is on PTO. |
Right, but this is easily workable. You could free the resources associated with frame N when you get a callback of frame N+1.
Will make sure to review today.
It's not obvious to me. The external image stuff is not allocated/owned by WR, so we should not be responsible for freeing it either. The less API surface is the better for me. How do you see this being a part of WR to be potentially safer? |
| @@ -344,6 +344,22 @@ pub enum TextureUpdateOp { | |||
| Grow(u32, u32, ImageFormat, TextureFilter, RenderTargetMode), | |||
| } | |||
|
|
|||
| pub struct ExternalImageUpdateList { | |||
This comment has been minimized.
This comment has been minimized.
kvark
Dec 15, 2016
Member
Are you planning to extend this struct? Otherwise, it could be turned into an alias (given that it only has one field and it's public, and no actual logic in the methods):
pub type ExternalImageUpdateList = Vec<ExternalImageId>;
This comment has been minimized.
This comment has been minimized.
| @@ -1409,7 +1423,7 @@ impl Renderer { | |||
| &projection); | |||
| } | |||
|
|
|||
| self.release_external_textures(); | |||
| self.unlock_external_images(); | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Please squash it as well. |
The removing condition will become complicated. You should remember the frame id for each external image. Then, you also need to have a flag to check the external image is be called with api.delete_image(). We should not delete an external image that is still exist in resource cache.
I will update a squashed version later. |
9df394d
to
e2bfc7e
| @@ -1263,7 +1265,7 @@ impl Renderer { | |||
| let props = &deferred_resolve.image_properties; | |||
| let external_id = props.external_id | |||
| .expect("BUG: Deferred resolves must be external images!"); | |||
| let image = handler.get(external_id); | |||
| let image = handler.lock(external_id); | |||
This comment has been minimized.
This comment has been minimized.
| self.pending_texture_updates.push(texture_update_list); | ||
|
|
||
| // When a new frame is ready, we could start to update all pending external image requests here. | ||
| self.release_external_images(external_image_update_list); |
This comment has been minimized.
This comment has been minimized.
JerryShih
Dec 16, 2016
Author
Contributor
Only release the external images used in previous frames when a new frame is ready.
|
@kvark , the updated patch is ready. |
Yes, that's what I meant originally:
I don't see which safety guarantees WR can impose on the external resources anyway, so I don't see a reason to complicate WR with an attempt to share any responsibility. The user should be fully responsible for the lifetime of those, and all information it needs to do so is just that frame ID. So, if the Renderer is done with frame N+1 (or later), the caller can free the resources associated with N. WR's delete_image() for the external resources would just clean up internal structures and not do anything dangerous by itself. I realize that my suggestion is a little hand-wavy, but I believe we should consider this as a way of minimal API to support the externally owned objects. Looking forward to hear what @glennw thinks about the possible safety extras we can get from a fatter API. |
|
@kvark I think you're quite right - this could be handled on the client side with a smaller API. I don't think having this API necessarily makes anything safer (that was bad wording on my part). But I do think if we can handle these details internally, it makes it easier for other clients to use the API correctly and safely (i.e. having the code to manage this in one place in WR is probably better than Servo, Gecko and other API clients having to correctly implement the semantics above). Thoughts? |
|
On the gecko side we're about to make some substantial changes how the different WebRender parts are organized and threads, so some of the assumptions that this PR has been designed against will probably change. While it's good to move forward with this and start iterating, I don't think we need to get all of details right just now. We can come back on how much is handled inside or outside of WebRender as the moving pieces come together. |
|
Given what @nical said, It looks like a classical textbook case for a fork, given that you need to play with API/internals and you don't know |
|
|
|
@kvark @glennw @nical |
|
I'm fine with API experiments happening here in upstream - so I'm happy to either merge this (once rebased and review is finished) or leave it until things settle down more - whatever will make it easiest for you. |
Now, the ExternalImageHandler has lock(), unlock() and release() callback functions. Use lock() instead of get() callback in WR render update_deferred_resolves().
Release the external images when a new frame is ready. Merge ResultMsg::UpdateTextureData and NewFrame::NewFrame message into one message.
e2bfc7e
to
ba33b43
|
@bors-servo r=glennw |
|
|
|
|
Handle external image life time in api.delete_image() call Currently, the delete_image() call just removes the image_key in the hash_map. If the key is associate to an external image, we should release that image using the ExternalImageHandler callbacks. Since the ExternalImageHandler callbacks are only used in renderer, a pending releasing array is used. Then, pass this array to the renderer for ExternalImageHandler::release() call. @nical @glennw <!-- 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/636) <!-- Reviewable:end -->
JerryShih commentedDec 14, 2016
•
edited by larsbergstrom
Currently, the delete_image() call just removes the image_key in the hash_map. If the key is associate to an external image, we should release that image using the ExternalImageHandler callbacks.
Since the ExternalImageHandler callbacks are only used in renderer, a pending releasing array is used. Then, pass this array to the renderer for ExternalImageHandler::release() call.
@nical @glennw
This change is