blob: 638737dd85351302eceea3546a661e556c662304 [file] [log] [blame]
// Copyright 2019 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/engine/engine_renderer_visitor.h"
#include "garnet/lib/ui/gfx/resources/camera.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/nodes/view_node.h"
#include "garnet/lib/ui/gfx/resources/shapes/circle_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/mesh_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/rectangle_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/rounded_rectangle_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"
#include "src/lib/fxl/logging.h"
#include "src/ui/lib/escher/paper/paper_renderer.h"
namespace scenic_impl {
namespace gfx {
EngineRendererVisitor::EngineRendererVisitor(escher::PaperRenderer* renderer,
escher::BatchGpuUploader* gpu_uploader)
: renderer_(renderer), gpu_uploader_(gpu_uploader) {}
void EngineRendererVisitor::Visit(Memory* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(Image* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(ImagePipe* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(Buffer* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(View* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(ViewNode* r) {
const size_t previous_count = draw_call_count_;
const bool previous_should_render_debug_bounds = should_render_debug_bounds_;
if (auto view = r->GetView()) {
should_render_debug_bounds_ = view->should_render_bounding_box();
}
VisitNode(r);
bool view_is_rendering_element = draw_call_count_ > previous_count;
if (r->GetView() && view_is_rendering_element) {
// TODO(SCN-1099) Add a test to ensure this signal isn't triggered when this
// view is not rendering.
r->GetView()->SignalRender();
}
should_render_debug_bounds_ = previous_should_render_debug_bounds;
}
void EngineRendererVisitor::Visit(ViewHolder* r) {
escher::PaperTransformStack* transform_stack = renderer_->transform_stack();
transform_stack->PushTransform(static_cast<escher::mat4>(r->transform()));
transform_stack->AddClipPlanes(r->clip_planes());
// A view holder should render its bounds if either its embedding view has
// debug rendering turned on (which will mean should_render_debug_bounds_=true)
// or if its own view specifies that debug bounds should be rendered.
if (should_render_debug_bounds_ || (r->view() && r->view()->should_render_bounding_box())) {
auto bbox = r->GetLocalBoundingBox();
// Create material and submit draw call.
escher::PaperMaterialPtr escher_material = escher::Material::New(r->bounds_color());
escher_material->set_type(escher::Material::Type::kWireframe);
renderer_->DrawBoundingBox(bbox, escher_material, escher::PaperDrawableFlags());
++draw_call_count_;
}
ForEachDirectDescendantFrontToBack(*r, [this](Node* node) { node->Accept(this); });
transform_stack->Pop();
}
void EngineRendererVisitor::Visit(EntityNode* r) { VisitNode(r); }
void EngineRendererVisitor::Visit(OpacityNode* r) {
if (r->opacity() == 0) {
return;
}
float old_opacity = opacity_;
opacity_ *= r->opacity();
VisitNode(r);
opacity_ = old_opacity;
}
void EngineRendererVisitor::VisitNode(Node* r) {
escher::PaperTransformStack* transform_stack = renderer_->transform_stack();
transform_stack->PushTransform(static_cast<escher::mat4>(r->transform()));
transform_stack->AddClipPlanes(r->clip_planes());
ForEachDirectDescendantFrontToBack(*r, [this](Node* node) { node->Accept(this); });
transform_stack->Pop();
}
void EngineRendererVisitor::Visit(Scene* r) { VisitNode(r); }
void EngineRendererVisitor::Visit(Compositor* r) { FXL_DCHECK(false); }
void EngineRendererVisitor::Visit(DisplayCompositor* r) { FXL_DCHECK(false); }
void EngineRendererVisitor::Visit(LayerStack* r) { FXL_DCHECK(false); }
void EngineRendererVisitor::Visit(Layer* r) { FXL_DCHECK(false); }
void EngineRendererVisitor::Visit(ShapeNode* r) {
// We don't need to call |VisitNode| because shape nodes don't have children.
FXL_DCHECK(r->children().empty());
auto& shape = r->shape();
auto& material = r->material();
if (!material || !shape)
return;
material->Accept(this);
escher::MaterialPtr escher_material = material->escher_material();
FXL_DCHECK(escher_material);
if (opacity_ < 1.f) {
// 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 *= opacity_;
escher_material = escher::Material::New(color, escher_material->texture());
escher_material->set_type(escher::Material::Type::kTranslucent);
}
escher::PaperTransformStack* transform_stack = renderer_->transform_stack();
transform_stack->PushTransform(static_cast<escher::mat4>(r->transform()));
escher::PaperDrawableFlags flags{};
if (shape->IsKindOf<RoundedRectangleShape>()) {
auto rect = static_cast<RoundedRectangleShape*>(shape.get());
renderer_->DrawRoundedRect(rect->spec(), escher_material, flags);
} else if (shape->IsKindOf<RectangleShape>()) {
auto rect = static_cast<RectangleShape*>(shape.get());
renderer_->DrawRect(rect->width(), rect->height(), escher_material, flags);
} else if (shape->IsKindOf<CircleShape>()) {
auto circle = static_cast<CircleShape*>(shape.get());
// Only draw the circle if its radius is greater than epsilon.
if (circle->radius() > escher::kEpsilon) {
renderer_->DrawCircle(circle->radius(), escher_material, flags);
}
} else if (shape->IsKindOf<MeshShape>()) {
auto mesh_shape = static_cast<MeshShape*>(shape.get());
renderer_->DrawMesh(mesh_shape->escher_mesh(), escher_material, flags);
} else {
FXL_LOG(ERROR) << "Unsupported shape type encountered.";
}
transform_stack->Pop();
++draw_call_count_;
}
void EngineRendererVisitor::Visit(CircleShape* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(RectangleShape* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(RoundedRectangleShape* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(MeshShape* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(Material* r) { r->UpdateEscherMaterial(gpu_uploader_); }
void EngineRendererVisitor::Visit(Import* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(Camera* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(Renderer* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(Light* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(AmbientLight* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(DirectionalLight* r) { FXL_CHECK(false); }
void EngineRendererVisitor::Visit(PointLight* r) { FXL_CHECK(false); }
} // namespace gfx
} // namespace scenic_impl