mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 02:25:23 +00:00
devices: video: dec: Support arbitrary buffers to be mapped as resources
Support a case where a guest client who may use arbitrary numbers of buffers. (e.g. C2V4L2Component with default pool in ARCVM) Such a client is valid as long as it uses at most 32 buffers at the same time. More specifically, this CL allows the guest to call ResourceCreate for an output resource_id which was already processed by the host. Such ResourceCreate calls will be handled as reassignment of DMAbuf to a FrameBufferId. BUG=b:157702336 TEST=Play a YouTube video on ARCVM w/ C2V4L2Component using default pool Change-Id: Ie9c457867abd91b6b7a17a5bca4a1a1e9f53c1ae Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2198327 Reviewed-by: Alexandre Courbot <acourbot@chromium.org> Tested-by: Keiichi Watanabe <keiichiw@chromium.org> Commit-Queue: Keiichi Watanabe <keiichiw@chromium.org>
This commit is contained in:
parent
97d6359feb
commit
4ffb3d06bd
1 changed files with 58 additions and 25 deletions
|
@ -5,7 +5,7 @@
|
|||
//! Implementation of a virtio video decoder device backed by LibVDA.
|
||||
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd};
|
||||
|
@ -42,6 +42,8 @@ type FrameBufferId = i32;
|
|||
type ResourceHandle = u32;
|
||||
type Timestamp = u64;
|
||||
|
||||
const OUTPUT_BUFFER_COUNT: usize = 32;
|
||||
|
||||
// Represents queue types of pending Clear commands if exist.
|
||||
#[derive(Default)]
|
||||
struct PendingClearCmds {
|
||||
|
@ -64,9 +66,14 @@ struct Context {
|
|||
res_id_to_res_handle: BTreeMap<u32, ResourceHandle>,
|
||||
|
||||
// OutputResourceId <-> FrameBufferId
|
||||
// TODO: Encapsulate the following two maps as a type of a bijective map.
|
||||
res_id_to_frame_buf_id: BTreeMap<OutputResourceId, FrameBufferId>,
|
||||
frame_buf_id_to_res_id: BTreeMap<FrameBufferId, OutputResourceId>,
|
||||
|
||||
// Stores free FrameBufferIds which `use_output_buffer()` or `reuse_output_buffer()` can be
|
||||
// called with.
|
||||
available_frame_buf_ids: BTreeSet<FrameBufferId>,
|
||||
|
||||
keep_resources: Vec<File>,
|
||||
|
||||
// Stores queue types of pending Clear commands if exist.
|
||||
|
@ -127,12 +134,31 @@ impl Context {
|
|||
self.res_id_to_res_handle.insert(resource_id, handle);
|
||||
}
|
||||
|
||||
fn register_queued_frame_buffer(&mut self, resource_id: OutputResourceId) -> FrameBufferId {
|
||||
// Generate a new FrameBufferId
|
||||
let id = self.res_id_to_frame_buf_id.len() as FrameBufferId;
|
||||
fn register_queued_frame_buffer(
|
||||
&mut self,
|
||||
resource_id: OutputResourceId,
|
||||
) -> VideoResult<FrameBufferId> {
|
||||
// Use the first free frame buffer id.
|
||||
let id = *self.available_frame_buf_ids.iter().next().ok_or_else(|| {
|
||||
error!(
|
||||
"no frame buffer ID is available for resource_id {}",
|
||||
resource_id
|
||||
);
|
||||
VideoError::InvalidOperation
|
||||
})?;
|
||||
self.available_frame_buf_ids.remove(&id);
|
||||
|
||||
// Invalidate old entries for `resource_id` and `id` from two maps to keep them bijective.
|
||||
if let Some(old_frame_buf_id) = self.res_id_to_frame_buf_id.remove(&resource_id) {
|
||||
self.frame_buf_id_to_res_id.remove(&old_frame_buf_id);
|
||||
}
|
||||
if let Some(old_res_id) = self.frame_buf_id_to_res_id.remove(&id) {
|
||||
self.res_id_to_frame_buf_id.remove(&old_res_id);
|
||||
}
|
||||
|
||||
self.res_id_to_frame_buf_id.insert(resource_id, id);
|
||||
self.frame_buf_id_to_res_id.insert(id, resource_id);
|
||||
id
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
|
@ -200,6 +226,9 @@ impl Context {
|
|||
right: i32,
|
||||
bottom: i32,
|
||||
) -> Option<ResourceId> {
|
||||
// `buffer_id` becomes available for another frame.
|
||||
self.available_frame_buf_ids.insert(buffer_id);
|
||||
|
||||
let plane_size = ((right - left) * (bottom - top)) as u32;
|
||||
for fmt in self.out_params.plane_formats.iter_mut() {
|
||||
fmt.plane_size = plane_size;
|
||||
|
@ -239,6 +268,10 @@ impl Context {
|
|||
} else if self.pending_clear_cmds.output {
|
||||
self.pending_clear_cmds.output = false;
|
||||
|
||||
// Reinitialize `available_frame_buf_ids`.
|
||||
self.available_frame_buf_ids.clear();
|
||||
self.available_frame_buf_ids = (0..OUTPUT_BUFFER_COUNT as i32).collect();
|
||||
|
||||
Some(QueueType::Output)
|
||||
} else {
|
||||
error!("unexpected ResetResponse");
|
||||
|
@ -426,8 +459,6 @@ impl<'a> Decoder<'a> {
|
|||
// first time.
|
||||
let mut ctx = self.contexts.get_mut(&stream_id)?;
|
||||
if !ctx.set_output_buffer_count {
|
||||
const OUTPUT_BUFFER_COUNT: usize = 32;
|
||||
|
||||
// Set the buffer count to the maximum value.
|
||||
// TODO(b/1518105): This is a hack due to the lack of way of telling a number of
|
||||
// frame buffers explictly in virtio-video v3 RFC. Once we have the way,
|
||||
|
@ -436,20 +467,18 @@ impl<'a> Decoder<'a> {
|
|||
.get(&stream_id)?
|
||||
.set_output_buffer_count(OUTPUT_BUFFER_COUNT)
|
||||
.map_err(VideoError::VdaError)?;
|
||||
|
||||
// FrameBufferId must be in the range of 0.. ((# of buffers) - 1).
|
||||
ctx.available_frame_buf_ids = (0..OUTPUT_BUFFER_COUNT as i32).collect();
|
||||
|
||||
ctx.set_output_buffer_count = true;
|
||||
}
|
||||
|
||||
// We assume ResourceCreate is not called to an output resource that is already
|
||||
// imported to Chrome for now.
|
||||
// TODO(keiichiw): We need to support this case for a guest client who may use
|
||||
// arbitrary numbers of buffers. (e.g. C2V4L2Component in ARCVM)
|
||||
// Such a client is valid as long as it uses at most 32 buffers at the same time.
|
||||
// If the given resource_id was associated with an old frame_buf_id,
|
||||
// dissociate them.
|
||||
if let Some(frame_buf_id) = ctx.res_id_to_frame_buf_id.get(&resource_id) {
|
||||
error!(
|
||||
"resource {} has already been imported to Chrome as a frame buffer {}",
|
||||
resource_id, frame_buf_id
|
||||
);
|
||||
return Err(VideoError::InvalidOperation);
|
||||
ctx.frame_buf_id_to_res_id.remove(&frame_buf_id);
|
||||
ctx.res_id_to_frame_buf_id.remove(&resource_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -545,14 +574,18 @@ impl<'a> Decoder<'a> {
|
|||
// In case a given resource has been imported to VDA, call
|
||||
// `session.reuse_output_buffer()` and return a response.
|
||||
// Otherwise, `session.use_output_buffer()` will be called below.
|
||||
match ctx.res_id_to_frame_buf_id.get(&resource_id) {
|
||||
Some(buffer_id) => {
|
||||
if let Some(buffer_id) = ctx.res_id_to_frame_buf_id.get(&resource_id) {
|
||||
if !ctx.available_frame_buf_ids.remove(&buffer_id) {
|
||||
error!(
|
||||
"resource_id {} is associated with VDA's frame_buffer id {}, which is in use or out of range.",
|
||||
resource_id, buffer_id
|
||||
);
|
||||
return Err(VideoError::InvalidOperation);
|
||||
}
|
||||
session
|
||||
.reuse_output_buffer(*buffer_id)
|
||||
.map_err(VideoError::VdaError)?;
|
||||
return Ok(());
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
|
||||
let resource_info = ctx.get_resource_info(resource_bridge, resource_id)?;
|
||||
|
@ -579,7 +612,7 @@ impl<'a> Decoder<'a> {
|
|||
let buffer_id = self
|
||||
.contexts
|
||||
.get_mut(&stream_id)?
|
||||
.register_queued_frame_buffer(resource_id);
|
||||
.register_queued_frame_buffer(resource_id)?;
|
||||
|
||||
session
|
||||
.use_output_buffer(buffer_id as i32, libvda::PixelFormat::NV12, fd, &planes)
|
||||
|
|
Loading…
Reference in a new issue