// 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; using fuchsia.ui.gfx; // Client use Sessions to interact with a Mozart instance by enqueuing commands // that create or modify resources. interface Session { 1: 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 Mozart 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 Mozart 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 Mozart 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 Mozart 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, Mozart 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 Mozart 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(MZ-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? 2: Present(uint64 presentation_time, vector<handle<event>> acquire_fences, vector<handle<event>> release_fences) -> (fuchsia.images.PresentationInfo presentation_info); // TODO(MZ-422) Remove these methods from the FIDL; they should just be // exposed to View Manager directly using a C++ interface. 3: HitTest(uint32 node_id, fuchsia.ui.gfx.vec3 ray_origin, fuchsia.ui.gfx.vec3 ray_direction) -> (vector<fuchsia.ui.gfx.Hit>? hits); 4: HitTestDeviceRay(fuchsia.ui.gfx.vec3 ray_origin, fuchsia.ui.gfx.vec3 ray_direction) -> (vector<fuchsia.ui.gfx.Hit>? hits); // Set an optional debug name for the session. The debug name will be // output in things such as logging and trace events. 5: SetDebugName(string debug_name); }; // Listens for events which occur within the session. interface SessionListener { // Called when an error has occurred and the session will be torn down. 1: 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. 2: OnScenicEvent(vector<Event> events); };