| // 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); |
| }; |