-
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
Add an API for providing external images. #561
Conversation
How does this look as an API? |
Also CC @jrmuizel |
How about the external "gl texture"? Here is my patch to resolve the external texture using the callback function: |
@JerryShih Added basic support for using a native texture as an external image. I haven't added support for the release callback of native textures yet (it should work for cpu external images though). If we're happy in general with the API, I think it's fine to get this merged, and I'll do the native texture release code in a follow up PR. |
The semantics of the get/release calls are a bit confusing probably. Perhaps there should be a separate callback to just get the timestamp instead of calling get() each frame to check if the timestamp has changed? Thoughts? |
@glennw |
@JerryShih The reason for the timestamp was based on a discussion with @nical. He suggested that we'd want to be able to just invoke the render thread to re-draw the frame in the case of a video where nothing else in the layout has changed, but the video is advancing (for performance and power reasons). If we go via the update_image() API, that involves waking up the backend thread, processing the frame and pushing that to the render thread. Personally, I'm happy with either solution - but hopefully that explains the logic of having the timestamp field, and we can discuss here the preferred option. |
The timestamped video frames logic lives in the gecko/compositor side. I don't think we need to expose it to webrender, as long as the latter invokes the the callback every frame. The gecko implementation of the callback can query the composition timestamp from the Compositor and select the texture accordingly. As far as gecko is concerned we don't want to use update_image() for video frames because it wakes up the backend thread and rebuilds the frame, which we don't want to happen every frame during video playback unless the geometry actually changed. We only want to swap out the the texture id and uv coordinates and submit the draw calls again. Edit: to be clear, with video we don't want to call add_image every frame, but we can use it in the first frame to to create the video source, passing it an external id instead of actual image data. Then, every time we render a new frame, the callback resolves that external id (which does not change) into a texture id. |
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.
Got a few questions, but it looks good.
@@ -55,6 +55,7 @@ uniform sampler2D sData16; | |||
uniform sampler2D sData32; | |||
uniform sampler2D sData64; | |||
uniform sampler2D sData128; | |||
uniform sampler2D sResourceRects; |
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.
why does it have to be a separate texture instead of re-using sData16?
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.
In the future, I expect this will be an array managed by the texture cache on a thread that is running async to the backend thread - so I've made it a separate array because of that.
@@ -16,6 +16,39 @@ use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering}; | |||
use webrender_traits::{FontRenderMode, WebGLContextId}; | |||
use webrender_traits::{ClipRegion, FontKey, ItemRange, ComplexClipRegion, GlyphKey}; | |||
|
|||
/// Stores two coordinates in texel space. The coordinates | |||
/// are stored in texel coordinates because the texture atlas | |||
/// may grow. Storing them as texel coords and normalizing |
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.
nit: if the atlas grows, means it's re-allocated and all the contents need to be copied over anyways, in which case we might want to re-arrange the placement of textures inside it, meaning that UV rects change anyways.
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 now, we don't do that - we resize via FBO. But in the future that's possible, yes.
} | ||
ImagePrimitiveKind::WebGL(context_id) => { | ||
resource_cache.get_webgl_texture(&context_id) | ||
let cache_item = resource_cache.get_webgl_texture(&context_id); | ||
let resource_rect = self.gpu_resource_rects.get_mut(image_cpu.resource_address); |
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.
nit: given that these 3 lines are copy-pasted from above, we might want to return Option<CacheItem>
from the match and do them afterwards
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.
Fixed
@@ -747,7 +805,7 @@ impl PrimitiveStore { | |||
debug_assert!(metadata.gpu_data_count == text.glyph_range.length as i32); | |||
debug_assert!(text.glyph_indices.is_empty()); | |||
let src_glyphs = auxiliary_lists.glyph_instances(&text.glyph_range); | |||
let dest_glyphs = self.gpu_data32.get_slice_mut(metadata.gpu_data_address, | |||
let dest_glyphs = self.gpu_data16.get_slice_mut(metadata.gpu_data_address, |
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.
future: in order to catch this kind of errors later, we might consider templating the GpuStoreAddress<T>
to only work with GpuStore<T>
ApiMsg::AddImage(id, width, height, stride, format, data) => { | ||
match data { | ||
ImageData::Raw(ref bytes) => { | ||
profile_counters.image_templates.inc(bytes.len()); |
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.
nit: perhaps do if let
instead
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.
Fixed
let texture_id = match image.data { | ||
ExternalImageData::Buffer(..) => { | ||
// For a custom user buffer, allocate a native texture. | ||
let texture_id = self.device |
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.
when is this texture deleted, if ever?
@@ -1267,6 +1299,100 @@ impl Renderer { | |||
fn draw_tile_frame(&mut self, | |||
frame: &mut Frame, | |||
framebuffer_size: &Size2D<u32>) { | |||
// The first thing we do is run through any pending deferred |
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.
voting to move this out into a separate method. You'd be able to do return
from there if the timestamps match, simplifying the logic a bit
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.
Fixed
@@ -1394,6 +1522,36 @@ impl Renderer { | |||
} | |||
} | |||
|
|||
pub enum ExternalImageData { |
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.
ExternalImageData::Native
doesn't sound right to me. Perhaps, ExternalImageSource
? with RawData
and NativeTexture
variants
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.
Fixed
Occupied(entry) => { | ||
let image_id = entry.get().texture_cache_id; | ||
|
||
if entry.get().epoch != image_template.epoch { |
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.
is the semantic of timespamps (above) different from the epoch
here?
.as_mut() | ||
.expect("Found external image, but no handler set!"); | ||
|
||
for deferred_resolve in &frame.deferred_resolves { |
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.
is this guaranteed to run once only? and if not, will it behave properly for multiple invocations (scanning the deferred_resolves
list)?
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.
It can run once per scroll, but this is expected behaviour.
☔ The latest upstream changes (presumably #556) made this pull request unmergeable. Please resolve the merge conflicts. |
@nical
For external "buffer":
I think we still need to have a function to notify WR that here comes a new image. Then, WR could do the texture uploading if the image is an "external buffer". Could update_image() only update the TextureSource, TextureId in the frame? It will still wake up the backend thread as other rendering api does, but just update the TextureSource or TextureId with for the new external key. Frame structure building should be skip in this case. |
373a7a9
to
003dbeb
Compare
Rebased over recent changes and addressed most of the review comments. I think @nical mentioned that a first test of this case would be using native textures? So I have removed the external buffer (and timestamp related code) until we work out a suitable API for them. Hopefully that means we can land this part of the external image support (once any further API changes / review comments are sorted), which will give us external texture support, and then work on the external buffer support as a follow up API. It now requests the external texture ID at the start of the frame, and releases after issuing all the draw calls. The calling application doesn't have to free the texture at that point (e.g. it may know it's going to use it again next frame), but it knows that WR will not issue another draw call referencing that texture without calling get() again first. |
@JerryShih @nical @kvark How does the above sound? |
I pushed a simple test case / sample of using the external image function to draw an image which is the output of an FBO clear - glennw@481a6b1 |
Sounds good to me. @JerryShih perhaps I misunderstand what you mean by update_image(). This API currently just sends a message to the backend thread which rebuilds the frame with the updated information. For externally allocated textures all we need to happen is on the renderer thread, so it would make sense to have a separate API which interacts with the renderer directly rather than the backend. |
r=me, squash if you want |
003dbeb
to
aa8aafc
Compare
@bors-servo r=pcwalton |
📌 Commit 003dbeb has been approved by |
@bors-servo clean retry |
@bors-servo force clean retry |
This allows applications to provide images without supplying the image bytes up front. This can be useful if the application doesn't have the bytes available immediately (e.g. video decoding) or wants to manage the buffer allocation and reduce memory copies.
aa8aafc
to
afb0f06
Compare
@bors-servo r=pcwalton |
📌 Commit afb0f06 has been approved by |
Add an API for providing external images. This allows applications to provide images without supplying the image bytes up front. This can be useful if the application doesn't have the bytes available immediately (e.g. video decoding) or wants to manage the buffer allocation and reduce memory copies. <!-- 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/561) <!-- Reviewable:end -->
☀️ Test successful - status-travis |
kvark has make several important contributions to webrender, both as a reviewer and committer. He also has significant Rust experience previously as the author of gfx-rs. Some examples of recent reviews: servo/webrender#546 servo/webrender#523 servo/webrender#561 servo/webrender#554
[Proposal] Give @kvark reviewer privileges. kvark has make several important contributions to webrender, both as a reviewer and committer. He also has significant Rust experience previously as the author of gfx-rs. Some examples of recent reviews: servo/webrender#546 servo/webrender#523 servo/webrender#561 servo/webrender#554 <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/saltfs/551) <!-- Reviewable:end -->
kvark has make several important contributions to webrender, both as a reviewer and committer. He also has significant Rust experience previously as the author of gfx-rs. Some examples of recent reviews: servo/webrender#546 servo/webrender#523 servo/webrender#561 servo/webrender#554
kvark has make several important contributions to webrender, both as a reviewer and committer. He also has significant Rust experience previously as the author of gfx-rs. Some examples of recent reviews: servo/webrender#546 servo/webrender#523 servo/webrender#561 servo/webrender#554
This allows applications to provide images without supplying
the image bytes up front. This can be useful if the application
doesn't have the bytes available immediately (e.g. video decoding)
or wants to manage the buffer allocation and reduce memory
copies.
This change is