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