blob: 1749c1b941a7c0904cce2ce575a2fdaa3af67744 [file] [log] [blame]
// 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_