blob: 04599782aad2618019dc22f8ec6e97d449741b6a [file] [log] [blame]
// Copyright 2021 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.
use {
fidl::endpoints::*, fidl_fuchsia_ui_gfx as fgfx, fidl_fuchsia_ui_scenic as fscenic,
fidl_fuchsia_ui_views as fviews, fuchsia_async as fasync, fuchsia_scenic as scenic,
futures::StreamExt, log::debug, rand::prelude::SliceRandom, rand::rngs::SmallRng, rand::Rng,
std::sync::Arc,
};
pub const DISPLAY_WIDTH: u16 = 1024;
pub const DISPLAY_HEIGHT: u16 = 600;
pub const MIN_CIRCLE_RADIUS: u16 = 5;
pub const MAX_CIRCLE_RADIUS: u16 = 100;
fn create_view_holder(
session: scenic::SessionPtr,
view_holder_token: fviews::ViewHolderToken,
) -> scenic::ViewHolder {
let view_holder = scenic::ViewHolder::new(session, view_holder_token, None);
let view_properties = fgfx::ViewProperties {
bounding_box: fgfx::BoundingBox {
min: fgfx::Vec3 { x: 0.0, y: 0.0, z: 0.0 },
max: fgfx::Vec3 { x: DISPLAY_HEIGHT as f32, y: DISPLAY_WIDTH as f32, z: 0.0 },
},
inset_from_min: fgfx::Vec3 { x: 0.0, y: 0.0, z: 0.0 },
inset_from_max: fgfx::Vec3 { x: 0.0, y: 0.0, z: 0.0 },
focus_change: true,
downward_input: true,
};
view_holder.set_view_properties(view_properties);
view_holder
}
// In a new task, indefinitely call Session::Present2() for a given session
fn autopresent(session: scenic::SessionPtr) -> fasync::Task<()> {
fasync::Task::spawn(async move {
let mut stream = {
let session = session.lock();
session.take_event_stream()
};
// Call present for the first time
let future = {
let mut session = session.lock();
session.present2(0, 0)
};
future.await.unwrap();
// Wait for a frame to be presented
while let Some(Ok(_)) = stream.next().await {
// Then call present
let future = {
let mut session = session.lock();
session.present2(0, 0)
};
future.await.unwrap();
}
})
}
// In a new task, listen for session events and print them out
fn autolisten(listener: ServerEnd<fscenic::SessionListenerMarker>) -> fasync::Task<()> {
fasync::Task::spawn(async move {
let mut stream = listener.into_stream().unwrap();
while let Some(Ok(request)) = stream.next().await {
match request {
fscenic::SessionListenerRequest::OnScenicEvent { events, control_handle: _ } => {
for event in events {
match event {
fscenic::Event::Gfx(e) => debug!("Received GFX event: {:#?}", e),
fscenic::Event::Input(e) => debug!("Received input event: {:#?}", e),
_ => {}
}
}
}
fscenic::SessionListenerRequest::OnScenicError { error, control_handle: _ } => {
panic!("Unexpected scenic error: {}", error);
}
}
}
})
}
fn create_session(
scenic: Arc<fscenic::ScenicProxy>,
) -> (scenic::SessionPtr, fasync::Task<()>, fasync::Task<()>) {
let (session_proxy, session_server) = create_proxy::<fscenic::SessionMarker>().unwrap();
let (listener_client, listener_server) =
create_endpoints::<fscenic::SessionListenerMarker>().unwrap();
scenic
.create_session2(session_server, Some(listener_client), None)
.expect("Could not create new session");
let session = scenic::Session::new(session_proxy);
let present_task = autopresent(session.clone());
let listener_task = autolisten(listener_server);
(session, present_task, listener_task)
}
/// Contains all scenic elements needed to render a simple circle.
/// Describes the radius, color and translation of the circle.
pub struct Circle {
shape_node: scenic::ShapeNode,
_material: scenic::Material,
_circle: scenic::Circle,
}
impl Circle {
fn new(rng: &mut SmallRng, session: scenic::SessionPtr) -> Self {
// Create a circle with random radius and translation
let shape_node = scenic::ShapeNode::new(session.clone());
let radius = rng.gen_range(MIN_CIRCLE_RADIUS, MAX_CIRCLE_RADIUS);
let circle = scenic::Circle::new(session.clone(), radius as f32);
let material = scenic::Material::new(session.clone());
material.set_color(fgfx::ColorRgba { red: 255, blue: 0, green: 0, alpha: 255 });
shape_node.set_shape(&circle);
shape_node.set_material(&material);
let x = rng.gen_range(0, DISPLAY_WIDTH);
let y = rng.gen_range(0, DISPLAY_HEIGHT);
shape_node.set_translation(x as f32, y as f32, 0.0);
Self { shape_node, _material: material, _circle: circle }
}
}
/// Represents a single Session and all comprising elements.
///
/// The root session contains the basic elements needed to render a complete scene.
/// Child sessions contain a simple view that is attached to a parent session's viewholder.
pub enum Session {
Root {
scenic: Arc<fscenic::ScenicProxy>,
session: scenic::SessionPtr,
compositor: scenic::DisplayCompositor,
layer_stack: scenic::LayerStack,
layer: scenic::Layer,
renderer: scenic::Renderer,
camera: scenic::Camera,
light: scenic::AmbientLight,
scene: scenic::Scene,
circle: Circle,
child_sessions: Vec<(Session, scenic::ViewHolder)>,
_present_task: fasync::Task<()>,
_listener_task: fasync::Task<()>,
},
Child {
scenic: Arc<fscenic::ScenicProxy>,
session: scenic::SessionPtr,
view: scenic::View,
circle: Circle,
child_sessions: Vec<(Session, scenic::ViewHolder)>,
_present_task: fasync::Task<()>,
_listener_task: fasync::Task<()>,
},
}
impl Session {
// Setup the root session, including all elements needed to properly render the scene.
pub fn initialize_as_root(
rng: &mut SmallRng,
scenic: Arc<fscenic::ScenicProxy>,
) -> (Self, u32, scenic::SessionPtr) {
let (session, _present_task, _listener_task) = create_session(scenic.clone());
let scene = scenic::Scene::new(session.clone());
let light = scenic::AmbientLight::new(session.clone());
light.set_color(fgfx::ColorRgb { red: 0.0, green: 0.0, blue: 0.0 });
scene.add_ambient_light(&light);
let camera = scenic::Camera::new(session.clone(), &scene);
let renderer = scenic::Renderer::new(session.clone());
renderer.set_camera(&camera);
let layer = scenic::Layer::new(session.clone());
layer.set_size(DISPLAY_WIDTH as f32, DISPLAY_HEIGHT as f32);
layer.set_renderer(&renderer);
let layer_stack = scenic::LayerStack::new(session.clone());
layer_stack.add_layer(&layer);
let compositor = scenic::DisplayCompositor::new(session.clone());
compositor.set_layer_stack(&layer_stack);
let compositor_id = compositor.id();
let circle = Circle::new(rng, session.clone());
scene.add_child(&circle.shape_node);
let session_clone = session.clone();
let root = Self::Root {
scenic,
session,
compositor,
layer_stack,
layer,
renderer,
camera,
light,
scene,
circle,
child_sessions: vec![],
_present_task,
_listener_task,
};
(root, compositor_id, session_clone)
}
// Create the child session. Also return the viewholder token that can be used
// to connect this session's view to a parent's viewholder.
fn initialize_as_child(
rng: &mut SmallRng,
scenic: Arc<fscenic::ScenicProxy>,
) -> (Self, fviews::ViewHolderToken) {
let (session, _present_task, _listener_task) = create_session(scenic.clone());
let token_pair = scenic::ViewTokenPair::new().unwrap();
let ref_pair = scenic::ViewRefPair::new().unwrap();
let view = scenic::View::new3(
session.clone(),
token_pair.view_token,
ref_pair.control_ref,
ref_pair.view_ref,
None,
);
let circle = Circle::new(rng, session.clone());
view.add_child(&circle.shape_node);
let child = Self::Child {
scenic,
session,
view,
circle,
child_sessions: vec![],
_present_task,
_listener_task,
};
(child, token_pair.view_holder_token)
}
// Add a child session + viewholder to this session
pub fn add_child(&mut self, rng: &mut SmallRng) {
let scenic = match self {
Self::Root { scenic, .. } => scenic.clone(),
Self::Child { scenic, .. } => scenic.clone(),
};
let (child, view_holder_token) = Self::initialize_as_child(rng, scenic);
// Create the view holder and attach it to the parent
match self {
Self::Root { session, scene, child_sessions, .. } => {
let view_holder = create_view_holder(session.clone(), view_holder_token);
scene.add_child(&view_holder);
child_sessions.push((child, view_holder));
}
Self::Child { session, view, child_sessions, .. } => {
let view_holder = create_view_holder(session.clone(), view_holder_token);
view.add_child(&view_holder);
child_sessions.push((child, view_holder));
}
};
}
// Remove a random child session + viewholder from the scene graph
pub fn delete_child(&mut self, rng: &mut SmallRng) {
let child_sessions = match self {
Self::Root { child_sessions, .. } => child_sessions,
Self::Child { child_sessions, .. } => child_sessions,
};
let num_sessions = child_sessions.len();
let index = rng.gen_range(0, num_sessions);
let (_, holder) = child_sessions.remove(index);
holder.detach();
}
// true if the session has child sessions. false otherwise.
pub fn has_children(&self) -> bool {
let child_sessions = match self {
Self::Root { child_sessions, .. } => child_sessions,
Self::Child { child_sessions, .. } => child_sessions,
};
!child_sessions.is_empty()
}
// Select a random child session
pub fn get_random_child_mut(&mut self, rng: &mut SmallRng) -> &mut Session {
let child_sessions = match self {
Self::Root { child_sessions, .. } => child_sessions,
Self::Child { child_sessions, .. } => child_sessions,
};
let (session, _) = child_sessions.choose_mut(rng).unwrap();
session
}
}