| // 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. |
| |
| mod cmd; |
| mod view_ref_pair; |
| mod view_token_pair; |
| pub use self::view_ref_pair::*; |
| pub use self::view_token_pair::*; |
| |
| use fidl::endpoints::ClientEnd; |
| use fidl::endpoints::ServerEnd; |
| use fidl_fuchsia_images::{ |
| ImageInfo, ImagePipe2Marker, MemoryType, PixelFormat, PresentationInfo, Tiling, |
| }; |
| use fidl_fuchsia_scenic_scheduling::FuturePresentationTimes; |
| use fidl_fuchsia_sysmem::BufferCollectionTokenMarker; |
| use fidl_fuchsia_ui_gfx::{ |
| AmbientLightArgs, CameraArgs, CircleArgs, ColorRgb, ColorRgba, DirectionalLightArgs, |
| DisplayCompositorArgs, EntityNodeArgs, ImageArgs, ImageArgs2, ImagePipe2Args, LayerArgs, |
| LayerStackArgs, MaterialArgs, MemoryArgs, PointLightArgs, RectangleArgs, RendererArgs, |
| ResourceArgs, RoundedRectangleArgs, SceneArgs, ShapeNodeArgs, Value, ViewArgs, ViewArgs3, |
| ViewHolderArgs, ViewProperties, |
| }; |
| use fidl_fuchsia_ui_scenic::{Command, Present2Args, SessionEventStream, SessionProxy}; |
| use fidl_fuchsia_ui_views::{ViewHolderToken, ViewRef, ViewRefControl, ViewToken}; |
| use fuchsia_zircon::{Event, 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 present2( |
| &mut self, |
| requested_prediction_span: i64, |
| requested_presentation_time: i64, |
| ) -> fidl::client::QueryResponseFut<FuturePresentationTimes> { |
| self.flush(); |
| let args = Present2Args { |
| requested_presentation_time: Some(requested_presentation_time), |
| requested_prediction_span: Some(requested_prediction_span), |
| acquire_fences: Some(self.acquire_fences.drain(..).collect()), |
| release_fences: Some(self.release_fences.drain(..).collect()), |
| ..Present2Args::EMPTY |
| }; |
| self.session.present2(args) |
| } |
| |
| pub fn take_event_stream(&self) -> SessionEventStream { |
| self.session.take_event_stream() |
| } |
| |
| 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 fn register_buffer_collection( |
| &self, |
| buffer_id: u32, |
| token: ClientEnd<BufferCollectionTokenMarker>, |
| ) -> Result<(), fidl::Error> { |
| self.session.register_buffer_collection(buffer_id, token) |
| } |
| |
| pub fn deregister_buffer_collection(&self, buffer_id: u32) -> Result<(), fidl::Error> { |
| self.session.deregister_buffer_collection(buffer_id) |
| } |
| } |
| |
| 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 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 Image2 { |
| resource: Resource, |
| } |
| |
| impl Image2 { |
| pub fn new( |
| session: &SessionPtr, |
| width: u32, |
| height: u32, |
| buffer_collection_id: u32, |
| buffer_collection_index: u32, |
| ) -> Image2 { |
| let args = ImageArgs2 { width, height, buffer_collection_id, buffer_collection_index }; |
| Image2 { resource: Resource::new(session.clone(), ResourceArgs::Image2(args)) } |
| } |
| |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| } |
| |
| impl Deref for Image2 { |
| type Target = Resource; |
| |
| fn deref(&self) -> &Resource { |
| &self.resource |
| } |
| } |
| |
| 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 fn set_texture_resource(&self, texture: Option<&Resource>) { |
| 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 AmbientLight { |
| resource: Resource, |
| } |
| |
| impl AmbientLight { |
| pub fn new(session: SessionPtr) -> AmbientLight { |
| let args = AmbientLightArgs { dummy: 0 }; |
| AmbientLight { resource: Resource::new(session, ResourceArgs::AmbientLight(args)) } |
| } |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| |
| pub fn set_color(&self, color: ColorRgb) { |
| self.resource.enqueue(cmd::set_light_color(self.id(), color)); |
| } |
| pub fn detach_light(&self) { |
| self.resource.enqueue(cmd::detach_light(self.id())); |
| } |
| } |
| pub struct PointLight { |
| resource: Resource, |
| } |
| |
| impl PointLight { |
| pub fn new(session: SessionPtr) -> PointLight { |
| let args = PointLightArgs { dummy: 0 }; |
| PointLight { resource: Resource::new(session, ResourceArgs::PointLight(args)) } |
| } |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| pub fn set_color(&self, color: ColorRgb) { |
| self.resource.enqueue(cmd::set_light_color(self.id(), color)); |
| } |
| pub fn detach_light(&self) { |
| self.resource.enqueue(cmd::detach_light(self.id())); |
| } |
| } |
| pub struct DirectionalLight { |
| resource: Resource, |
| } |
| |
| impl DirectionalLight { |
| pub fn new(session: SessionPtr) -> DirectionalLight { |
| let args = DirectionalLightArgs { dummy: 0 }; |
| DirectionalLight { resource: Resource::new(session, ResourceArgs::DirectionalLight(args)) } |
| } |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| pub fn set_color(&self, color: ColorRgb) { |
| self.resource.enqueue(cmd::set_light_color(self.id(), color)); |
| } |
| pub fn set_direction(&self, x: f32, y: f32, z: f32) { |
| self.resource.enqueue(cmd::set_light_direction(self.id(), x, y, z)); |
| } |
| |
| pub fn detach_light(&self) { |
| self.resource.enqueue(cmd::detach_light(self.id())); |
| } |
| } |
| |
| pub struct Scene { |
| resource: Resource, |
| } |
| |
| impl Scene { |
| pub fn new(session: SessionPtr) -> Scene { |
| let args = SceneArgs { dummy: 0 }; |
| Scene { resource: Resource::new(session, ResourceArgs::Scene(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 add_directional_light(&self, light: &DirectionalLight) { |
| self.resource.enqueue(cmd::scene_add_directional_light(self.id(), light.id())); |
| } |
| |
| pub fn add_ambient_light(&self, light: &AmbientLight) { |
| self.resource.enqueue(cmd::scene_add_ambient_light(self.id(), light.id())); |
| } |
| |
| pub fn add_point_light(&self, light: &PointLight) { |
| self.resource.enqueue(cmd::scene_add_point_light(self.id(), light.id())); |
| } |
| |
| pub fn detach_lights(&self) { |
| self.resource.enqueue(cmd::detach_lights(self.id())); |
| } |
| |
| pub fn set_scale(&self, x: f32, y: f32, z: f32) { |
| self.resource.enqueue(cmd::set_scale(self.id(), x, y, z)) |
| } |
| } |
| |
| pub struct Camera { |
| resource: Resource, |
| } |
| |
| impl Camera { |
| pub fn new(session: SessionPtr, scene: &Scene) -> Camera { |
| let args = CameraArgs { scene_id: scene.id() }; |
| Camera { resource: Resource::new(session, ResourceArgs::Camera(args)) } |
| } |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| } |
| |
| pub struct Renderer { |
| resource: Resource, |
| } |
| |
| impl Renderer { |
| pub fn new(session: SessionPtr) -> Renderer { |
| let args = RendererArgs { dummy: 0 }; |
| Renderer { resource: Resource::new(session, ResourceArgs::Renderer(args)) } |
| } |
| |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| |
| pub fn set_camera(&self, camera: &Camera) { |
| self.resource.enqueue(cmd::set_camera(self.id(), camera.id())) |
| } |
| } |
| |
| pub struct Layer { |
| resource: Resource, |
| } |
| |
| impl Layer { |
| pub fn new(session: SessionPtr) -> Layer { |
| let args = LayerArgs { dummy: 0 }; |
| Layer { resource: Resource::new(session, ResourceArgs::Layer(args)) } |
| } |
| |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| |
| pub fn set_renderer(&self, renderer: &Renderer) { |
| self.resource.enqueue(cmd::set_renderer(self.id(), renderer.id())) |
| } |
| |
| pub fn set_size(&self, x: f32, y: f32) { |
| self.resource.enqueue(cmd::set_size(self.id(), x, y)) |
| } |
| } |
| |
| pub struct LayerStack { |
| resource: Resource, |
| } |
| |
| impl LayerStack { |
| pub fn new(session: SessionPtr) -> LayerStack { |
| let args = LayerStackArgs { dummy: 0 }; |
| LayerStack { resource: Resource::new(session, ResourceArgs::LayerStack(args)) } |
| } |
| |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| |
| pub fn add_layer(&self, layer: &Layer) { |
| self.resource.enqueue(cmd::add_layer(self.id(), layer.id())) |
| } |
| |
| pub fn remove_layer(&self, layer: &Layer) { |
| self.resource.enqueue(cmd::remove_layer(self.id(), layer.id())) |
| } |
| |
| pub fn remove_all_layers(&self) { |
| self.resource.enqueue(cmd::remove_all_layers(self.id())) |
| } |
| } |
| |
| pub struct DisplayCompositor { |
| resource: Resource, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Debug)] |
| pub enum DisplayRotation { |
| None = 0, |
| By90Degrees = 90, |
| By180Degrees = 180, |
| By270Degrees = 270, |
| } |
| |
| impl DisplayCompositor { |
| pub fn new(session: SessionPtr) -> DisplayCompositor { |
| let args = DisplayCompositorArgs { dummy: 0 }; |
| DisplayCompositor { |
| resource: Resource::new(session, ResourceArgs::DisplayCompositor(args)), |
| } |
| } |
| |
| pub fn id(&self) -> u32 { |
| self.resource.id |
| } |
| |
| pub fn set_display_rotation(&self, rotation: DisplayRotation) { |
| self.resource.enqueue(cmd::set_display_rotation(self.id(), rotation as u32)) |
| } |
| |
| pub fn set_layer_stack(&self, layer_stack: &LayerStack) { |
| self.resource.enqueue(cmd::set_layer_stack(self.id(), layer_stack.id())) |
| } |
| } |
| |
| pub struct View { |
| resource: Resource, |
| } |
| |
| impl View { |
| pub fn new(session: SessionPtr, token: ViewToken, debug_name: Option<String>) -> View { |
| let args = ViewArgs { token: token, debug_name: debug_name }; |
| View { resource: Resource::new(session, ResourceArgs::View(args)) } |
| } |
| |
| /// Creates a new view using the ViewArgs3 resource which allows the user to |
| /// use view_refs. |
| pub fn new3( |
| session: SessionPtr, |
| token: ViewToken, |
| control_ref: ViewRefControl, |
| view_ref: ViewRef, |
| debug_name: Option<String>, |
| ) -> View { |
| let args = ViewArgs3 { token, control_ref, view_ref, debug_name }; |
| View { resource: Resource::new(session, ResourceArgs::View3(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(Node); |
| |
| impl ViewHolder { |
| pub fn new( |
| session: SessionPtr, |
| token: ViewHolderToken, |
| debug_name: Option<String>, |
| ) -> ViewHolder { |
| let args = ViewHolderArgs { token: token, debug_name: debug_name }; |
| ViewHolder(Node::new(Resource::new(session, ResourceArgs::ViewHolder(args)))) |
| } |
| |
| pub fn set_view_properties(&self, view_properties: ViewProperties) { |
| self.enqueue(cmd::set_view_properties(self.id(), view_properties)) |
| } |
| } |
| |
| impl Deref for ViewHolder { |
| type Target = Node; |
| |
| fn deref(&self) -> &Node { |
| &self.0 |
| } |
| } |
| |
| 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 ImagePipe2(Resource); |
| |
| impl ImagePipe2 { |
| pub fn new(session: SessionPtr, image_pipe_request: ServerEnd<ImagePipe2Marker>) -> ImagePipe2 { |
| let args = ImagePipe2Args { image_pipe_request }; |
| ImagePipe2(Resource::new(session, ResourceArgs::ImagePipe2(args))) |
| } |
| } |
| |
| impl Deref for ImagePipe2 { |
| type Target = Resource; |
| |
| fn deref(&self) -> &Resource { |
| &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 remove_child(&self, node: &Node) { |
| self.enqueue(cmd::detach(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 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())) |
| } |
| } |
| |
| 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() as usize |
| } |
| } |
| |
| 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 | PixelFormat::R8G8B8A8 => (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, |
| // TODO(fxbug.dev/69494): Remove this or explain why it's here. |
| #[allow(dead_code)] |
| 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); |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, fidl::endpoints::create_proxy_and_stream, fidl_fuchsia_ui_gfx, |
| fidl_fuchsia_ui_scenic, fuchsia_async as fasync, futures::prelude::*, |
| }; |
| |
| /// Returns `true` if the received session command matches the test expectation. |
| /// |
| /// If the received request is not a SessionRequest::Enqueue, the function will return |
| /// `false`. |
| /// |
| /// Parameters: |
| /// - `request`: The request the test case received. |
| /// - `check_command`: The function which is applied to the received request. |
| /// |
| /// Returns: |
| /// `true` iff the request is enqueuing a command, and that command passes the provided |
| /// check. |
| fn verify_session_command<F>( |
| request: fidl_fuchsia_ui_scenic::SessionRequest, |
| check_command: F, |
| ) -> bool |
| where |
| F: Fn(&fidl_fuchsia_ui_gfx::Command) -> bool, |
| { |
| match request { |
| fidl_fuchsia_ui_scenic::SessionRequest::Enqueue { cmds, control_handle: _ } => { |
| let cmd = cmds.first(); |
| match cmd { |
| Some(fidl_fuchsia_ui_scenic::Command::Gfx(gfx_command)) => { |
| assert!(check_command(gfx_command)); |
| true |
| } |
| _ => false, |
| } |
| } |
| _ => false, |
| } |
| } |
| |
| /// Verifies that the session's next resource id is given to a newly created layer. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_resource_id() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| let layer_id = session.lock().next_resource_id(); |
| |
| let _ = Layer::new(session.clone()); |
| |
| fasync::Task::spawn(async move { |
| let fut = session.lock().present(0); |
| let _ = fut.await; |
| }) |
| .detach(); |
| |
| if let Some(session_request) = session_server.try_next().await.unwrap() { |
| assert!(verify_session_command(session_request, |gfx_command| { |
| match gfx_command { |
| fidl_fuchsia_ui_gfx::Command::CreateResource(create_command) => { |
| // Verify that the layer that was created got the expected layer id. |
| create_command.id == layer_id |
| } |
| _ => false, |
| } |
| })); |
| } |
| } |
| |
| /// Print a more detailed message about the failure, and return false. |
| /// |
| /// Example: |
| /// fidl_fuchsia_ui_gfx::Command::$command_type(command_struct) => { |
| /// command_struct.$member_name == $expected_value |
| /// || fail!("{}.{} = '{}' (expected: '{}')", |
| /// stringify!($command_type), stringify!($member_name), |
| /// command_struct.$member_name, $expected_value) |
| /// } |
| /// unexpected_type => { |
| /// fail!("{} type is '{:?}' (expected '{}')", |
| /// stringify!($command), unexpected_type, stringify!($command_type)) |
| /// } |
| /// |
| /// Note: |
| /// * Do not end the macro call with a semicolon. The result is a bool "false". |
| /// * Note the "||" (or) expression in the first example returns true if the test passes, |
| /// and othewise executes the fail!() macro and returns false. |
| macro_rules! fail { |
| ( |
| $format_string:literal, |
| $($args:expr),* |
| ) => { |
| { |
| println!(concat!("FAILED({}:{}:{}): ", $format_string), |
| file!(), line!(), column!() $(,$args)*); |
| false |
| } |
| }; |
| } |
| |
| /// Tests that the appropriate resource type is created. |
| macro_rules! test_resource_creation { |
| ( |
| session: $session:expr, |
| session_server: $session_server:expr, |
| resource_type: $resource_type:ident |
| ) => { |
| let _ = $resource_type::new($session.clone()); |
| |
| fasync::Task::spawn(async move { |
| let fut = $session.lock().present(0); |
| let _ = fut.await; |
| }) |
| .detach(); |
| |
| while let Some(session_request) = $session_server.try_next().await.unwrap() { |
| let passed = verify_session_command(session_request, |gfx_command| { |
| match gfx_command { |
| fidl_fuchsia_ui_gfx::Command::CreateResource(create_command) => { |
| // Verify that the resource that was created got the expected resource |
| // id. |
| match create_command.resource { |
| fidl_fuchsia_ui_gfx::ResourceArgs::$resource_type(_resource) => { |
| true |
| } |
| _ => false, |
| } |
| } |
| _ => false, |
| } |
| }); |
| |
| if passed { |
| break; |
| } |
| } |
| }; |
| } |
| |
| /// Asserts that the incoming command is the type we expect. |
| macro_rules! assert_command_type { |
| ( |
| command: $command:expr, |
| command_type: $command_type:ident |
| ) => { |
| assert!({ |
| match $command { |
| fidl_fuchsia_ui_scenic::Command::Gfx(gfx_command) => match gfx_command { |
| fidl_fuchsia_ui_gfx::Command::$command_type(_) => true, |
| unexpected_type => fail!( |
| "{} type is '{:?}' (expected '{}')", |
| stringify!($command), |
| unexpected_type, |
| stringify!($command_type) |
| ), |
| }, |
| unexpected_command => fail!( |
| "{} = '{:?}' (expected fidl_fuchsia_ui_gfx::Command::Gfx)", |
| stringify!($command), |
| unexpected_command |
| ), |
| } |
| }) |
| }; |
| } |
| |
| /// Asserts that the incoming command is the type we expect. |
| /// |
| /// Example: |
| /// assert_command_value!(command: &commands[0], command_type: SetDisplayRotation, |
| /// rotation_degrees, 270.0)); |
| macro_rules! assert_command_value { |
| ( |
| command: $command:expr, |
| command_type: $command_type:ident, |
| $member_name:ident, |
| $expected_value:expr |
| ) => { |
| assert!({ |
| match $command { |
| fidl_fuchsia_ui_scenic::Command::Gfx(gfx_command) => match gfx_command { |
| fidl_fuchsia_ui_gfx::Command::$command_type(command_struct) => { |
| command_struct.$member_name == $expected_value |
| || fail!( |
| "{}.{} = '{:?}' (expected: '{:?}')", |
| stringify!($command_type), |
| stringify!($member_name), |
| command_struct.$member_name, |
| $expected_value |
| ) |
| } |
| unexpected_type => fail!( |
| "{} type is '{:?}' (expected '{}')", |
| stringify!($command), |
| unexpected_type, |
| stringify!($command_type) |
| ), |
| }, |
| unexpected_command => fail!( |
| "{} = '{:?}' (expected fidl_fuchsia_ui_gfx::Command::Gfx)", |
| stringify!($command), |
| unexpected_command |
| ), |
| } |
| }) |
| }; |
| } |
| |
| /// Verifies that a new resource is created for a layer. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_layer() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: Layer |
| ); |
| } |
| |
| /// Verifies that a new resource is created for a layer stack. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_layer_stack() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: LayerStack |
| ); |
| } |
| |
| /// Verifies that a new resource is created for a renderer. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_renderer() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: Renderer |
| ); |
| } |
| |
| /// Verifies that a new resource is created for a scene. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_scene() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: Scene |
| ); |
| } |
| |
| /// Verifies that a new resource is created for an ambient light. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_ambient() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: AmbientLight |
| ); |
| } |
| |
| /// Verifies that a new resource is created for a directional light. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_directional() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: DirectionalLight |
| ); |
| } |
| |
| /// Verifies that a new resource is created for a point light. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_point() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: PointLight |
| ); |
| } |
| |
| /// Verifies that a new resource is created for a display compositor. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_display_compositor() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| test_resource_creation!( |
| session: session, |
| session_server: session_server, |
| resource_type: DisplayCompositor |
| ); |
| } |
| |
| /// Verifies that adding a child to a node creates the correct command |
| #[fasync::run_singlethreaded(test)] |
| async fn test_add_child() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| let _ = session.lock(); |
| let parent_node = EntityNode::new(session.clone()); |
| let child_node = EntityNode::new(session.clone()); |
| parent_node.add_child(&child_node); |
| |
| fasync::Task::spawn(async move { |
| let fut = session.lock().present(0); |
| let _ = fut.await; |
| }) |
| .detach(); |
| |
| let mut commands = vec![]; |
| |
| if let Some(session_request) = session_server.try_next().await.unwrap() { |
| if let fidl_fuchsia_ui_scenic::SessionRequest::Enqueue { mut cmds, .. } = |
| session_request |
| { |
| commands.append(&mut cmds); |
| } |
| } |
| |
| assert_command_type!(command: &commands[0], command_type: CreateResource); |
| assert_command_type!(command: &commands[1], command_type: CreateResource); |
| assert_command_type!(command: &commands[2], command_type: AddChild); |
| } |
| |
| /// Verifies that removing a child from a node creates the correct command. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_remove_child() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| let _ = session.lock(); |
| let parent_node = EntityNode::new(session.clone()); |
| let child_node = EntityNode::new(session.clone()); |
| parent_node.add_child(&child_node); |
| parent_node.remove_child(&child_node); |
| |
| fasync::Task::spawn(async move { |
| let fut = session.lock().present(0); |
| let _ = fut.await; |
| }) |
| .detach(); |
| |
| let mut commands = vec![]; |
| |
| if let Some(session_request) = session_server.try_next().await.unwrap() { |
| if let fidl_fuchsia_ui_scenic::SessionRequest::Enqueue { mut cmds, .. } = |
| session_request |
| { |
| commands.append(&mut cmds); |
| } |
| } |
| |
| assert_command_type!(command: &commands[0], command_type: CreateResource); |
| assert_command_type!(command: &commands[1], command_type: CreateResource); |
| assert_command_type!(command: &commands[2], command_type: AddChild); |
| assert_command_type!(command: &commands[3], command_type: Detach); |
| } |
| |
| /// Verifies that setting display rotation by enum creates a command with the correct u32 value |
| #[fasync::run_singlethreaded(test)] |
| async fn test_set_display_rotation() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| let _ = session.lock(); |
| let compositor = DisplayCompositor::new(session.clone()); |
| compositor.set_display_rotation(DisplayRotation::By270Degrees); |
| compositor.set_display_rotation(DisplayRotation::By180Degrees); |
| compositor.set_display_rotation(DisplayRotation::By90Degrees); |
| compositor.set_display_rotation(DisplayRotation::None); |
| |
| fasync::Task::spawn(async move { |
| let fut = session.lock().present(0); |
| let _ = fut.await; |
| }) |
| .detach(); |
| |
| let mut commands = vec![]; |
| |
| if let Some(session_request) = session_server.try_next().await.unwrap() { |
| if let fidl_fuchsia_ui_scenic::SessionRequest::Enqueue { mut cmds, .. } = |
| session_request |
| { |
| commands.append(&mut cmds); |
| } |
| } |
| |
| assert_command_type!(command: &commands[0], command_type: CreateResource); |
| assert_command_value!(command: &commands[1], command_type: SetDisplayRotation, |
| rotation_degrees, 270); |
| assert_command_value!(command: &commands[2], command_type: SetDisplayRotation, |
| rotation_degrees, 180); |
| assert_command_value!(command: &commands[3], command_type: SetDisplayRotation, |
| rotation_degrees, 90); |
| assert_command_value!(command: &commands[4], command_type: SetDisplayRotation, |
| rotation_degrees, 0); |
| } |
| |
| /// Verifies that adding a child to a node creates the correct command |
| #[fasync::run_singlethreaded(test)] |
| async fn test_view_holder() { |
| let (session_proxy, mut session_server) = |
| create_proxy_and_stream::<fidl_fuchsia_ui_scenic::SessionMarker>() |
| .expect("Failed to create Session FIDL."); |
| |
| let session = Session::new(session_proxy); |
| let _ = session.lock(); |
| |
| let ViewTokenPair { view_token: _, view_holder_token } = ViewTokenPair::new().unwrap(); |
| let view_holder = ViewHolder::new(session.clone(), view_holder_token, None); |
| |
| let view_properties = fidl_fuchsia_ui_gfx::ViewProperties { |
| bounding_box: fidl_fuchsia_ui_gfx::BoundingBox { |
| min: fidl_fuchsia_ui_gfx::Vec3 { x: 100.0, y: 200.0, z: 300.0 }, |
| max: fidl_fuchsia_ui_gfx::Vec3 { x: 400.0, y: 500.0, z: 600.0 }, |
| }, |
| downward_input: false, |
| focus_change: true, |
| inset_from_min: fidl_fuchsia_ui_gfx::Vec3 { x: 10.0, y: 20.0, z: 30.0 }, |
| inset_from_max: fidl_fuchsia_ui_gfx::Vec3 { x: 40.0, y: 50.0, z: 60.0 }, |
| }; |
| view_holder.set_view_properties(view_properties); |
| |
| fasync::Task::spawn(async move { |
| let fut = session.lock().present(0); |
| let _ = fut.await; |
| }) |
| .detach(); |
| |
| let mut commands = vec![]; |
| |
| if let Some(session_request) = session_server.try_next().await.unwrap() { |
| if let fidl_fuchsia_ui_scenic::SessionRequest::Enqueue { mut cmds, .. } = |
| session_request |
| { |
| commands.append(&mut cmds); |
| } |
| } |
| |
| assert_command_type!(command: &commands[0], command_type: CreateResource); |
| assert_command_value!(command: &commands[1], command_type: SetViewProperties, |
| view_holder_id, view_holder.id()); |
| |
| if let fidl_fuchsia_ui_scenic::Command::Gfx(gfx_command) = &commands[1] { |
| if let fidl_fuchsia_ui_gfx::Command::SetViewProperties(command_struct) = gfx_command { |
| assert_eq!(command_struct.properties.bounding_box.min.x, 100.0); |
| assert_eq!(command_struct.properties.bounding_box.min.y, 200.0); |
| assert_eq!(command_struct.properties.bounding_box.min.z, 300.0); |
| assert_eq!(command_struct.properties.bounding_box.max.x, 400.0); |
| assert_eq!(command_struct.properties.bounding_box.max.y, 500.0); |
| assert_eq!(command_struct.properties.bounding_box.max.z, 600.0); |
| assert_eq!(command_struct.properties.downward_input, false); |
| assert_eq!(command_struct.properties.focus_change, true); |
| assert_eq!(command_struct.properties.inset_from_min.x, 10.0); |
| assert_eq!(command_struct.properties.inset_from_min.y, 20.0); |
| assert_eq!(command_struct.properties.inset_from_min.z, 30.0); |
| assert_eq!(command_struct.properties.inset_from_max.x, 40.0); |
| assert_eq!(command_struct.properties.inset_from_max.y, 50.0); |
| assert_eq!(command_struct.properties.inset_from_max.z, 60.0); |
| } |
| } |
| } |
| } |