-
Notifications
You must be signed in to change notification settings - Fork 279
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
Implement sub-image updates. #1003
Conversation
r? @glennw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One nit and a question about the purpose of the union() and how that works.
@@ -317,6 +320,11 @@ impl ResourceCache { | |||
data: ImageData::new(bytes), | |||
epoch: next_epoch, | |||
tiling: None, | |||
dirty_rect: match (dirty_rect, prev_dirty_rect) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand this part. I'm assuming the union() here is for the case where you update different parts of the same image multiple times in one frame? But then, the second update loses the bytes from the original update doesn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case I'm trying to support is updating an image several times (either several times in the same frame or across several frames if the image isn't requested (not in the viewport?)). In this situation the idea is to keep a dirty rectangle containing all dirty rectangles since the last time the texture_cache was updated.
I'm not sure what you mean by losing the bytes of the original update.
Edit: Ah! After talking with @kvark on irc I realize you probably meant by losing the bytes. The image data sent in update image still contains the whole image, the optional dirty rect only gives a hint that only a portion of the new image is different from the previous version.
So at any time the ImageData that we have can fully describe the whole image (only sending the damaged part would make things more complicated because the image might not be in the texture cache, in which case the dirty rect isn't useful since we need to upload the whole thing).
Same thing for blob images, the new blob is the whole "blob display list" for that image.
data: bytes, | ||
stride: descriptor.stride, | ||
offset: descriptor.offset, | ||
if let Some(dirty) = dirty_rect { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This if let
is better as a match
since there is an else
clause anyway.
@@ -147,8 +147,8 @@ impl RenderBackend { | |||
} | |||
self.resource_cache.add_image_template(id, descriptor, data, tiling); | |||
} | |||
ApiMsg::UpdateImage(id, descriptor, bytes) => { | |||
self.resource_cache.update_image_template(id, descriptor, bytes); | |||
ApiMsg::UpdateImage(id, descriptor, bytes, dirty_rect) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
naming nit: this is not a "dirty" rectangle, just the one to update. "dirty" brings on more semantics than the interface really provides.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can rename that updated_rect if you prefer.
stride: descriptor.stride, | ||
offset: descriptor.offset, | ||
if let Some(dirty) = dirty_rect { | ||
let stride = descriptor.compute_stride(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a bit confusing that we have both descriptor.stride
and descriptor.compute_stride()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you mean is that ImageDescriptor::stride being an option is confusing, and that's true (but it's convenient because in a lot of cases we don't have a stride larger than width * bpp
).
You can fix that by making the stride not optional, and computing it in a construction function if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right. It's not related to this PR though
@@ -317,6 +320,11 @@ impl ResourceCache { | |||
data: ImageData::new(bytes), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where does the old image data go? Looks like it gets lost
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old image data is replaced by the new one, yes. The underlying buffer is reference counted, not sure what you mean by lost.
offset: descriptor.offset, | ||
if let Some(dirty) = dirty_rect { | ||
let stride = descriptor.compute_stride(); | ||
let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotta double-check this line, but it depends on the answers to previous questions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After talking to @nical on IRC, the questions and details were clarified. My only concern would be the need to re-allocate the whole image on each sub-image update, which seems a bit wasteful.
Yes, I considered only passing the updated rect's worth of pixel data in the update message, but that makes the implementation a lot more complicated, and isn't necessarily more efficient because we currently save copies thanks to the image data being immutable and atomically ref counted. I'm not against doing it this way but I would prefer to have a compelling use case to motivate the extra complexity. |
@nical OK, that makes sense. Let's get this merged - but it would be good to change that |
@bors-servo r+ |
📌 Commit 4a6703d has been approved by |
Implement sub-image updates. Add an optional dirty rect to the update_image command (None means invalidate all), and only upload the required part of the image if the image was already in the texture cache. This will become more important when used with blob images since the blob image renderer will use this information to avoid repainting the entire image when possible. <!-- 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/1003) <!-- Reviewable:end -->
☀️ Test successful - status-travis |
Add an optional dirty rect to the update_image command (None means invalidate all), and only upload the required part of the image if the image was already in the texture cache. This will become more important when used with blob images since the blob image renderer will use this information to avoid repainting the entire image when possible.
This change is