blob: 17cce6ad8002cc9ed75b29c089f40bad6e0f39e0 [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;
mod view_token_pair;
pub use self::view_token_pair::*;
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, ViewArgs2,
ViewHolderArgs2, ViewProperties,
};
use fidl_fuchsia_ui_scenic::{Command, SessionProxy};
use fidl_fuchsia_ui_views::{ViewHolderToken, ViewToken};
use fuchsia_zircon::{Event, EventPair, HandleBased, Rights, Status, Vmo};
use mapped_vmo::Mapping;
use parking_lot::Mutex;
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) {
// A single FIDL message can contain only 64k of serialized
// data. Chunk the command list into fixed size chunks as a hacky
// way to work around this limit. This is imperfect, though, as
// there's no strict relationship between the number of commands
// and the size of the serialized data.
const CHUNK_SIZE: usize = 32;
for chunk in self.commands.chunks_mut(CHUNK_SIZE).into_iter() {
self.session
.enqueue(&mut chunk.into_iter())
.expect("Session failed to enqueue commands");
}
self.commands.truncate(0);
}
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(..),
)
}
pub fn next_resource_id(&self) -> u32 {
self.next_resource_id
}
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,
}
}
pub fn id(&self) -> u32 {
self.resource.id
}
}
pub struct Shape {
resource: Resource,
}
impl Shape {
fn new(resource: Resource) -> Shape {
Shape { resource }
}
pub 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)) }
}
pub 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, texture: Option<&Image>) {
self.resource.enqueue(cmd::set_texture(self.id(), texture.map(|t| t.id()).unwrap_or(0)));
}
}
pub struct Node {
resource: Resource,
}
impl Node {
fn new(resource: Resource) -> Node {
Node { resource }
}
pub 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 set_anchor(&self, x: f32, y: f32, z: f32) {
self.resource.enqueue(cmd::set_anchor(self.id(), x, y, z))
}
pub fn detach(&self) {
self.resource.enqueue(cmd::detach(self.id()))
}
fn enqueue(&self, command: Command) {
self.resource.enqueue(command);
}
}
pub struct View {
resource: Resource,
}
impl View {
pub fn new(session: SessionPtr, token: ViewToken, debug_name: Option<String>) -> View {
let args = ViewArgs2 { token: token, debug_name: debug_name };
View { resource: Resource::new(session, ResourceArgs::View2(args)) }
}
pub fn id(&self) -> u32 {
self.resource.id
}
pub fn add_child(&self, child: &Node) {
self.resource.enqueue(cmd::add_child(self.id(), child.id()))
}
pub fn detach_child(&self, child: &Node) {
self.resource.enqueue(cmd::detach(child.id()))
}
}
pub struct ViewHolder {
resource: Resource,
}
impl ViewHolder {
pub fn new(
session: SessionPtr,
token: ViewHolderToken,
debug_name: Option<String>,
) -> ViewHolder {
let args = ViewHolderArgs2 { token: token, debug_name: debug_name };
ViewHolder { resource: Resource::new(session, ResourceArgs::ViewHolder2(args)) }
}
pub fn id(&self) -> u32 {
self.resource.id
}
pub fn set_view_properties(&self, view_properties: ViewProperties) {
self.resource.enqueue(cmd::set_view_properties(self.id(), view_properties))
}
}
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 attach(&self, view_holder: &ViewHolder) {
self.enqueue(cmd::add_child(self.id(), view_holder.id()))
}
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
}
}
pub struct HostMemory {
memory: Memory,
mapping: Arc<Mapping>,
}
impl HostMemory {
pub fn allocate(session: SessionPtr, size: usize) -> Result<HostMemory, Status> {
let (mapping, vmo) = Mapping::allocate(size)?;
let remote = vmo.duplicate_handle(
Rights::DUPLICATE
| Rights::TRANSFER
| Rights::WAIT
| Rights::INSPECT
| Rights::READ
| Rights::MAP,
)?;
Ok(HostMemory {
memory: Memory::new(session, remote, size as u64, MemoryType::HostMemory),
mapping: Arc::new(mapping),
})
}
pub fn len(&self) -> usize {
self.mapping.len()
}
}
pub struct HostImage {
image: Image,
mapping: Arc<Mapping>,
}
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 mapping(&self) -> &Arc<Mapping> {
&self.mapping
}
}
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(Some(&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);
}
}