app/viewer.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file viewer.h | ||
| 3 | /// @brief The interactive viewer: a window, or an offscreen image, showing the shell's panels | ||
| 4 | /// around the viewport, and the command line that opens it. | ||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "ernie/app/imgui_backend.h" | ||
| 8 | #include "ernie/app/ui.h" | ||
| 9 | #include "ernie/device/bindless.h" | ||
| 10 | #include "ernie/render/device_scene.h" | ||
| 11 | #include "ernie/render/rt/camera_pass.h" | ||
| 12 | |||
| 13 | #include <glm/vec2.hpp> | ||
| 14 | |||
| 15 | #include <chrono> | ||
| 16 | #include <cstdint> | ||
| 17 | #include <deque> | ||
| 18 | #include <filesystem> | ||
| 19 | #include <memory> | ||
| 20 | #include <optional> | ||
| 21 | #include <span> | ||
| 22 | #include <string> | ||
| 23 | #include <string_view> | ||
| 24 | |||
| 25 | namespace ernie::app { | ||
| 26 | |||
| 27 | 4 | struct ViewerOptions { | |
| 28 | std::string title = "Ernie"; | ||
| 29 | /// The window's or the offscreen image's size, in pixels. | ||
| 30 | glm::uvec2 size{1600, 900}; | ||
| 31 | /// Draw into an image without a window, for tests and machines without a display. | ||
| 32 | bool offscreen = false; | ||
| 33 | /// Frames to draw before `Run` returns; 0 runs until the window closes. | ||
| 34 | std::uint64_t frames = 0; | ||
| 35 | /// The device: its UUID in hex or part of its name; empty for the best. | ||
| 36 | std::string device; | ||
| 37 | /// The Khronos validation layer, when installed. | ||
| 38 | bool validation = false; | ||
| 39 | /// Wait for the display's refresh; off by default, so frames show what they cost. | ||
| 40 | bool vsync = false; | ||
| 41 | ShellOptions shell; | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// @brief The viewer on a scene. Each frame takes input, draws the shell's UI, traces the orbit | ||
| 45 | /// camera into the viewport (or clears it to the theme's colour on devices without ray queries), | ||
| 46 | /// draws the UI over it and presents it. Drive it on the thread that made it. | ||
| 47 | class Viewer { | ||
| 48 | public: | ||
| 49 | /// @param scene Read by the panels; must outlive the viewer. | ||
| 50 | /// @throws std::runtime_error without a display unless offscreen, DeviceError without a | ||
| 51 | /// device. | ||
| 52 | Viewer(const Scene& scene, const ViewerOptions& options = {}); | ||
| 53 | ~Viewer(); | ||
| 54 | Viewer(const Viewer&) = delete; | ||
| 55 | Viewer& operator=(const Viewer&) = delete; | ||
| 56 | |||
| 57 | /// @brief Draws frames until the window closes, the user quits or the frame count is reached. | ||
| 58 | void Run(); | ||
| 59 | |||
| 60 | 1 | std::uint64_t Frames() const { | |
| 61 | 1 | return frames_; | |
| 62 | } | ||
| 63 | 4 | Shell& GetShell() { | |
| 64 | 4 | return *shell_; | |
| 65 | } | ||
| 66 | 5 | DeviceContext& Context() { | |
| 67 | 5 | return *context_; | |
| 68 | } | ||
| 69 | /// @return The offscreen presenter, or null when presenting to a window. | ||
| 70 | 2 | const OffscreenPresenter* Offscreen() const { | |
| 71 | 2 | return dynamic_cast<const OffscreenPresenter*>(presenter_.get()); | |
| 72 | } | ||
| 73 | |||
| 74 | private: | ||
| 75 | /// @return Whether to keep going. | ||
| 76 | bool Step(); | ||
| 77 | /// @brief Sizes the viewport's image to the dock space's central node and draws it there, | ||
| 78 | /// behind the panels. | ||
| 79 | void ShowViewport(); | ||
| 80 | /// @brief Makes the viewport's image `pixels` in size, in the UI and the heap. | ||
| 81 | void ResizeViewport(glm::uvec2 pixels); | ||
| 82 | /// @brief Adds the pass filling the viewport: traced, or the theme's colour without ray | ||
| 83 | /// queries. | ||
| 84 | /// @return The viewport, filled. | ||
| 85 | graph::Image AddViewportPass(graph::Graph& graph); | ||
| 86 | /// @brief Adds the frame's own passes: rendering the viewport, then the UI over the target. | ||
| 87 | void AddPasses(graph::Graph& graph, ImDrawData& data, const FrameTarget& target); | ||
| 88 | |||
| 89 | ViewerOptions options_; | ||
| 90 | // Made in this order, destroyed in reverse. | ||
| 91 | ImGuiSession imgui_; | ||
| 92 | std::unique_ptr<DeviceContext> context_; | ||
| 93 | std::unique_ptr<Presenter> presenter_; | ||
| 94 | std::unique_ptr<ImGuiRenderer> renderer_; | ||
| 95 | std::unique_ptr<Shell> shell_; | ||
| 96 | std::unique_ptr<Arenas> arenas_; | ||
| 97 | std::unique_ptr<DeviceScene> deviceScene_; | ||
| 98 | /// The heap and pass tracing the viewport; null without ray queries. | ||
| 99 | std::unique_ptr<BindlessHeap> heap_; | ||
| 100 | std::unique_ptr<CameraPass> camera_; | ||
| 101 | /// The image the viewport is rendered into, the UI's texture of it, and its storage slot. | ||
| 102 | std::unique_ptr<Sampler> sampler_; | ||
| 103 | std::unique_ptr<DeviceImage> viewport_; | ||
| 104 | ImTextureID viewportTexture_ = 0; | ||
| 105 | BindlessIndex viewportImage_; | ||
| 106 | /// The frames the device may still be drawing, oldest first. | ||
| 107 | std::deque<Ticket> inFlight_; | ||
| 108 | std::uint64_t frames_ = 0; | ||
| 109 | std::chrono::steady_clock::time_point last_ = std::chrono::steady_clock::now(); | ||
| 110 | }; | ||
| 111 | |||
| 112 | /// @brief What the viewer's command line, `ernie [view] [SCENE.json] [options]`, asks for. | ||
| 113 | struct ViewCommand { | ||
| 114 | /// The scene file to open; none for an empty scene. | ||
| 115 | std::optional<std::filesystem::path> scene; | ||
| 116 | ViewerOptions viewer; | ||
| 117 | }; | ||
| 118 | |||
| 119 | /// @return The options the viewer's command line usage lists. | ||
| 120 | std::string_view ViewUsage(); | ||
| 121 | |||
| 122 | /// @brief Reads the viewer's arguments: an optional scene file and options. | ||
| 123 | /// @throws std::invalid_argument for an unknown option, a missing value or a second scene. | ||
| 124 | ViewCommand ParseViewCommand(std::span<const std::string_view> arguments); | ||
| 125 | |||
| 126 | /// @brief Opens the viewer on the command's scene and runs it until closed. | ||
| 127 | /// @return The process's exit status. | ||
| 128 | int RunView(const ViewCommand& command); | ||
| 129 | |||
| 130 | } // namespace ernie::app | ||
| 131 |