blob: a5adb5c5ca9d809269f0d952133fbeec046238c3 [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 "src/lib/fxl/logging.h"
#include "src/ui/lib/escher/debug/debug_rects.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/renderer.h"
#include "src/ui/lib/escher/renderer/uniform_binding.h"
namespace escher {
// |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.
class PaperRenderer final : public Renderer {
public:
static PaperRendererPtr New(EscherWeakPtr escher,
const PaperRendererConfig& config = {
.shadow_type = PaperRendererShadowType::kNone});
~PaperRenderer() override;
// Set configuration parameters which affect how the renderer will render
// subsequent frames.
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|.
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() {
FXL_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(ES-203) - 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);
// Draws a graph onto the screen using DrawDebugText and Draw Line calls. The graph corners are:
// (150, 100) (width - 150, 100)
//
// (150, height - 100) (width - 150, height - 100)
void DrawDebugGraph(std::string x_label, std::string y_label, DebugRects::Color lineColor);
// 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;
};
// Used to calculate the area of the debug graph that bars will be drawn in.
const static int32_t kHeightPadding = 100;
// Add TimeStamp to be graphed.
void AddDebugTimeStamp(TimeStamp ts);
private:
explicit PaperRenderer(EscherWeakPtr escher, const PaperRendererConfig& config);
PaperRenderer(const PaperRenderer&) = delete;
// 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;
};
// Stores all per-frame data in one place. See |FrameData| in |Renderer|
// for base-struct data.
struct PaperFrame : FrameData {
PaperFrame(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);
~PaperFrame() override;
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);
// Loops through debug_times_ array to draw data to the graph.
// Red render_time Blue random_value
// Yellow random_value Purple presentation_time
void GraphDebugData();
// Called when |config_.debug_frame_number| is true. Uses |debug_font_| to
// blit the current frame number to the output image.
void RenderFrameCounter();
PaperRendererConfig config_;
PaperDrawCallFactory draw_call_factory_;
PaperRenderQueue render_queue_;
PaperShapeCache shape_cache_;
PaperTransformStack transform_stack_;
std::unique_ptr<PaperFrame> frame_data_;
ShaderProgramPtr ambient_light_program_;
ShaderProgramPtr no_lighting_program_;
ShaderProgramPtr point_light_program_;
ShaderProgramPtr point_light_falloff_program_;
ShaderProgramPtr shadow_volume_geometry_program_;
ShaderProgramPtr shadow_volume_geometry_debug_program_;
ShaderProgramPtr shadow_volume_lighting_program_;
std::unique_ptr<DebugFont> debug_font_;
std::unique_ptr<DebugRects> debug_lines_;
// A list of TimeStamps where each cell represents the data we want to
// display on the graph for each frame.
std::vector<TimeStamp> debug_times_;
};
} // namespace escher
#endif // SRC_UI_LIB_ESCHER_PAPER_PAPER_RENDERER_H_