blob: 228f29450a030963bb59eaabc5ee30e5286ac5bf [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![deny(warnings)]
mod cmd;
use fidl_fuchsia_images::{ImageInfo, MemoryType, PixelFormat, PresentationInfo, Tiling};
use fidl_fuchsia_ui_gfx::{
CircleArgs, ColorRgba, EntityNodeArgs, ImageArgs, ImportSpec, MaterialArgs, MemoryArgs,
RectangleArgs, ResourceArgs, RoundedRectangleArgs, ShapeNodeArgs, Value,
};
use fidl_fuchsia_ui_scenic::{Command, SessionProxy};
use fuchsia_zircon::{Event, EventPair, HandleBased, Rights, Status, Vmar, VmarFlags, Vmo};
use parking_lot::Mutex;
use shared_buffer::SharedBuffer;
use std::mem;
use std::ops::Deref;
use std::sync::Arc;
pub struct Session {
session: SessionProxy,
next_resource_id: u32,
resource_count: u32,
commands: Vec<Command>,
acquire_fences: Vec<Event>,
release_fences: Vec<Event>,
}
pub type SessionPtr = Arc<Mutex<Session>>;
impl Session {
pub fn new(session: SessionProxy) -> SessionPtr {
Arc::new(Mutex::new(Session {
session,
next_resource_id: 1,
resource_count: 0,
commands: vec![],
acquire_fences: vec![],
release_fences: vec![],
}))
}
pub fn enqueue(&mut self, command: Command) {
self.commands.push(command)
}
pub fn flush(&mut self) {
let mut commands = mem::replace(&mut self.commands, vec![]);
self.session.enqueue(&mut commands.iter_mut()).ok();
}
pub fn present(
&mut self, presentation_time: u64,
) -> fidl::client::QueryResponseFut<(PresentationInfo)> {
self.flush();
self.session.present(
presentation_time,
&mut self.acquire_fences.drain(..),
&mut self.release_fences.drain(..),
)
}
fn alloc_resource_id(&mut self) -> u32 {
let id = self.next_resource_id;
self.next_resource_id += 1;
self.resource_count += 1;
assert!(id != 0);
id
}
fn release_resource(&mut self, id: u32) {
self.resource_count -= 1;
self.enqueue(cmd::release_resource(id))
}
pub fn add_acquire_fence(&mut self, fence: Event) {
self.acquire_fences.push(fence)
}
pub fn add_release_fence(&mut self, fence: Event) {
self.release_fences.push(fence)
}
}
pub struct Resource {
session: SessionPtr,
id: u32,
}
impl Resource {
fn new(session: SessionPtr, resource: ResourceArgs) -> Resource {
let id = {
let mut s = session.lock();
let id = s.alloc_resource_id();
s.enqueue(cmd::create_resource(id, resource));
id
};
Resource { session, id }
}
fn import(session: SessionPtr, token: EventPair, spec: ImportSpec) -> Resource {
let id = {
let mut s = session.lock();
let id = s.alloc_resource_id();
s.enqueue(cmd::import_resource(id, token, spec));
id
};
Resource { session, id }
}
fn enqueue(&self, command: Command) {
let mut session = self.session.lock();
session.enqueue(command);
}
pub fn set_event_mask(&self, event_mask: u32) {
self.enqueue(cmd::set_event_mask(self.id, event_mask))
}
}
impl Drop for Resource {
fn drop(&mut self) {
let mut s = self.session.lock();
s.release_resource(self.id);
}
}
pub struct Memory {
resource: Resource,
}
impl Memory {
pub fn new(
session: SessionPtr, vmo: Vmo, allocation_size: u64, memory_type: MemoryType,
) -> Memory {
let args = MemoryArgs {
vmo: vmo,
allocation_size: allocation_size,
memory_type,
};
Memory {
resource: Resource::new(session, ResourceArgs::Memory(args)),
}
}
}
pub struct Image {
resource: Resource,
info: ImageInfo,
}
impl Image {
pub fn new(memory: &Memory, memory_offset: u32, info: ImageInfo) -> Image {
let args = ImageArgs {
memory_id: memory.resource.id,
memory_offset,
// TODO: Use clone once FIDL generated the proper annotations.
info: ImageInfo { ..info },
};
Image {
resource: Resource::new(memory.resource.session.clone(), ResourceArgs::Image(args)),
info: info,
}
}
fn id(&self) -> u32 {
self.resource.id
}
}
pub struct Shape {
resource: Resource,
}
impl Shape {
fn new(resource: Resource) -> Shape {
Shape { resource }
}
fn id(&self) -> u32 {
self.resource.id
}
}
pub struct Circle(Shape);
impl Circle {
pub fn new(session: SessionPtr, radius: f32) -> Circle {
let args = CircleArgs {
radius: Value::Vector1(radius),
};
Circle(Shape::new(Resource::new(
session,
ResourceArgs::Circle(args),
)))
}
}
impl Deref for Circle {
type Target = Shape;
fn deref(&self) -> &Shape {
&self.0
}
}
pub struct Rectangle(Shape);
impl Rectangle {
pub fn new(session: SessionPtr, width: f32, height: f32) -> Rectangle {
let args = RectangleArgs {
width: Value::Vector1(width),
height: Value::Vector1(height),
};
Rectangle(Shape::new(Resource::new(
session,
ResourceArgs::Rectangle(args),
)))
}
}
impl Deref for Rectangle {
type Target = Shape;
fn deref(&self) -> &Shape {
&self.0
}
}
pub struct RoundedRectangle(Shape);
impl RoundedRectangle {
pub fn new(
session: SessionPtr, width: f32, height: f32, top_left_radius: f32, top_right_radius: f32,
bottom_right_radius: f32, bottom_left_radius: f32,
) -> RoundedRectangle {
let args = RoundedRectangleArgs {
width: Value::Vector1(width),
height: Value::Vector1(height),
top_left_radius: Value::Vector1(top_left_radius),
top_right_radius: Value::Vector1(top_right_radius),
bottom_right_radius: Value::Vector1(bottom_right_radius),
bottom_left_radius: Value::Vector1(bottom_left_radius),
};
RoundedRectangle(Shape::new(Resource::new(
session,
ResourceArgs::RoundedRectangle(args),
)))
}
}
impl Deref for RoundedRectangle {
type Target = Shape;
fn deref(&self) -> &Shape {
&self.0
}
}
pub struct Material {
resource: Resource,
}
impl Material {
pub fn new(session: SessionPtr) -> Material {
let args = MaterialArgs { dummy: 0 };
Material {
resource: Resource::new(session, ResourceArgs::Material(args)),
}
}
fn id(&self) -> u32 {
self.resource.id
}
pub fn set_color(&self, color: ColorRgba) {
self.resource.enqueue(cmd::set_color(self.id(), color));
}
pub fn set_texture(&self, image: &Image) {
self.resource
.enqueue(cmd::set_texture(self.id(), image.id()));
}
}
pub struct Node {
resource: Resource,
}
impl Node {
fn new(resource: Resource) -> Node {
Node { resource }
}
fn id(&self) -> u32 {
self.resource.id
}
pub fn resource(&self) -> &Resource {
&self.resource
}
pub fn set_translation(&self, x: f32, y: f32, z: f32) {
self.resource
.enqueue(cmd::set_translation(self.id(), x, y, z))
}
pub fn set_scale(&self, x: f32, y: f32, z: f32) {
self.resource.enqueue(cmd::set_scale(self.id(), x, y, z))
}
pub fn set_rotation(&self, x: f32, y: f32, z: f32, w: f32) {
self.resource
.enqueue(cmd::set_rotation(self.id(), x, y, z, w))
}
pub fn detach(&self) {
self.resource.enqueue(cmd::detach(self.id()))
}
fn enqueue(&self, command: Command) {
self.resource.enqueue(command);
}
}
pub struct ShapeNode(Node);
impl ShapeNode {
pub fn new(session: SessionPtr) -> ShapeNode {
let args = ShapeNodeArgs { unused: 0 };
ShapeNode(Node::new(Resource::new(
session,
ResourceArgs::ShapeNode(args),
)))
}
pub fn set_shape(&self, shape: &Shape) {
self.enqueue(cmd::set_shape(self.id(), shape.id()));
}
pub fn set_material(&self, material: &Material) {
self.enqueue(cmd::set_material(self.id(), material.id()));
}
}
impl Deref for ShapeNode {
type Target = Node;
fn deref(&self) -> &Node {
&self.0
}
}
pub struct ContainerNode(Node);
impl ContainerNode {
fn new(resource: Resource) -> ContainerNode {
ContainerNode(Node::new(resource))
}
pub fn add_child(&self, node: &Node) {
self.enqueue(cmd::add_child(self.id(), node.id()));
}
pub fn add_part(&self, node: &Node) {
self.enqueue(cmd::add_part(self.id(), node.id()));
}
}
impl Deref for ContainerNode {
type Target = Node;
fn deref(&self) -> &Node {
&self.0
}
}
pub struct ImportNode(ContainerNode);
impl ImportNode {
pub fn new(session: SessionPtr, token: EventPair) -> ImportNode {
ImportNode(ContainerNode::new(Resource::import(
session,
token,
ImportSpec::Node,
)))
}
}
impl Deref for ImportNode {
type Target = ContainerNode;
fn deref(&self) -> &ContainerNode {
&self.0
}
}
pub struct EntityNode(ContainerNode);
impl EntityNode {
pub fn new(session: SessionPtr) -> EntityNode {
let args = EntityNodeArgs { unused: 0 };
EntityNode(ContainerNode::new(Resource::new(
session,
ResourceArgs::EntityNode(args),
)))
}
pub fn set_clip(&self, clip_id: u32, clip_to_self: bool) {
self.enqueue(cmd::set_clip(self.id(), clip_id, clip_to_self));
}
pub fn export_as_request(&self) -> EventPair {
let (mine, theirs) = EventPair::create().unwrap();
self.enqueue(cmd::export_resource(self.id(), mine));
theirs
}
}
impl Deref for EntityNode {
type Target = ContainerNode;
fn deref(&self) -> &ContainerNode {
&self.0
}
}
struct MemoryMapping {
addr: usize,
len: usize,
}
impl MemoryMapping {
fn allocate(size: usize) -> Result<(Vmo, MemoryMapping), Status> {
let vmo = Vmo::create(size as u64)?;
let remote = vmo.duplicate_handle(
Rights::DUPLICATE
| Rights::TRANSFER
| Rights::WAIT
| Rights::INSPECT
| Rights::READ
| Rights::MAP,
)?;
let memory = Self::new(
&vmo,
0,
size,
VmarFlags::PERM_READ | VmarFlags::PERM_WRITE | VmarFlags::MAP_RANGE,
)?;
Ok((remote, memory))
}
fn new(vmo: &Vmo, offset: u64, len: usize, flags: VmarFlags) -> Result<MemoryMapping, Status> {
let addr = Vmar::root_self().map(0, vmo, offset, len, flags)?;
Ok(MemoryMapping { addr, len })
}
pub fn buffer(&self) -> SharedBuffer {
unsafe { SharedBuffer::new(self.addr as *mut u8, self.len) }
}
}
impl Drop for MemoryMapping {
fn drop(&mut self) {
unsafe {
Vmar::root_self().unmap(self.addr, self.len).unwrap();
}
}
}
pub struct HostMemory {
memory: Memory,
mapping: Arc<MemoryMapping>,
}
impl HostMemory {
pub fn allocate(session: SessionPtr, size: usize) -> Result<HostMemory, Status> {
let (vmo, mapping) = MemoryMapping::allocate(size)?;
Ok(HostMemory {
memory: Memory::new(session, vmo, size as u64, MemoryType::HostMemory),
mapping: Arc::new(mapping),
})
}
pub fn size(&self) -> usize {
self.mapping.len
}
}
pub struct HostImage {
image: Image,
mapping: Arc<MemoryMapping>,
}
impl HostImage {
pub fn new(memory: &HostMemory, memory_offset: u32, info: ImageInfo) -> HostImage {
HostImage {
image: Image::new(&memory.memory, memory_offset, info),
mapping: memory.mapping.clone(),
}
}
pub fn buffer(&self) -> SharedBuffer {
self.mapping.buffer()
}
}
impl Deref for HostImage {
type Target = Image;
fn deref(&self) -> &Image {
&self.image
}
}
fn get_image_size(info: &ImageInfo) -> usize {
assert!(info.tiling == Tiling::Linear);
match info.pixel_format {
PixelFormat::Bgra8 => (info.height * info.stride) as usize,
PixelFormat::Yuy2 => (info.height * info.stride) as usize,
PixelFormat::Nv12 => (info.height * info.stride * 3 / 2) as usize,
PixelFormat::Yv12 => (info.height * info.stride * 3 / 2) as usize,
}
}
pub struct HostImageGuard<'a> {
cycler: &'a mut HostImageCycler,
image: Option<HostImage>,
}
impl<'a> HostImageGuard<'a> {
pub fn image(&self) -> &HostImage {
self.image.as_ref().unwrap()
}
}
impl<'a> Drop for HostImageGuard<'a> {
fn drop(&mut self) {
self.cycler.release(self.image.take().unwrap());
}
}
pub struct HostImageCycler {
node: EntityNode,
content_node: ShapeNode,
content_material: Material,
content_shape: Option<Rectangle>,
}
impl HostImageCycler {
pub fn new(session: SessionPtr) -> HostImageCycler {
let node = EntityNode::new(session.clone());
let content_node = ShapeNode::new(session.clone());
let content_material = Material::new(session);
content_node.set_material(&content_material);
node.add_child(&content_node);
HostImageCycler {
node,
content_node,
content_material,
content_shape: None,
}
}
pub fn node(&self) -> &EntityNode {
&self.node
}
pub fn acquire<'a>(&'a mut self, info: ImageInfo) -> Result<HostImageGuard<'a>, Status> {
if info.tiling != Tiling::Linear {
return Err(Status::NOT_SUPPORTED);
}
let desired_size = get_image_size(&info);
let memory = HostMemory::allocate(self.node.resource.session.clone(), desired_size)?;
let image = HostImage::new(&memory, 0, info);
Ok(HostImageGuard {
cycler: self,
image: Some(image),
})
}
fn release(&mut self, image: HostImage) {
self.content_material.set_texture(&image);
let rectangle = Rectangle::new(
self.node.resource.session.clone(),
image.info.width as f32,
image.info.height as f32,
);
self.content_node.set_shape(&rectangle);
self.content_shape = Some(rectangle);
}
}