blob: 438ddfa95581baedaec92968f2c6afb3d33d5c2d [file] [log] [blame]
// Copyright 2017 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.
#include "garnet/lib/ui/gfx/resources/renderers/renderer.h"
#include <trace/event.h>
#include "lib/escher/impl/ssdo_sampler.h"
#include "lib/escher/renderer/renderer.h"
#include "lib/escher/scene/model.h"
#include "lib/escher/scene/stage.h"
#include "garnet/lib/ui/gfx/engine/session.h"
#include "garnet/lib/ui/gfx/resources/camera.h"
#include "garnet/lib/ui/gfx/resources/dump_visitor.h"
#include "garnet/lib/ui/gfx/resources/import.h"
#include "garnet/lib/ui/gfx/resources/material.h"
#include "garnet/lib/ui/gfx/resources/nodes/entity_node.h"
#include "garnet/lib/ui/gfx/resources/nodes/node.h"
#include "garnet/lib/ui/gfx/resources/nodes/opacity_node.h"
#include "garnet/lib/ui/gfx/resources/nodes/scene.h"
#include "garnet/lib/ui/gfx/resources/nodes/shape_node.h"
#include "garnet/lib/ui/gfx/resources/nodes/traversal.h"
#include "garnet/lib/ui/gfx/resources/shapes/circle_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/shape.h"
#include "garnet/lib/ui/gfx/resources/view.h"
#include "garnet/lib/ui/gfx/resources/view_holder.h"
namespace scenic_impl {
namespace gfx {
const ResourceTypeInfo Renderer::kTypeInfo = {ResourceType::kRenderer,
"Renderer"};
const uint32_t Renderer::kRequiredSwapchainPixelMultiple =
escher::impl::SsdoSampler::kSsdoAccelDownsampleFactor;
Renderer::Renderer(Session* session, ResourceId id)
: Resource(session, id, Renderer::kTypeInfo) {
escher::MaterialPtr default_material_ =
fxl::MakeRefCounted<escher::Material>();
default_material_->set_color(escher::vec3(0.f, 0.f, 0.f));
}
Renderer::~Renderer() = default;
std::vector<escher::Object> Renderer::CreateDisplayList(
const ScenePtr& scene, escher::vec2 screen_dimensions,
escher::BatchGpuUploader* uploader) {
TRACE_DURATION("gfx", "Renderer::CreateDisplayList");
VisitorContext visitor_context(default_material_, /* opacity= */ 1.0f,
disable_clipping_, uploader);
// Construct a display list from the tree.
Visitor v(std::move(visitor_context));
scene->Accept(&v);
return v.TakeDisplayList();
}
void Renderer::SetCamera(CameraPtr camera) { camera_ = std::move(camera); }
bool Renderer::SetShadowTechnique(
::fuchsia::ui::gfx::ShadowTechnique technique) {
shadow_technique_ = technique;
return true;
}
void Renderer::DisableClipping(bool disable_clipping) {
disable_clipping_ = disable_clipping;
}
Renderer::Visitor::Visitor(Renderer::VisitorContext context)
: context_(context) {}
std::vector<escher::Object> Renderer::Visitor::TakeDisplayList() {
return std::move(display_list_);
}
void Renderer::Visitor::Visit(Memory* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(Image* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(ImagePipe* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(Buffer* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(View* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(ViewHolder* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(EntityNode* r) { VisitNode(r); }
void Renderer::Visitor::Visit(OpacityNode* r) {
if (r->opacity() == 0) {
return;
}
float old_opacity = context_.opacity;
context_.opacity *= r->opacity();
VisitNode(r);
context_.opacity = old_opacity;
}
void Renderer::Visitor::VisitNode(Node* r) {
size_t previous_display_size = display_list_.size();
VisitAndMaybeClipNode(r);
bool view_is_rendering_element = display_list_.size() > previous_display_size;
if (r->view() && view_is_rendering_element) {
// TODO(SCN-1099) Add a test to ensure this signal isn't triggered when this
// view is not rendering.
r->view()->SignalRender();
}
}
std::vector<escher::Object> Renderer::Visitor::GenerateClippeeDisplayList(
Node* r) {
// Gather the escher::Objects corresponding to the children and imports.
VisitorContext clippee_context(context_);
Renderer::Visitor clippee_visitor(clippee_context);
ForEachChildAndImportFrontToBack(
*r, [&clippee_visitor](Node* node) { node->Accept(&clippee_visitor); });
return clippee_visitor.TakeDisplayList();
}
std::vector<escher::Object> Renderer::Visitor::GenerateClipperDisplayList(
Node* r) {
// Create a VisitorContext with no material for the clippers.
const escher::MaterialPtr kNoMaterial;
VisitorContext clipper_context(kNoMaterial, context_.opacity,
context_.disable_clipping,
context_.batch_gpu_uploader);
// The node's children and imports must be clipped by the
// Shapes/ShapeNodes amongst the node's parts. First gather the
// escher::Objects corresponding to these ShapeNodes.
Renderer::Visitor clipper_visitor(clipper_context);
ForEachPartFrontToBack(*r, [&clipper_visitor](Node* node) {
if (node->IsKindOf<ShapeNode>()) {
node->Accept(&clipper_visitor);
} else {
// TODO(MZ-167): accept non-ShapeNode parts. This might already work
// (i.e. it might be as simple as saying
// "part->Accept(&part_visitor)"), but this hasn't been tested.
FXL_LOG(WARNING) << "Renderer::Visitor::VisitNode(): Clipping only "
"supports ShapeNode parts.";
}
});
return clipper_visitor.TakeDisplayList();
}
void Renderer::Visitor::VisitAndMaybeClipNode(Node* r) {
// If not clipping, recursively visit all descendants in the normal fashion.
if (!r->clip_to_self() || context_.disable_clipping) {
ForEachDirectDescendantFrontToBack(
*r, [this](Node* node) { node->Accept(this); });
return;
}
// Check whether there's anything to clip.
auto clippees = GenerateClippeeDisplayList(r);
if (clippees.empty()) {
// Nothing to clip! Just draw the parts as usual.
ForEachPartFrontToBack(*r, [this](Node* node) { node->Accept(this); });
return;
}
// Gather the objects used to form the clip regions.
auto clippers = GenerateClipperDisplayList(r);
if (clippers.empty()) {
// The clip is empty so there's nothing to draw.
return;
}
// Some chicanery is required to draw in the order specified by
// ForEachDirectDescendantFrontToBack(). Namely, all clippers that are
// also visible (i.e. have a non-null material) need to be drawn twice:
// once as a clipper (with the material removed), and later as a clippee
// (with the material intact).
// TODO(MZ-176): are there some constraints that we can put on allowable
// elevations that would allow us to relax the draw-order constraint,
// and thereby not render the objects twice?
for (auto& obj : clippers) {
if (obj.material()) {
clippees.push_back(obj);
obj.set_material(escher::MaterialPtr());
}
}
// Create a new "clip object" from the display-lists generated by the
// two visitors above.
display_list_.push_back(
escher::Object(std::move(clippers), std::move(clippees)));
}
void Renderer::Visitor::Visit(Scene* r) { VisitNode(r); }
void Renderer::Visitor::Visit(Compositor* r) { FXL_DCHECK(false); }
void Renderer::Visitor::Visit(DisplayCompositor* r) { FXL_DCHECK(false); }
void Renderer::Visitor::Visit(LayerStack* r) { FXL_DCHECK(false); }
void Renderer::Visitor::Visit(Layer* r) { FXL_DCHECK(false); }
void Renderer::Visitor::Visit(ShapeNode* r) {
auto& shape = r->shape();
auto& material = r->material();
if (material) {
material->Accept(this);
}
if (shape) {
escher::MaterialPtr escher_material =
material ? material->escher_material() : context_.default_material;
if (escher_material && context_.opacity < 1) {
// When we want to support other material types (e.g. metallic shaders),
// we'll need to change this. If we want to support semitransparent
// textures and materials, we'll need more pervasive changes.
glm::vec4 color = escher_material->color();
color.a *= context_.opacity;
escher_material =
escher::Material::New(color, escher_material->texture());
escher_material->set_opaque(false);
}
display_list_.push_back(
shape->GenerateRenderObject(r->GetGlobalTransform(), escher_material));
}
// We don't need to call |VisitNode| because shape nodes don't have
// children or parts.
}
void Renderer::Visitor::Visit(CircleShape* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(RectangleShape* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(RoundedRectangleShape* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(MeshShape* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(Material* r) {
r->UpdateEscherMaterial(context_.batch_gpu_uploader);
}
void Renderer::Visitor::Visit(Import* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(Camera* r) {
// TODO: use camera's projection matrix.
Visit(r->scene().get());
}
void Renderer::Visitor::Visit(Renderer* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(Light* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(AmbientLight* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(DirectionalLight* r) { FXL_CHECK(false); }
void Renderer::Visitor::Visit(PointLight* r) { FXL_CHECK(false); }
} // namespace gfx
} // namespace scenic_impl