Commit b25a8522 authored by Alexandre Courbot's avatar Alexandre Courbot Committed by Commit Bot

media/gpu/v4l2: add buffer affinity tracker

Add a small tracker that will always return the same V4L2 index when
given the same VideoFrame. This ensures that video buffers are always
assigned to the same V4L2 buffer, avoiding unmappings/remappings in the
kernel.

BUG=b:159688625
TEST=video.DecodeAccel.h264 passes on Kukui.

Change-Id: I4c439109891b0c02b607f2b238d21a4cca3a9c67
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2413945
Commit-Queue: Alexandre Courbot <acourbot@chromium.org>
Reviewed-by: default avatarFritz Koenig <frkoenig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813078}
parent 9f096e6b
...@@ -23,6 +23,8 @@ if (use_v4lplugin) { ...@@ -23,6 +23,8 @@ if (use_v4lplugin) {
source_set("v4l2") { source_set("v4l2") {
defines = [ "MEDIA_GPU_IMPLEMENTATION" ] defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
sources = [ sources = [
"buffer_affinity_tracker.cc",
"buffer_affinity_tracker.h",
"generic_v4l2_device.cc", "generic_v4l2_device.cc",
"generic_v4l2_device.h", "generic_v4l2_device.h",
"v4l2_decode_surface.cc", "v4l2_decode_surface.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/v4l2/buffer_affinity_tracker.h"
#include "base/synchronization/lock.h"
#include "media/gpu/macros.h"
#include "ui/gfx/generic_shared_memory_id.h"
namespace media {
BufferAffinityTracker::BufferAffinityTracker(size_t nb_buffers) {
resize(0);
}
void BufferAffinityTracker::resize(size_t nb_buffers) {
base::AutoLock lock(lock_);
id_to_buffer_map_.clear();
nb_buffers_ = nb_buffers;
DVLOGF(4) << this << " resize: " << nb_buffers;
}
base::Optional<size_t> BufferAffinityTracker::get_buffer_for_id(
gfx::GenericSharedMemoryId id) {
base::AutoLock lock(lock_);
auto it = id_to_buffer_map_.find(id);
// If the handle is already bound to a buffer, return it.
if (it != id_to_buffer_map_.end()) {
DVLOGF(4) << this << " match for " << it->second;
return it->second;
}
// Try to assign a new buffer for this handle...
// No buffer available? No luck then.
if (id_to_buffer_map_.size() == nb_buffers()) {
DVLOGF(4) << this << " tracker is full!";
return base::nullopt;
}
const size_t v4l2_id = id_to_buffer_map_.size();
id_to_buffer_map_.emplace(id, v4l2_id);
DVLOGF(4) << this << " add " << v4l2_id;
return v4l2_id;
}
} // namespace media
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_GPU_V4L2_V4L2_BUFFER_AFFINITY_TRACKER_H_
#define MEDIA_GPU_V4L2_V4L2_BUFFER_AFFINITY_TRACKER_H_
#include <cstddef>
#include <map>
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "ui/gfx/generic_shared_memory_id.h"
namespace media {
/**
* Maintains affinity between native handles and V4L2 buffers.
*
* Give a handle ID, `get_buffer_for_id()` will attempt to always return the
* same V4L2 buffer ID so handles are always used with the same buffer. This
* is both beneficial for performance, and necessary in some cases like the
* stateful decoder.
*
* All the methods of this class are thread-safe.
*/
class BufferAffinityTracker {
public:
explicit BufferAffinityTracker(size_t nb_buffers);
size_t nb_buffers() const { return nb_buffers_; }
// Resize this tracker and reset its state.
void resize(size_t nb_buffers);
/**
* Return the V4L2 buffer index suitable for this buffer ID.
*
* If it is the first time this method is called with a given id, return the
* first available buffer it can find, and memorize the association between
* the id and the V4L2 buffer.
*
* On subsequent calls with the same id, that same V4L2 buffer will be
* returned.
*/
base::Optional<size_t> get_buffer_for_id(gfx::GenericSharedMemoryId id);
private:
base::Lock lock_;
std::map<gfx::GenericSharedMemoryId, size_t> id_to_buffer_map_;
// Maximum number of buffers we are allowed to track.
size_t nb_buffers_;
};
} // namespace media
#endif // MEDIA_GPU_V4L2_V4L2_BUFFER_AFFINITY_TRACKER_H_
...@@ -29,7 +29,9 @@ ...@@ -29,7 +29,9 @@
#include "media/gpu/chromeos/fourcc.h" #include "media/gpu/chromeos/fourcc.h"
#include "media/gpu/chromeos/platform_video_frame_utils.h" #include "media/gpu/chromeos/platform_video_frame_utils.h"
#include "media/gpu/macros.h" #include "media/gpu/macros.h"
#include "media/gpu/v4l2/buffer_affinity_tracker.h"
#include "media/gpu/v4l2/generic_v4l2_device.h" #include "media/gpu/v4l2/generic_v4l2_device.h"
#include "ui/gfx/generic_shared_memory_id.h"
#include "ui/gfx/native_pixmap_handle.h" #include "ui/gfx/native_pixmap_handle.h"
#if defined(ARCH_CPU_ARMEL) #if defined(ARCH_CPU_ARMEL)
...@@ -908,6 +910,7 @@ V4L2Queue::V4L2Queue(scoped_refptr<V4L2Device> dev, ...@@ -908,6 +910,7 @@ V4L2Queue::V4L2Queue(scoped_refptr<V4L2Device> dev,
enum v4l2_buf_type type, enum v4l2_buf_type type,
base::OnceClosure destroy_cb) base::OnceClosure destroy_cb)
: type_(type), : type_(type),
affinity_tracker_(0),
device_(dev), device_(dev),
destroy_cb_(std::move(destroy_cb)), destroy_cb_(std::move(destroy_cb)),
weak_this_factory_(this) { weak_this_factory_(this) {
...@@ -1097,6 +1100,8 @@ size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) { ...@@ -1097,6 +1100,8 @@ size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
free_buffers_->ReturnBuffer(i); free_buffers_->ReturnBuffer(i);
} }
affinity_tracker_.resize(buffers_.size());
DCHECK(free_buffers_); DCHECK(free_buffers_);
DCHECK_EQ(free_buffers_->size(), buffers_.size()); DCHECK_EQ(free_buffers_->size(), buffers_.size());
DCHECK_EQ(queued_buffers_.size(), 0u); DCHECK_EQ(queued_buffers_.size(), 0u);
...@@ -1117,6 +1122,7 @@ bool V4L2Queue::DeallocateBuffers() { ...@@ -1117,6 +1122,7 @@ bool V4L2Queue::DeallocateBuffers() {
weak_this_factory_.InvalidateWeakPtrs(); weak_this_factory_.InvalidateWeakPtrs();
buffers_.clear(); buffers_.clear();
affinity_tracker_.resize(0);
free_buffers_ = nullptr; free_buffers_ = nullptr;
// Free all buffers. // Free all buffers.
...@@ -1184,6 +1190,37 @@ base::Optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBuffer( ...@@ -1184,6 +1190,37 @@ base::Optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBuffer(
weak_this_factory_.GetWeakPtr()); weak_this_factory_.GetWeakPtr());
} }
base::Optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBufferForFrame(
const VideoFrame& frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// No buffers allocated at the moment?
if (!free_buffers_)
return base::nullopt;
if (memory_ != V4L2_MEMORY_DMABUF) {
DVLOGF(1) << "Queue is not DMABUF";
return base::nullopt;
}
gfx::GenericSharedMemoryId id;
if (auto gmb = frame.GetGpuMemoryBuffer()) {
id = gmb->GetId();
} else if (frame.HasDmaBufs()) {
id = gfx::GenericSharedMemoryId(frame.DmabufFds()[0].get());
} else {
DVLOGF(1) << "Unsupported frame provided";
return base::nullopt;
}
const auto v4l2_id = affinity_tracker_.get_buffer_for_id(id);
if (!v4l2_id) {
return base::nullopt;
}
return GetFreeBuffer(*v4l2_id);
}
bool V4L2Queue::QueueBuffer(struct v4l2_buffer* v4l2_buffer, bool V4L2Queue::QueueBuffer(struct v4l2_buffer* v4l2_buffer,
scoped_refptr<VideoFrame> video_frame) { scoped_refptr<VideoFrame> video_frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "media/base/video_frame_layout.h" #include "media/base/video_frame_layout.h"
#include "media/gpu/chromeos/fourcc.h" #include "media/gpu/chromeos/fourcc.h"
#include "media/gpu/media_gpu_export.h" #include "media/gpu/media_gpu_export.h"
#include "media/gpu/v4l2/buffer_affinity_tracker.h"
#include "media/gpu/v4l2/v4l2_device_poller.h" #include "media/gpu/v4l2/v4l2_device_poller.h"
#include "media/video/video_decode_accelerator.h" #include "media/video/video_decode_accelerator.h"
#include "media/video/video_encode_accelerator.h" #include "media/video/video_encode_accelerator.h"
...@@ -374,6 +375,23 @@ class MEDIA_GPU_EXPORT V4L2Queue ...@@ -374,6 +375,23 @@ class MEDIA_GPU_EXPORT V4L2Queue
// return |base::nullopt|. // return |base::nullopt|.
base::Optional<V4L2WritableBufferRef> GetFreeBuffer( base::Optional<V4L2WritableBufferRef> GetFreeBuffer(
size_t requested_buffer_id); size_t requested_buffer_id);
// Return a V4L2 buffer suitable for the passed VideoFrame.
//
// This method will try as much as possible to always return the same V4L2
// buffer when the same frame is passed again, to avoid memory unmap
// operations in the kernel driver.
//
// The operating mode of the queue must be DMABUF, and the VideoFrame must
// be backed either by a GpuMemoryBuffer, or by DMABUFs. In the case of
// DMABUFs, this method will only work correctly if the same DMABUFs are
// passed with each call, i.e. no dup shall be performed.
//
// This should be the preferred way to obtain buffers when using DMABUF mode,
// since it will maximize performance in that case provided the number of
// different VideoFrames passed to this method does not exceed the number of
// V4L2 buffers allocated on the queue.
base::Optional<V4L2WritableBufferRef> GetFreeBufferForFrame(
const VideoFrame& frame);
// Attempt to dequeue a buffer, and return a reference to it if one was // Attempt to dequeue a buffer, and return a reference to it if one was
// available. // available.
...@@ -443,6 +461,9 @@ class MEDIA_GPU_EXPORT V4L2Queue ...@@ -443,6 +461,9 @@ class MEDIA_GPU_EXPORT V4L2Queue
// value will be set to the VideoFrame that has been passed when we queued // value will be set to the VideoFrame that has been passed when we queued
// the buffer, if any. // the buffer, if any.
std::map<size_t, scoped_refptr<VideoFrame>> queued_buffers_; std::map<size_t, scoped_refptr<VideoFrame>> queued_buffers_;
// Keep track of which buffer was assigned to which frame by
// |GetFreeBufferForFrame()| so we reuse the same buffer in subsequent calls.
BufferAffinityTracker affinity_tracker_;
scoped_refptr<V4L2Device> device_; scoped_refptr<V4L2Device> device_;
// Callback to call in this queue's destructor. // Callback to call in this queue's destructor.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment