| // 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. |
| |
| #ifndef SRC_UI_LIB_ESCHER_PAPER_PAPER_RENDERER_H_ |
| #define SRC_UI_LIB_ESCHER_PAPER_PAPER_RENDERER_H_ |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include "src/ui/lib/escher/debug/debug_rects.h" |
| #include "src/ui/lib/escher/escher.h" |
| #include "src/ui/lib/escher/paper/paper_draw_call_factory.h" |
| #include "src/ui/lib/escher/paper/paper_drawable.h" |
| #include "src/ui/lib/escher/paper/paper_light.h" |
| #include "src/ui/lib/escher/paper/paper_readme.h" |
| #include "src/ui/lib/escher/paper/paper_render_queue.h" |
| #include "src/ui/lib/escher/paper/paper_renderer_config.h" |
| #include "src/ui/lib/escher/paper/paper_shape_cache.h" |
| #include "src/ui/lib/escher/paper/paper_transform_stack.h" |
| #include "src/ui/lib/escher/renderer/uniform_binding.h" |
| #include "src/ui/lib/escher/vk/texture.h" |
| |
| namespace escher { |
| |
| namespace test { |
| class PaperRendererTest; |
| } |
| |
| // |PaperRenderer| provides a convenient and flexible interface for rendering |
| // shapes in a 3D space, as required by Scenic. Clients achieve this primarily |
| // by passing instances of |PaperDrawable| to the |Draw()| method, using either |
| // pre-existing drawable types or their own subclasses. For convenience, other |
| // drawing methods are provided, such as |DrawCircle()|. |
| // |
| // These draw methods are legal only between |BeginFrame()| and |EndFrame()|. |
| // Respectively, these two methods prepare the renderer to render a frame, and |
| // generate the Vulkan commands which actually perform the rendering. |
| // |
| // All other public methods must *not* be called between |BeginFrame()| and |
| // |EndFrame()|. For example, |SetConfig()| can be used to choose a different |
| // shadow algorithm; changing this during the frame would cause incompatibility |
| // between the |PaperDrawCalls| previously and subsequently enqueued by the |
| // |PaperDrawCallFactory|. |
| // |
| // Implementation details follow... |
| // |
| // |PaperRenderer| is responsible for coordinating its sub-components: |
| // - |PaperDrawCallFactory| |
| // - |PaperShapeCache| |
| // - |PaperRenderQueue| |
| // See their class comments for details. |
| // |
| // Clients call |SetConfig()| to specify the coordination policies that will be |
| // used to render subsequent frames. When the config changes, the renderer |
| // applies the appropriate changes to its sub-components. |
| // |
| // When |BeginFrame()| is called, each sub-component is made ready to render the |
| // new frame. This depends on both the policies specified by |SetConfig()|, as |
| // well as the |PaperScene|, |Camera|, and |output_image| parameters. Together, |
| // these determine how: |
| // - shader data is encoded in the draw calls built by |PaperDrawCallFactory| |
| // - tessellated meshes are post-processed before they are cached/uploaded |
| // ... and so forth. |
| // |
| // During |EndFrame()| the renderer first builds |RenderPassInfo| descriptions |
| // of the Vulkan render passes necessary to render the scene. During each of |
| // these render passes, the renderer directs the render-queue to iterate over |
| // its draw calls and emit Vulkan commands into a |CommandBuffer|. This is |
| // controlled by two parameters passed to the queue: |
| // - |PaperRenderQueueFlags|, to control iteration over draw calls. |
| // - |PaperRenderQueueContext|, used by draw calls to emit Vulkan commands. |
| // TODO: Consider removing fxl::RefCountedThreadSafe. |
| class PaperRenderer final : public fxl::RefCountedThreadSafe<PaperRenderer> { |
| public: |
| static PaperRendererPtr New(EscherWeakPtr escher, |
| const PaperRendererConfig& config = { |
| .shadow_type = PaperRendererShadowType::kNone}); |
| ~PaperRenderer(); |
| |
| const VulkanContext& vulkan_context() { return context_; } |
| |
| Escher* escher() const { return escher_.get(); } |
| EscherWeakPtr GetEscherWeakPtr() { return escher_; } |
| |
| // Set configuration parameters which affect how the renderer will render subsequent frames. Must |
| // not be called during a frame, i.e. between |BeginFrame()| and |EndFrame()|. |
| void SetConfig(const PaperRendererConfig& config); |
| const PaperRendererConfig& config() const { return config_; } |
| |
| // Does the renderer support the specified shadow type? |
| bool SupportsShadowType(PaperRendererShadowType shadow_type) const; |
| |
| // Configures the renderer to render a frame into |output_image|. The |
| // renderer configures its sub-components to render the frame based on the |
| // |scene| and |camera| parameters, along with the configuration params |
| // previously set by |SetConfig()|. |
| // |
| // |PaperScene| describes aspects of the scene that affect the appearance of |
| // scene object (e.g. lighting parameters), but does not provide the list of |
| // scene objects to be rendered. To render the scene, clients should follow |
| // these steps: |
| // - |BeginFrame()| |
| // - |Draw()| each object in the scene. |
| // - |FinalizeFrame()| |
| // - |EndFrame()| emits the Vulkan commands that actually render the scene. |
| // |
| // Multiple cameras are supported, each rendering into its own viewport. |
| // However, the position of the first camera is the one used for depth-sorting |
| // the scene contents. For use-cases such as stereo rendering this is not a |
| // problem, however there can be problems with e.g. translucent objects if two |
| // cameras have dramatically different positions. |
| void BeginFrame(const FramePtr& frame, std::shared_ptr<BatchGpuUploader> uploader, |
| const PaperScenePtr& scene, const std::vector<Camera>& cameras, |
| const escher::ImagePtr& output_image); |
| |
| // After calling |FinalizeFrame()|: |
| // - No more upload requests will be made for this frame. Therefore, it is safe for the |
| // client to call |BatchGpuUploader::Submit()| on the uploader that was passed to |
| // |BeginFrame()|. |
| // - It is illegal to make any additional draw calls. |
| void FinalizeFrame(); |
| |
| // See |BeginFrame()|. After telling the renderer to draw the scene content, |
| // |EndFrame()| emits commands into a Vulkan command buffer. Submitting this |
| // command buffer causes the scene to be rendered into |output_image|. |
| // |
| // The layout of |output_image| should be initialized to its swapchain layout |
| // (or scheduled to be initialized by the time we submit te commands) before |
| // we call this method. |
| void EndFrame(const std::vector<SemaphorePtr>& upload_wait_semaphores); |
| |
| void EndFrame(SemaphorePtr upload_wait_semaphore) { |
| EndFrame(std::vector{std::move(upload_wait_semaphore)}); |
| } |
| |
| // The following methods may only be used during an unfinalized frame, i.e. between |
| // calls to |BeginFrame()| and |FinalizeFrame()|. |
| |
| // Return the transform stack, which affects the transform and clipping that |
| // is applied to subsequently-drawn |PaperDrawables|. |
| PaperTransformStack* transform_stack() { |
| FX_DCHECK(frame_data_) << "transform_stack only accessible during frame."; |
| return &transform_stack_; |
| } |
| |
| // Invokes DrawInScene() on the drawable object to generate and enqueue the |
| // draw-calls that be transformed into Vulkan commands during EndFrame(). |
| void Draw(PaperDrawable* drawable, PaperDrawableFlags flags = {}); |
| |
| // Draw predefined shapes: circles, rectangles, and rounded-rectangles. |
| // Generates and enqueues draw-calls that will emit Vulkan commands during |
| // EndFrame(). |
| void DrawCircle(float radius, const PaperMaterialPtr& material, PaperDrawableFlags flags = {}); |
| void DrawRect(vec2 min, vec2 max, const PaperMaterialPtr& material, |
| PaperDrawableFlags flags = {}); |
| |
| // Convenience function for the above DrawRect function that takes in the width/ height |
| // of the rect and centers it at (0,0). |
| void DrawRect(float width, float height, const PaperMaterialPtr& material, |
| PaperDrawableFlags flags = {}); |
| |
| void DrawRoundedRect(const RoundedRectSpec& spec, const PaperMaterialPtr& material, |
| PaperDrawableFlags flags = {}); |
| void DrawBoundingBox(const BoundingBox& box, const PaperMaterialPtr& material, |
| PaperDrawableFlags flags = {}); |
| void DrawMesh(const MeshPtr& mesh, const PaperMaterialPtr& material, |
| PaperDrawableFlags flags = {}); |
| |
| // TODO(fxbug.dev/7292) - We will remove this once PaperDrawCallFactory becomes |
| // injectable. We should never have to access this directly from the |
| // renderer - it should be completely opaque. |
| PaperDrawCallFactory* draw_call_factory() { return &draw_call_factory_; } |
| |
| // Draws debug text on top of output image. |
| void DrawDebugText(std::string text, vk::Offset2D offset, int32_t scale); |
| |
| // Draws vertical line to the output image. The entire line will be to the right of |x_coord|. |
| void DrawVLine(DebugRects::Color kColor, uint32_t x_coord, int32_t y_start, uint32_t y_end, |
| uint32_t thickness); |
| |
| // Draws horizontal line to the output image. The entire line will be below |y_coord|. |
| void DrawHLine(DebugRects::Color kColor, int32_t y_coord, int32_t x_start, uint32_t x_end, |
| int32_t thickness); |
| |
| // Corresponds to FrameTimings::Timestamps and will be used to calculate values to graph. |
| struct Timestamp { |
| int16_t latch_point; |
| int16_t update_done; |
| int16_t render_start; |
| int16_t render_done; |
| int16_t target_present; |
| int16_t actual_present; |
| }; |
| |
| // Utility function to warm up the pipeline/render-pass caches before any frames are rendered, |
| // in order to avoid janking on the first frame that a particular config is used. |
| static void WarmPipelineAndRenderPassCaches(Escher* escher, const PaperRendererConfig& config, |
| vk::Format output_format, |
| vk::ImageLayout output_swapchain_layout, |
| const std::vector<SamplerPtr>& immutable_samplers, |
| bool use_protected_memory); |
| |
| // Compute the sum total memory commitment of all transient depth-stencil and MSAA images. Only |
| // count those images which are intended to be transient. |
| vk::DeviceSize GetTransientImageMemoryCommitment(); |
| |
| private: |
| friend class escher::test::PaperRendererTest; |
| explicit PaperRenderer(EscherWeakPtr escher, const PaperRendererConfig& config); |
| |
| // Store relevant info from cameras passed to BeginFrame(). |
| struct CameraData { |
| UniformBinding binding; |
| vk::Rect2D rect; |
| vk::Viewport viewport; |
| uint32_t eye_index; // For PaperShaderPushConstants. |
| }; |
| |
| // Store relevant info about text to draw to the output image. |
| struct TextData { |
| std::string text; |
| vk::Offset2D offset; |
| int32_t scale; |
| }; |
| |
| // Store relevant info about lines to draw to the output image. |
| struct LineData { |
| DebugRects::Color kColor; |
| vk::Rect2D rect; |
| }; |
| |
| // Basic struct for the data a renderer needs to render a given |
| // frame. Data that is reusable amongst different renderer |
| // subclasses are stored here. Each renderer can also extend this |
| // struct to include any additional data they may need. |
| struct FrameData { |
| FrameData(const FramePtr& frame, std::shared_ptr<BatchGpuUploader> gpu_uploader, |
| const PaperScenePtr& scene, const ImagePtr& output_image, |
| std::pair<TexturePtr, TexturePtr> depth_and_msaa_textures, |
| const std::vector<Camera>& cameras); |
| ~FrameData(); |
| FramePtr frame; |
| ImagePtr output_image; |
| TexturePtr depth_texture; |
| TexturePtr msaa_texture; |
| std::shared_ptr<BatchGpuUploader> gpu_uploader; |
| PaperScenePtr scene; |
| size_t num_lights; |
| std::vector<CameraData> cameras; |
| std::vector<TextData> texts; |
| std::vector<LineData> lines; |
| |
| // UniformBindings returned by PaperDrawCallFactory::BeginFrame(). These |
| // contain camera and lighting parameters that are shared between draw |
| // calls. The contents are opaque to the PaperRenderer, who trusts that |
| // the PaperDrawCallFactory will generate DrawCalls that are compatible with |
| // these UniformBindings. |
| std::vector<UniformBinding> scene_uniform_bindings; |
| |
| bool scene_finalized = false; |
| }; |
| |
| // Called during EndFrame(). |
| void BindSceneAndCameraUniforms(uint32_t camera_index); |
| void GenerateCommandsForNoShadows(uint32_t camera_index); |
| void GenerateCommandsForShadowVolumes(uint32_t camera_index); |
| |
| // Called to write text onto screen |
| void GenerateDebugCommands(CommandBuffer* cmd_buf); |
| |
| // Called when |config_.debug_frame_number| is true. Uses |debug_font_| to |
| // blit the current frame number to the output image. |
| void RenderFrameCounter(); |
| |
| // Returns true if the material is valid and supported by the Escher device. |
| bool SupportsMaterial(const PaperMaterialPtr& material); |
| |
| const EscherWeakPtr escher_; |
| const VulkanContext context_; |
| PaperRendererConfig config_; |
| |
| bool supports_transient_attachments_ = false; |
| bool supports_protected_transient_attachments_ = false; |
| |
| PaperDrawCallFactory draw_call_factory_; |
| PaperRenderQueue render_queue_; |
| PaperShapeCache shape_cache_; |
| PaperTransformStack transform_stack_; |
| |
| std::unique_ptr<FrameData> frame_data_; |
| |
| std::vector<TexturePtr> depth_buffers_; |
| std::vector<TexturePtr> msaa_buffers_; |
| |
| std::unique_ptr<DebugFont> debug_font_; |
| std::unique_ptr<DebugRects> debug_lines_; |
| }; |
| |
| } // namespace escher |
| |
| #endif // SRC_UI_LIB_ESCHER_PAPER_PAPER_RENDERER_H_ |