[Spice-devel] [PATCH v5 09/20] server: Avoid copying the input frame in the GStreamer encoder.
Francois Gouget
fgouget at codeweavers.com
Tue Sep 29 04:01:21 PDT 2015
I attached the new patch to give an idea of what things look like now.
On Tue, 22 Sep 2015, Christophe Fergeau wrote:
[...]
> > + SpiceChunks *chunks = bitmap->data;
> > + while (*chunk_offset >= chunks->chunk[*chunk].len) {
> > + /* Make sure that the chunk is not padded */
> > + if (chunks->chunk[*chunk].len % bitmap->stride != 0) {
> > + }
>
> Empty block?
Fixed. I introduced this bug when moving the code to the zero_copy()
function.
> > + int max_mem = gst_buffer_get_max_memory();
> > + if (chunks->num_chunks - *chunk > max_mem) {
> > + /* There are more chunks than we can fit memory objects in a
> > + * buffer. This will cause the buffer to merge memory objects to
> > + * fit the extra chunks, which means doing wasteful memory copies.
> > + * So use the zero-copy approach for the first max_mem-1 chunks,
> > + * and directly copy the remainder to the last memory object.
> > + */
>
> The copy of the remainder is done in push_raw_frame if I followed the
> code correctly?
Yes. I tweaked the comment to make that more explicit.
> > + max_mem = *chunk + max_mem - 1;
>
> max_mem = max_mem - skipped_chunk_count - 1; ?
No.
Let's say there are 7 chunks, that we skip the first two, and that we
can put 4 GstMemory objects a GstBuffer.
chunk 0 -> zero_copy() skip
chunk 1 -> zero_copy() skip
Now chunk == 2, max_mem == 4 and what we want to know is when to exit
the zero-copy loop:
while (*len && *chunk < max_mem) {
And that's when *chunk == 2 + 4 -1
chunk 2 -> zero_copy() wrap it in a GstMemory
chunk 3 -> zero_copy() wrap it in a GstMemory
chunk 4 -> zero_copy() wrap it in a GstMemory
Now we return to push_raw_frame() when chunk is now 5 so it will pick up
where we left off and copy the rest in a single GstMemory object.
chunk 5 -> push_raw_frame() copy to the last GstMemory
chunk 6 -> push_raw_frame() copy to the last GstMemory
chunk 7 -> push_raw_frame() copy to the last GstMemory
> > + } else {
> > + max_mem = chunks->num_chunks;
> > + }
> > +
> > + while (*len && *chunk < max_mem) {
>
> and here 'chunk' becomes a loop index initialized at
> skipped_chunk_count, maybe call it 'i' ? or make it a SpiceChunk *
> initialized to chunks->chunk[skipped_chunk_count], this would make the
> loop body a bit easier to read.
It is a loop index all along. Also we need to compare it to max_mem so
keeping it as an integer is clearer. I've renamed it to chunk_index
though.
[...]
> > @@ -405,15 +484,14 @@ static int push_raw_frame(GstEncoder *encoder, const SpiceBitmap *bitmap,
> > const uint32_t height = src->bottom - src->top;
> > const uint32_t stream_stride = (src->right - src->left) * encoder->format->bpp / 8;
> > uint32_t len = stream_stride * height;
> > - GstBuffer *buffer = gst_buffer_new_and_alloc(len);
> > #ifdef HAVE_GSTREAMER_0_10
> > + GstBuffer *buffer = gst_buffer_new_and_alloc(len);
> > uint8_t *b = GST_BUFFER_DATA(buffer);
> > + uint8_t *dst = b;
>
> Seems like the declaration of uint8_t *dst = GST_BUFFER_DATA(buffer);
> could also be moved close to the 'gst_allocator_alloc()' calls in an
> inner block.
It's used in two sibling blocks so this would requires duplicating that
line and some #if check. Not really simpler.
[...]
> > + GstMapInfo map = { .memory = NULL };
>
> Could be GST_MAP_INFO_INIT
I didn't know about it. However it turns out it was buggy until
recently. So I added a TODO.
https://bugzilla.gnome.org/show_bug.cgi?id=751881
[...]
> > +#ifndef HAVE_GSTREAMER_0_10
> > + GstMemory *mem = gst_allocator_alloc(NULL, len, NULL);
> > + gst_memory_map(mem, &map, GST_MAP_WRITE);
>
> Apparently it's possible for gst_memory_map() to fail, would be nicer to
> check its return value
The GstMemory object is allocated with the specific intent of mapping
it. So if the map operation fails it's either because we're out of
memory or because of a coding error. So I added a spice_assert() on the
gst_memory_map() and code to check the allocation.
> > + uint8_t *b = map.data;
> > + uint8_t *dst = b;
> > +#endif
>
> Could gst_buffer_new_allocate() be used instead? I don't think the
> temporary 'b' variable is used at all apart for being assigned to 'dst'.
gst_buffer_new_allocate() allocates a new buffer which is not what we
want. Also note that we cannot use gst_buffer_append() as that would
cause a copy of all the memory objects of the first buffer, thus
defeating the purpose of the zero_copy code..
I removed the b variables.
> > chunk_offset += src->left * encoder->format->bpp / 8;
> > if (!line_copy(encoder, bitmap, chunk_offset, stream_stride, height, dst)) {
> > +#ifndef HAVE_GSTREAMER_0_10
> > + gst_memory_unmap(map.memory, &map);
> > + gst_memory_unref(map.memory);
> > +#endif
>
> Looks like some gst_buffer_unmap() should have been there before this
> commit.
Yep. Added.
--
Francois Gouget <fgouget at codeweavers.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 09-copying.diff
Type: text/x-diff
Size: 9702 bytes
Desc: 09-copying.diff
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20150929/c3e73899/attachment.diff>
More information about the Spice-devel
mailing list