blob: ca5cceaad10435d4f925cc28ef50cec022656ae4 [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.
library fuchsia.ui.scenic;
using fuchsia.images;
/// Client use Sessions to interact with a Scenic instance by enqueuing commands
/// that create or modify resources.
protocol Session {
Enqueue(vector<Command> cmds);
/// Present all previously enqueued operations. In order to pipeline the
/// preparation of the resources required to render the scene, two lists of
/// fences (implemented as events) are passed.
///
/// SCHEDULING PRESENTATION
///
/// `presentation_time` specifies the time on or after which the
/// client would like the enqueued operations should take visible effect
/// (light up pixels on the screen), expressed in nanoseconds in the
/// `CLOCK_MONOTONIC` timebase. Desired presentation times must be
/// monotonically non-decreasing.
///
/// Using a desired presentation time in the present or past (such as 0)
/// schedules enqueued operations to take visible effect as soon as possible
/// (during the next frame to be prepared).
///
/// Using a desired presentation time in the future schedules the enqueued
/// operations to take visible effect as closely as possible to or after
/// the stated time (but no earlier).
///
/// Each rendered frame has a target presentation time. Before rendering
/// a frame, the scene manager applies all enqueued operations associated
/// with all prior calls to `Present()` whose desired presentation time
/// is on or before the frame's target presentation time.
///
/// The `Present()` method does not return until the scene manager begins
/// preparing the first frame which includes its presented content.
/// Upon return, the `PresentationInfo` provides timing information for the
/// frame which includes the presented content.
///
/// To present new content on each successive frame, wait for `Present()`
/// to return before calling `Present()` again with content for the next
/// frame.
///
/// It is also possible to enqueue and present successive frames of content
/// all at once with increasing desired presentation times, incrementing by
/// `PresentationInfo.presentation_interval` for each one.
///
/// Animation updates are also coordinated in terms of presentation time.
///
// TODO(jeffbrown): Defining presentation time in terms of `CLOCK_MONOTONIC`
// simplifies synchronization across subsystems but it might be too simple.
// We should consider using a synthetic timebase and describing its relation
// to other clocks separately. That would make it possible to present
// content (animations, media, and UI) in "slow mode" simply by varying the
// timing relation, assuming clients play along.
//
/// SYNCHRONIZATION
///
/// `acquire_fences` are used by Scenic to wait until all of the session's
/// resources are ready to render (or to allow downstream components, such as
/// the Vulkan driver, to wait for these resources).
///
/// For example, Fuchsia's Vulkan driver allows an zx::event to be obtained
/// from a VkSemaphore. This allows a Scenic client to submit a Vulkan command
/// buffer to generate images/meshes/etc., and instructing Vulkan to signal a
/// VkSemaphore when it is done. By inserting the zx::event corresponding to
/// this semaphore into `acquire_fences`, the client allows Scenic to submit work
/// to the Vulkan driver without waiting on the CPU for the event to be
/// signalled.
///
/// `release_fences` is a list of events that will be signalled by Scenic when
/// the updated session state has been fully committed: future frames will be
/// rendered using this state, and all frames generated using previous session
/// states have been fully-rendered and presented to the display.
///
/// Together, `acquire_fences` and `release_fences` are intended to allow clients
/// to implement strategies such as double-buffering. For example, a client
/// might do the following in the Scenic subsystem:
/// 1) create two Image with resource IDs #1 and #2.
/// 2) create two Materials with resource IDs #3 and #4, which respectively
/// use Images #1 and #2 as their texture.
/// 3) create a tree of Nodes and attach them to the scene.
/// 4) set one of the nodes above, say #5, to use Material #3.
/// 5) submit a Vulkan command-buffer which renders into Image #1, and
/// will signal a VkSemaphore.
/// 6) call Present() with one acquire-fence (obtained from the VkSemaphore
/// above) and one newly-created release-fence.
///
/// After the steps above, Scenic will use the committed session state to render
/// frames whenever necessary. When the client wants to display something
/// different than Image #1, it would do something similar to steps 4) to 6):
/// 7) set Node #5 to use Material #4.
/// 8) submit a Vulkan command-buffer which renders into Image #1, and
/// will signal a VkSemaphore.
/// 9) call Present() with one acquire-fence (obtained from the VkSemaphore
/// above) and one newly-created release-fence.
///
/// Finally, to continually draw new content, the client could repeat steps
/// 4) to 9), with one important difference: step 5) must wait on the event
/// signalled by step 9). Otherwise, it might render into Image #1 while that
/// image is still being used by Scenic to render a frame. Similarly, step 8)
/// must wait on the event signalled by step 6).
///
/// The scenario described above uses one acquire-fence and one release-fence,
/// but it is easy to imagine cases that require more. For example, in addition
/// to using Vulkan to render into Images #1 and #2, the client might also
/// upload other resources to Vulkan on a different VkQueue, which would
/// would signal a separate semaphore, and therefore require an additional
/// acquire-fence.
///
/// Note: `acquire_fences` and `release_fences` are only necessary to synchronize
/// access to memory (and other external resources). Any modification to
/// resources made via the Session API are automatically synchronized.
///
// TODO(SCN-400): document invariants that apply to `presentation_info`. Is it
// strong enough to guarantee that receiving the response means that all
// previously-enqueued Commands have been applied? Or does it need to be stronger,
// e.g. that all frames based on previous presentations are completely done,
// and subsequent frames will be rendered based on the most recent presented
// content?
Present(uint64 presentation_time,
vector<handle<event>> acquire_fences, vector<handle<event>> release_fences)
-> (fuchsia.images.PresentationInfo presentation_info);
/// Set an optional debug name for the session. The debug name will be
/// output in things such as logging and trace events.
SetDebugName(string debug_name);
};
/// Listens for events which occur within the session.
protocol SessionListener {
/// Called when an error has occurred and the session will be torn down.
OnScenicError(string error);
/// Called to deliver a batch of one or more events to the listener.
/// Use `SetEventMaskCmd` to enable event delivery for a resource.
OnScenicEvent(vector<Event> events);
};