gpu: add support for fences

BUG=None
TEST=build with --features=gpu; null_platform_test

Change-Id: Ib863c8ef3e85aa0f345c1f20be414979808b6a17
Reviewed-on: https://chromium-review.googlesource.com/1073959
Commit-Ready: David Riley <davidriley@chromium.org>
Tested-by: David Riley <davidriley@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
David Riley 2018-05-17 17:14:42 -07:00 committed by chrome-bot
parent ba7c6035f8
commit f89e0b50e2
3 changed files with 122 additions and 5 deletions

View file

@ -920,4 +920,21 @@ impl Backend {
None => GpuResponse::ErrInvalidContextId,
}
}
pub fn create_fence(&mut self, ctx_id: u32, fence_id: u32) -> GpuResponse {
// There is a mismatch of ordering that is intentional.
// This create_fence matches the other functions in Backend, yet
// the renderer matches the virgl interface.
match self.renderer.create_fence(fence_id, ctx_id) {
Ok(_) => GpuResponse::OkNoData,
Err(e) => {
error!("failed to create fence: {}", e);
GpuResponse::ErrUnspec
}
}
}
pub fn fence_poll(&mut self) -> u32 {
self.renderer.poll()
}
}

View file

@ -9,14 +9,16 @@ extern crate gpu_renderer;
mod protocol;
mod backend;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::i64;
use std::mem::size_of;
use std::os::unix::io::RawFd;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread::spawn;
use std::time::Duration;
use data_model::*;
@ -35,6 +37,7 @@ use self::backend::Backend;
// First queue is for virtio gpu commands. Second queue is for cursor commands, which we expect
// there to be fewer of.
const QUEUE_SIZES: &'static [u16] = &[256, 16];
const FENCE_POLL_MS: u64 = 1;
struct QueueDescriptor {
index: u16,
@ -49,11 +52,18 @@ struct ReturnDescriptor {
len: u32,
}
struct FenceDescriptor {
fence_id: u32,
len: u32,
desc: QueueDescriptor,
}
struct Frontend {
ctrl_descriptors: VecDeque<QueueDescriptor>,
cursor_descriptors: VecDeque<QueueDescriptor>,
return_ctrl_descriptors: VecDeque<ReturnDescriptor>,
return_cursor_descriptors: VecDeque<ReturnDescriptor>,
fence_descriptors: Vec<FenceDescriptor>,
backend: Backend,
}
@ -64,6 +74,7 @@ impl Frontend {
cursor_descriptors: Default::default(),
return_ctrl_descriptors: Default::default(),
return_cursor_descriptors: Default::default(),
fence_descriptors: Default::default(),
backend,
}
}
@ -373,17 +384,40 @@ impl Frontend {
let mut flags = 0;
if let Some(cmd) = gpu_cmd {
let ctrl_hdr = cmd.ctrl_hdr();
// TODO: add proper fence support
if ctrl_hdr.flags.to_native() & VIRTIO_GPU_FLAG_FENCE != 0 {
fence_id = ctrl_hdr.fence_id.to_native();
ctx_id = ctrl_hdr.ctx_id.to_native();
flags = VIRTIO_GPU_FLAG_FENCE;
let fence_resp = self.backend
.create_fence(ctx_id, fence_id as u32);
if fence_resp.is_err() {
warn!("create_fence {} -> {:?}",
fence_id, fence_resp);
resp = fence_resp;
}
}
}
// Prepare the response now, even if it is going to wait until
// fence is complete.
match resp.encode(flags, fence_id, ctx_id, ret_desc_mem) {
Ok(l) => len = l,
Err(e) => debug!("ctrl queue response encode error: {:?}", e),
}
if flags & VIRTIO_GPU_FLAG_FENCE != 0 {
self.fence_descriptors.push(FenceDescriptor {
fence_id: fence_id as u32,
len,
desc,
});
return None
}
// No fence, respond now.
}
}
Some(ReturnDescriptor {
@ -411,6 +445,22 @@ impl Frontend {
.and_then(|desc| self.process_descriptor(mem, desc))
})
}
fn fence_poll(&mut self) {
let fence_id = self.backend.fence_poll();
let return_descs = &mut self.return_ctrl_descriptors;
self.fence_descriptors.retain(|f_desc| {
if f_desc.fence_id > fence_id {
true
} else {
return_descs.push_back(ReturnDescriptor {
index: f_desc.desc.index,
len: f_desc.len
});
false
}
})
}
}
struct Worker {
@ -462,7 +512,14 @@ impl Worker {
};
'poll: loop {
let events = match poll_ctx.wait() {
// If there are outstanding fences, wake up early to poll them.
let duration = if self.state.fence_descriptors.len() != 0 {
Duration::from_millis(FENCE_POLL_MS)
} else {
Duration::new(i64::MAX as u64, 0)
};
let events = match poll_ctx.wait_timeout(duration) {
Ok(v) => v,
Err(e) => {
error!("failed polling for events: {:?}", e);
@ -505,6 +562,8 @@ impl Worker {
}
}
self.state.fence_poll();
loop {
match self.state.process_ctrl(&self.mem) {
Some(ReturnDescriptor { index, len }) => {

View file

@ -12,6 +12,7 @@ mod generated;
mod pipe_format_fourcc;
mod command_buffer;
use std::cell::RefCell;
use std::ffi::CStr;
use std::fmt;
use std::fs::File;
@ -144,10 +145,32 @@ impl Box3 {
}
}
struct FenceState {
latest_fence: u32,
}
impl FenceState {
pub fn write(&mut self, latest_fence: u32) {
if latest_fence > self.latest_fence {
self.latest_fence = latest_fence;
}
}
}
struct VirglCookie {
display: EGLDisplay,
egl_config: EGLConfig,
egl_funcs: EGLFunctions,
fence_state: Rc<RefCell<FenceState>>,
}
extern "C" fn write_fence(cookie: *mut c_void,
fence: u32) {
assert!(!cookie.is_null());
let cookie = unsafe { &*(cookie as *mut VirglCookie) };
// Track the most recent fence.
let mut fence_state = cookie.fence_state.borrow_mut();
fence_state.write(fence);
}
unsafe extern "C" fn create_gl_context(cookie: *mut c_void,
@ -187,7 +210,7 @@ unsafe extern "C" fn destroy_gl_context(cookie: *mut c_void, ctx: virgl_renderer
const VIRGL_RENDERER_CALLBACKS: &virgl_renderer_callbacks =
&virgl_renderer_callbacks {
version: 1,
write_fence: None,
write_fence: Some(write_fence),
create_gl_context: Some(create_gl_context),
destroy_gl_context: Some(destroy_gl_context),
make_current: Some(make_current),
@ -305,6 +328,7 @@ pub struct Renderer {
no_sync_send: PhantomData<*mut ()>,
egl_funcs: EGLFunctions,
display: EGLDisplay,
fence_state: Rc<RefCell<FenceState>>,
}
impl Renderer {
@ -361,10 +385,14 @@ impl Renderer {
// Otherwise, Resource and Context would become invalid because their lifetime is not tied
// to the Renderer instance. Doing so greatly simplifies the ownership for users of this
// library.
let fence_state = Rc::new(RefCell::new(FenceState { latest_fence: 0 }));
let cookie: *mut VirglCookie = Box::into_raw(Box::new(VirglCookie {
display,
egl_config,
egl_funcs: egl_funcs.clone(),
fence_state: Rc::clone(&fence_state),
}));
// Safe because EGL was properly initialized before here..
@ -401,7 +429,8 @@ impl Renderer {
Ok(Renderer {
no_sync_send: PhantomData,
egl_funcs,
display
display,
fence_state
})
}
@ -553,6 +582,18 @@ impl Renderer {
image
})
}
pub fn poll(&self) -> u32 {
unsafe { virgl_renderer_poll() };
self.fence_state.borrow().latest_fence
}
pub fn create_fence(&mut self, fence_id: u32, ctx_id: u32) -> Result<()> {
let ret = unsafe {
virgl_renderer_create_fence(fence_id as i32, ctx_id)
};
ret_to_res(ret)
}
}
/// A context in which resources can be attached/detached and commands can be submitted.