<commit_before>// Copyright 2013 The Flutter 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 "flutter/flow/scene_update_context.h"

#include "flutter/flow/layers/layer.h"
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/fml/trace_event.h"

namespace flutter {

// Helper function to generate clip planes for a scenic::EntityNode.
static void SetEntityNodeClipPlanes(scenic::EntityNode* entity_node,
                                    const SkRect& bounds) {
  const float top = bounds.top();
  const float bottom = bounds.bottom();
  const float left = bounds.left();
  const float right = bounds.right();

  // We will generate 4 oriented planes, one for each edge of the bounding rect.
  std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
  clip_planes.resize(4);

  // Top plane.
  clip_planes[0].dist = top;
  clip_planes[0].dir.x = 0.f;
  clip_planes[0].dir.y = 1.f;
  clip_planes[0].dir.z = 0.f;

  // Bottom plane.
  clip_planes[1].dist = -bottom;
  clip_planes[1].dir.x = 0.f;
  clip_planes[1].dir.y = -1.f;
  clip_planes[1].dir.z = 0.f;

  // Left plane.
  clip_planes[2].dist = left;
  clip_planes[2].dir.x = 1.f;
  clip_planes[2].dir.y = 0.f;
  clip_planes[2].dir.z = 0.f;

  // Right plane.
  clip_planes[3].dist = -right;
  clip_planes[3].dir.x = -1.f;
  clip_planes[3].dir.y = 0.f;
  clip_planes[3].dir.z = 0.f;

  entity_node->SetClipPlanes(std::move(clip_planes));
}

SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
                                       SurfaceProducer* surface_producer)
    : session_(session), surface_producer_(surface_producer) {
  FML_DCHECK(surface_producer_ != nullptr);
}

void SceneUpdateContext::CreateFrame(
    std::unique_ptr<scenic::EntityNode> entity_node,
    std::unique_ptr<scenic::ShapeNode> shape_node,
    const SkRRect& rrect,
    SkColor color,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer) {
  // Frames always clip their children.
  SetEntityNodeClipPlanes(entity_node.get(), rrect.getBounds());
  // TODO(SCN-1274): AddPart() and SetClip() will be deleted.
  entity_node->SetClip(0u, true /* clip to self */);

  // We don't need a shape if the frame is zero size.
  if (rrect.isEmpty())
    return;

  // Add a part which represents the frame's geometry for clipping purposes
  // and possibly for its texture.
  // TODO(SCN-137): Need to be able to express the radii as vectors.
  SkRect shape_bounds = rrect.getBounds();
  scenic::RoundedRectangle shape(
      session_,                                      // session
      rrect.width(),                                 // width
      rrect.height(),                                // height
      rrect.radii(SkRRect::kUpperLeft_Corner).x(),   // top_left_radius
      rrect.radii(SkRRect::kUpperRight_Corner).x(),  // top_right_radius
      rrect.radii(SkRRect::kLowerRight_Corner).x(),  // bottom_right_radius
      rrect.radii(SkRRect::kLowerLeft_Corner).x()    // bottom_left_radius
  );
  shape_node->SetShape(shape);
  shape_node->SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
                             shape_bounds.height() * 0.5f + shape_bounds.top(),
                             0.f);

  // Check whether the painted layers will be visible.
  if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds))
    paint_layers.clear();

  // Check whether a solid color will suffice.
  if (paint_layers.empty()) {
    SetShapeColor(*shape_node, color);
    return;
  }

  // Apply current metrics and transformation scale factors.
  const float scale_x = ScaleX();
  const float scale_y = ScaleY();

  // Apply a texture to the whole shape.
  SetShapeTextureOrColor(*shape_node, color, scale_x, scale_y, shape_bounds,
                         std::move(paint_layers), layer,
                         std::move(entity_node));
}

void SceneUpdateContext::SetShapeTextureOrColor(
    scenic::ShapeNode& shape_node,
    SkColor color,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    std::unique_ptr<scenic::EntityNode> entity_node) {
  scenic::Image* image = GenerateImageIfNeeded(
      color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer,
      std::move(entity_node));
  if (image != nullptr) {
    scenic::Material material(session_);
    material.SetTexture(*image);
    shape_node.SetMaterial(material);
    return;
  }

  SetShapeColor(shape_node, color);
}

void SceneUpdateContext::SetShapeColor(scenic::ShapeNode& shape_node,
                                       SkColor color) {
  if (SkColorGetA(color) == 0)
    return;

  scenic::Material material(session_);
  material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
                    SkColorGetA(color));
  shape_node.SetMaterial(material);
}

scenic::Image* SceneUpdateContext::GenerateImageIfNeeded(
    SkColor color,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    std::unique_ptr<scenic::EntityNode> entity_node) {
  // Bail if there's nothing to paint.
  if (paint_layers.empty())
    return nullptr;

  // Bail if the physical bounds are empty after rounding.
  SkISize physical_size = SkISize::Make(paint_bounds.width() * scale_x,
                                        paint_bounds.height() * scale_y);
  if (physical_size.isEmpty())
    return nullptr;

  // Acquire a surface from the surface producer and register the paint tasks.
  std::unique_ptr<SurfaceProducerSurface> surface =
      surface_producer_->ProduceSurface(
          physical_size,
          LayerRasterCacheKey(
              // Root frame has a nullptr layer
              layer ? layer->unique_id() : 0, Matrix()),
          std::move(entity_node));

  if (!surface) {
    FML_LOG(ERROR) << "Could not acquire a surface from the surface producer "
                      "of size: "
                   << physical_size.width() << "x" << physical_size.height();
    return nullptr;
  }

  auto image = surface->GetImage();

  // Enqueue the paint task.
  paint_tasks_.push_back({.surface = std::move(surface),
                          .left = paint_bounds.left(),
                          .top = paint_bounds.top(),
                          .scale_x = scale_x,
                          .scale_y = scale_y,
                          .background_color = color,
                          .layers = std::move(paint_layers)});
  return image;
}

std::vector<
    std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>
SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) {
  TRACE_EVENT0("flutter", "SceneUpdateContext::ExecutePaintTasks");
  std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces_to_submit;
  for (auto& task : paint_tasks_) {
    FML_DCHECK(task.surface);
    SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas();
    Layer::PaintContext context = {canvas,
                                   canvas,
                                   frame.gr_context(),
                                   nullptr,
                                   frame.context().raster_time(),
                                   frame.context().ui_time(),
                                   frame.context().texture_registry(),
                                   &frame.context().raster_cache(),
                                   false};
    canvas->restoreToCount(1);
    canvas->save();
    canvas->clear(task.background_color);
    canvas->scale(task.scale_x, task.scale_y);
    canvas->translate(-task.left, -task.top);
    for (Layer* layer : task.layers) {
      layer->Paint(context);
    }
    surfaces_to_submit.emplace_back(std::move(task.surface));
  }
  paint_tasks_.clear();
  return surfaces_to_submit;
}

SceneUpdateContext::Entity::Entity(SceneUpdateContext& context)
    : context_(context), previous_entity_(context.top_entity_) {
  entity_node_ptr_ = std::make_unique<scenic::EntityNode>(context.session());
  shape_node_ptr_ = std::make_unique<scenic::ShapeNode>(context.session());
  // TODO(SCN-1274): AddPart() and SetClip() will be deleted.
  entity_node_ptr_->AddPart(*shape_node_ptr_);
  if (previous_entity_)
    previous_entity_->entity_node_ptr_->AddChild(*entity_node_ptr_);
  context.top_entity_ = this;
}

SceneUpdateContext::Entity::~Entity() {
  FML_DCHECK(context_.top_entity_ == this);
  context_.top_entity_ = previous_entity_;
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         const SkMatrix& transform)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  if (!transform.isIdentity()) {
    // TODO(SCN-192): The perspective and shear components in the matrix
    // are not handled correctly.
    MatrixDecomposition decomposition(transform);
    if (decomposition.IsValid()) {
      entity_node().SetTranslation(decomposition.translation().x(),  //
                                   decomposition.translation().y(),  //
                                   -decomposition.translation().z()  //
      );

      entity_node().SetScale(decomposition.scale().x(),  //
                             decomposition.scale().y(),  //
                             decomposition.scale().z()   //
      );
      context.top_scale_x_ *= decomposition.scale().x();
      context.top_scale_y_ *= decomposition.scale().y();

      entity_node().SetRotation(decomposition.rotation().fData[0],  //
                                decomposition.rotation().fData[1],  //
                                decomposition.rotation().fData[2],  //
                                decomposition.rotation().fData[3]   //
      );
    }
  }
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         float scale_x,
                                         float scale_y,
                                         float scale_z)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) {
    entity_node().SetScale(scale_x, scale_y, scale_z);
    context.top_scale_x_ *= scale_x;
    context.top_scale_y_ *= scale_y;
  }
}

SceneUpdateContext::Transform::~Transform() {
  context().top_scale_x_ = previous_scale_x_;
  context().top_scale_y_ = previous_scale_y_;
}

SceneUpdateContext::Frame::Frame(SceneUpdateContext& context,
                                 const SkRRect& rrect,
                                 SkColor color,
                                 float local_elevation,
                                 float world_elevation,
                                 float depth,
                                 Layer* layer)
    : Entity(context),
      rrect_(rrect),
      color_(color),
      paint_bounds_(SkRect::MakeEmpty()),
      layer_(layer) {
  if (local_elevation != 0.0) {
    if (depth > -1 && world_elevation >= depth) {
      // TODO(mklim): Deal with bounds overflow correctly.
      FML_LOG(ERROR) << "Elevation " << world_elevation << " is outside of "
                     << depth;
    }
    entity_node().SetTranslation(0.f, 0.f, -local_elevation);
  }
}

SceneUpdateContext::Frame::~Frame() {
  context().CreateFrame(std::move(entity_node_ptr()),
                        std::move(shape_node_ptr()), rrect_, color_,
                        paint_bounds_, std::move(paint_layers_), layer_);
}

void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) {
  FML_DCHECK(layer->needs_painting());
  paint_layers_.push_back(layer);
  paint_bounds_.join(layer->paint_bounds());
}

SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
                               scenic::Shape& shape,
                               const SkRect& shape_bounds)
    : Entity(context) {
  shape_node().SetShape(shape);
  shape_node().SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
                              shape_bounds.height() * 0.5f + shape_bounds.top(),
                              0.f);
  entity_node().SetClip(0u, true /* clip to self */);

  SetEntityNodeClipPlanes(&entity_node(), shape_bounds);
}

}  // namespace flutter
<commit_msg>Clamp when overflowing z bounds (#9402)<commit_after>// Copyright 2013 The Flutter 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 "flutter/flow/scene_update_context.h"

#include "flutter/flow/layers/layer.h"
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/fml/trace_event.h"

namespace flutter {

// Helper function to generate clip planes for a scenic::EntityNode.
static void SetEntityNodeClipPlanes(scenic::EntityNode* entity_node,
                                    const SkRect& bounds) {
  const float top = bounds.top();
  const float bottom = bounds.bottom();
  const float left = bounds.left();
  const float right = bounds.right();

  // We will generate 4 oriented planes, one for each edge of the bounding rect.
  std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
  clip_planes.resize(4);

  // Top plane.
  clip_planes[0].dist = top;
  clip_planes[0].dir.x = 0.f;
  clip_planes[0].dir.y = 1.f;
  clip_planes[0].dir.z = 0.f;

  // Bottom plane.
  clip_planes[1].dist = -bottom;
  clip_planes[1].dir.x = 0.f;
  clip_planes[1].dir.y = -1.f;
  clip_planes[1].dir.z = 0.f;

  // Left plane.
  clip_planes[2].dist = left;
  clip_planes[2].dir.x = 1.f;
  clip_planes[2].dir.y = 0.f;
  clip_planes[2].dir.z = 0.f;

  // Right plane.
  clip_planes[3].dist = -right;
  clip_planes[3].dir.x = -1.f;
  clip_planes[3].dir.y = 0.f;
  clip_planes[3].dir.z = 0.f;

  entity_node->SetClipPlanes(std::move(clip_planes));
}

SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
                                       SurfaceProducer* surface_producer)
    : session_(session), surface_producer_(surface_producer) {
  FML_DCHECK(surface_producer_ != nullptr);
}

void SceneUpdateContext::CreateFrame(
    std::unique_ptr<scenic::EntityNode> entity_node,
    std::unique_ptr<scenic::ShapeNode> shape_node,
    const SkRRect& rrect,
    SkColor color,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer) {
  // Frames always clip their children.
  SetEntityNodeClipPlanes(entity_node.get(), rrect.getBounds());
  // TODO(SCN-1274): AddPart() and SetClip() will be deleted.
  entity_node->SetClip(0u, true /* clip to self */);

  // We don't need a shape if the frame is zero size.
  if (rrect.isEmpty())
    return;

  // Add a part which represents the frame's geometry for clipping purposes
  // and possibly for its texture.
  // TODO(SCN-137): Need to be able to express the radii as vectors.
  SkRect shape_bounds = rrect.getBounds();
  scenic::RoundedRectangle shape(
      session_,                                      // session
      rrect.width(),                                 // width
      rrect.height(),                                // height
      rrect.radii(SkRRect::kUpperLeft_Corner).x(),   // top_left_radius
      rrect.radii(SkRRect::kUpperRight_Corner).x(),  // top_right_radius
      rrect.radii(SkRRect::kLowerRight_Corner).x(),  // bottom_right_radius
      rrect.radii(SkRRect::kLowerLeft_Corner).x()    // bottom_left_radius
  );
  shape_node->SetShape(shape);
  shape_node->SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
                             shape_bounds.height() * 0.5f + shape_bounds.top(),
                             0.f);

  // Check whether the painted layers will be visible.
  if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds))
    paint_layers.clear();

  // Check whether a solid color will suffice.
  if (paint_layers.empty()) {
    SetShapeColor(*shape_node, color);
    return;
  }

  // Apply current metrics and transformation scale factors.
  const float scale_x = ScaleX();
  const float scale_y = ScaleY();

  // Apply a texture to the whole shape.
  SetShapeTextureOrColor(*shape_node, color, scale_x, scale_y, shape_bounds,
                         std::move(paint_layers), layer,
                         std::move(entity_node));
}

void SceneUpdateContext::SetShapeTextureOrColor(
    scenic::ShapeNode& shape_node,
    SkColor color,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    std::unique_ptr<scenic::EntityNode> entity_node) {
  scenic::Image* image = GenerateImageIfNeeded(
      color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer,
      std::move(entity_node));
  if (image != nullptr) {
    scenic::Material material(session_);
    material.SetTexture(*image);
    shape_node.SetMaterial(material);
    return;
  }

  SetShapeColor(shape_node, color);
}

void SceneUpdateContext::SetShapeColor(scenic::ShapeNode& shape_node,
                                       SkColor color) {
  if (SkColorGetA(color) == 0)
    return;

  scenic::Material material(session_);
  material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
                    SkColorGetA(color));
  shape_node.SetMaterial(material);
}

scenic::Image* SceneUpdateContext::GenerateImageIfNeeded(
    SkColor color,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    std::unique_ptr<scenic::EntityNode> entity_node) {
  // Bail if there's nothing to paint.
  if (paint_layers.empty())
    return nullptr;

  // Bail if the physical bounds are empty after rounding.
  SkISize physical_size = SkISize::Make(paint_bounds.width() * scale_x,
                                        paint_bounds.height() * scale_y);
  if (physical_size.isEmpty())
    return nullptr;

  // Acquire a surface from the surface producer and register the paint tasks.
  std::unique_ptr<SurfaceProducerSurface> surface =
      surface_producer_->ProduceSurface(
          physical_size,
          LayerRasterCacheKey(
              // Root frame has a nullptr layer
              layer ? layer->unique_id() : 0, Matrix()),
          std::move(entity_node));

  if (!surface) {
    FML_LOG(ERROR) << "Could not acquire a surface from the surface producer "
                      "of size: "
                   << physical_size.width() << "x" << physical_size.height();
    return nullptr;
  }

  auto image = surface->GetImage();

  // Enqueue the paint task.
  paint_tasks_.push_back({.surface = std::move(surface),
                          .left = paint_bounds.left(),
                          .top = paint_bounds.top(),
                          .scale_x = scale_x,
                          .scale_y = scale_y,
                          .background_color = color,
                          .layers = std::move(paint_layers)});
  return image;
}

std::vector<
    std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>
SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) {
  TRACE_EVENT0("flutter", "SceneUpdateContext::ExecutePaintTasks");
  std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces_to_submit;
  for (auto& task : paint_tasks_) {
    FML_DCHECK(task.surface);
    SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas();
    Layer::PaintContext context = {canvas,
                                   canvas,
                                   frame.gr_context(),
                                   nullptr,
                                   frame.context().raster_time(),
                                   frame.context().ui_time(),
                                   frame.context().texture_registry(),
                                   &frame.context().raster_cache(),
                                   false};
    canvas->restoreToCount(1);
    canvas->save();
    canvas->clear(task.background_color);
    canvas->scale(task.scale_x, task.scale_y);
    canvas->translate(-task.left, -task.top);
    for (Layer* layer : task.layers) {
      layer->Paint(context);
    }
    surfaces_to_submit.emplace_back(std::move(task.surface));
  }
  paint_tasks_.clear();
  return surfaces_to_submit;
}

SceneUpdateContext::Entity::Entity(SceneUpdateContext& context)
    : context_(context), previous_entity_(context.top_entity_) {
  entity_node_ptr_ = std::make_unique<scenic::EntityNode>(context.session());
  shape_node_ptr_ = std::make_unique<scenic::ShapeNode>(context.session());
  // TODO(SCN-1274): AddPart() and SetClip() will be deleted.
  entity_node_ptr_->AddPart(*shape_node_ptr_);
  if (previous_entity_)
    previous_entity_->entity_node_ptr_->AddChild(*entity_node_ptr_);
  context.top_entity_ = this;
}

SceneUpdateContext::Entity::~Entity() {
  FML_DCHECK(context_.top_entity_ == this);
  context_.top_entity_ = previous_entity_;
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         const SkMatrix& transform)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  if (!transform.isIdentity()) {
    // TODO(SCN-192): The perspective and shear components in the matrix
    // are not handled correctly.
    MatrixDecomposition decomposition(transform);
    if (decomposition.IsValid()) {
      entity_node().SetTranslation(decomposition.translation().x(),  //
                                   decomposition.translation().y(),  //
                                   -decomposition.translation().z()  //
      );

      entity_node().SetScale(decomposition.scale().x(),  //
                             decomposition.scale().y(),  //
                             decomposition.scale().z()   //
      );
      context.top_scale_x_ *= decomposition.scale().x();
      context.top_scale_y_ *= decomposition.scale().y();

      entity_node().SetRotation(decomposition.rotation().fData[0],  //
                                decomposition.rotation().fData[1],  //
                                decomposition.rotation().fData[2],  //
                                decomposition.rotation().fData[3]   //
      );
    }
  }
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         float scale_x,
                                         float scale_y,
                                         float scale_z)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) {
    entity_node().SetScale(scale_x, scale_y, scale_z);
    context.top_scale_x_ *= scale_x;
    context.top_scale_y_ *= scale_y;
  }
}

SceneUpdateContext::Transform::~Transform() {
  context().top_scale_x_ = previous_scale_x_;
  context().top_scale_y_ = previous_scale_y_;
}

SceneUpdateContext::Frame::Frame(SceneUpdateContext& context,
                                 const SkRRect& rrect,
                                 SkColor color,
                                 float local_elevation,
                                 float world_elevation,
                                 float depth,
                                 Layer* layer)
    : Entity(context),
      rrect_(rrect),
      color_(color),
      paint_bounds_(SkRect::MakeEmpty()),
      layer_(layer) {
  if (depth > -1 && world_elevation > depth) {
    // TODO(mklim): Deal with bounds overflow more elegantly. We'd like to be
    // able to have developers specify the behavior here to alternatives besides
    // clamping, like normalization on some arbitrary curve.

    // Clamp the local z coordinate at our max bound. Take into account the
    // parent z position here to fix clamping in cases where the child is
    // overflowing because of its parents.
    const float parent_elevation = world_elevation - local_elevation;
    local_elevation = depth - parent_elevation;
  }
  if (local_elevation != 0.0) {
    entity_node().SetTranslation(0.f, 0.f, -local_elevation);
  }
}

SceneUpdateContext::Frame::~Frame() {
  context().CreateFrame(std::move(entity_node_ptr()),
                        std::move(shape_node_ptr()), rrect_, color_,
                        paint_bounds_, std::move(paint_layers_), layer_);
}

void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) {
  FML_DCHECK(layer->needs_painting());
  paint_layers_.push_back(layer);
  paint_bounds_.join(layer->paint_bounds());
}

SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
                               scenic::Shape& shape,
                               const SkRect& shape_bounds)
    : Entity(context) {
  shape_node().SetShape(shape);
  shape_node().SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
                              shape_bounds.height() * 0.5f + shape_bounds.top(),
                              0.f);
  entity_node().SetClip(0u, true /* clip to self */);

  SetEntityNodeClipPlanes(&entity_node(), shape_bounds);
}

}  // namespace flutter
<|endoftext|>


<commit_before>/*
    Copyright (C) 2000-2001 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "csgeom/math3d.h"
#include "csgeom/box.h"
#include "nullmesh.h"
#include "iengine/movable.h"
#include "iengine/rview.h"
#include "ivideo/graph3d.h"
#include "ivideo/graph2d.h"
#include "ivideo/material.h"
#include "ivideo/vbufmgr.h"
#include "iengine/material.h"
#include "iengine/camera.h"
#include "igeom/clip2d.h"
#include "iengine/engine.h"
#include "iengine/light.h"
#include "iutil/objreg.h"
#include "qsqrt.h"

CS_IMPLEMENT_PLUGIN

SCF_IMPLEMENT_IBASE (csNullmeshMeshObject)
  SCF_IMPLEMENTS_INTERFACE (iMeshObject)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iMeshObjectFactory)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iObjectModel)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iNullMeshState)
SCF_IMPLEMENT_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObject::MeshObjectFactory)
  SCF_IMPLEMENTS_INTERFACE (iMeshObjectFactory)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObject::ObjectModel)
  SCF_IMPLEMENTS_INTERFACE (iObjectModel)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObject::NullMeshState)
  SCF_IMPLEMENTS_INTERFACE (iNullMeshState)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

csNullmeshMeshObject::csNullmeshMeshObject (iMeshObjectFactory* factory)
{
  SCF_CONSTRUCT_IBASE (0);
  SCF_CONSTRUCT_EMBEDDED_IBASE (scfiMeshObjectFactory);
  SCF_CONSTRUCT_EMBEDDED_IBASE (scfiObjectModel);
  SCF_CONSTRUCT_EMBEDDED_IBASE (scfiNullMeshState);
  csNullmeshMeshObject::factory = factory;
  logparent = 0;
  vis_cb = 0;
  radius = .1;
}

csNullmeshMeshObject::~csNullmeshMeshObject ()
{
  if (vis_cb) vis_cb->DecRef ();
}

bool csNullmeshMeshObject::DrawTest (iRenderView* rview, iMovable* movable)
{
  iCamera* camera = rview->GetCamera ();

  // First create the transformation from object to camera space directly:
  //   W = Mow * O - Vow;
  //   C = Mwc * (W - Vwc)
  // ->
  //   C = Mwc * (Mow * O - Vow - Vwc)
  //   C = Mwc * Mow * O - Mwc * (Vow + Vwc)
  csReversibleTransform tr_o2c = camera->GetTransform ();
  if (!movable->IsFullTransformIdentity ())
    tr_o2c /= movable->GetFullTransform ();

  csSphere sphere;
  sphere.SetCenter (csVector3 (0, 0, 0));
  sphere.SetRadius (radius);
  int clip_portal, clip_plane, clip_z_plane;
  if (rview->ClipBSphere (tr_o2c, sphere, clip_portal, clip_plane,
  	clip_z_plane) == false)
    return false;
  return true;
}

void csNullmeshMeshObject::SetRadius (float radius)
{
  csNullmeshMeshObject::radius = radius;
  box.Set (-radius, -radius, -radius, radius, radius, radius);
  scfiObjectModel.ShapeChanged ();
}

void csNullmeshMeshObject::SetBoundingBox (const csBox3& box)
{
  csNullmeshMeshObject::box = box;
  radius = qsqrt (csSquaredDist::PointPoint (box.Max (), box.Min ())) / 2.0;
  scfiObjectModel.ShapeChanged ();
}

void csNullmeshMeshObject::UpdateLighting (iLight**, int, iMovable*)
{
  return;
}

bool csNullmeshMeshObject::Draw (iRenderView* rview, iMovable* /*movable*/,
	csZBufMode /*mode*/)
{
  if (vis_cb) if (!vis_cb->BeforeDrawing (this, rview)) return false;
  return true;
}

void csNullmeshMeshObject::GetObjectBoundingBox (csBox3& bbox, int /*type*/)
{
  bbox = box;
}

bool csNullmeshMeshObject::HitBeamOutline (const csVector3& /*start*/,
  const csVector3& /*end*/, csVector3& /*isect*/, float* /*pr*/)
{
  // @@@ TODO
  return false;
}

bool csNullmeshMeshObject::HitBeamObject (const csVector3& /*start*/,
  const csVector3& /*end*/, csVector3& /*isect*/, float* /*pr*/)
{
  // @@@ TODO
  return false;
}

void csNullmeshMeshObject::GetRadius (csVector3& rad, csVector3& cent)
{
  rad.Set (radius);
  cent.Set (box.GetCenter ());
}

csPtr<iMeshObject> csNullmeshMeshObject::MeshObjectFactory::NewInstance ()
{
  csNullmeshMeshObject* cm = new csNullmeshMeshObject (
  	(iMeshObjectFactory*)this);
  csRef<iMeshObject> im (SCF_QUERY_INTERFACE (cm, iMeshObject));
  cm->DecRef ();
  return csPtr<iMeshObject> (im);
}

//----------------------------------------------------------------------

SCF_IMPLEMENT_IBASE (csNullmeshMeshObjectType)
  SCF_IMPLEMENTS_INTERFACE (iMeshObjectType)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iComponent)
SCF_IMPLEMENT_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObjectType::eiComponent)
  SCF_IMPLEMENTS_INTERFACE (iComponent)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

SCF_IMPLEMENT_FACTORY (csNullmeshMeshObjectType)

csNullmeshMeshObjectType::csNullmeshMeshObjectType (iBase* pParent)
{
  SCF_CONSTRUCT_IBASE (pParent);
  SCF_CONSTRUCT_EMBEDDED_IBASE(scfiComponent);
}

csNullmeshMeshObjectType::~csNullmeshMeshObjectType ()
{
}

csPtr<iMeshObjectFactory> csNullmeshMeshObjectType::NewFactory ()
{
  csNullmeshMeshObject* cm = new csNullmeshMeshObject (0);
  csRef<iMeshObjectFactory> ifact (
  	SCF_QUERY_INTERFACE (cm, iMeshObjectFactory));
  cm->DecRef ();
  return csPtr<iMeshObjectFactory> (ifact);
}

<commit_msg>Nullmesh is now initialized with a legal (albeit very small) bounding box.<commit_after>/*
    Copyright (C) 2000-2001 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "csgeom/math3d.h"
#include "csgeom/box.h"
#include "nullmesh.h"
#include "iengine/movable.h"
#include "iengine/rview.h"
#include "ivideo/graph3d.h"
#include "ivideo/graph2d.h"
#include "ivideo/material.h"
#include "ivideo/vbufmgr.h"
#include "iengine/material.h"
#include "iengine/camera.h"
#include "igeom/clip2d.h"
#include "iengine/engine.h"
#include "iengine/light.h"
#include "iutil/objreg.h"
#include "qsqrt.h"

CS_IMPLEMENT_PLUGIN

SCF_IMPLEMENT_IBASE (csNullmeshMeshObject)
  SCF_IMPLEMENTS_INTERFACE (iMeshObject)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iMeshObjectFactory)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iObjectModel)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iNullMeshState)
SCF_IMPLEMENT_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObject::MeshObjectFactory)
  SCF_IMPLEMENTS_INTERFACE (iMeshObjectFactory)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObject::ObjectModel)
  SCF_IMPLEMENTS_INTERFACE (iObjectModel)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObject::NullMeshState)
  SCF_IMPLEMENTS_INTERFACE (iNullMeshState)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

csNullmeshMeshObject::csNullmeshMeshObject (iMeshObjectFactory* factory)
{
  SCF_CONSTRUCT_IBASE (0);
  SCF_CONSTRUCT_EMBEDDED_IBASE (scfiMeshObjectFactory);
  SCF_CONSTRUCT_EMBEDDED_IBASE (scfiObjectModel);
  SCF_CONSTRUCT_EMBEDDED_IBASE (scfiNullMeshState);
  csNullmeshMeshObject::factory = factory;
  logparent = 0;
  vis_cb = 0;
  radius = .0001;
  box.Set (-radius, -radius, -radius, radius, radius, radius);
}

csNullmeshMeshObject::~csNullmeshMeshObject ()
{
  if (vis_cb) vis_cb->DecRef ();
}

bool csNullmeshMeshObject::DrawTest (iRenderView* rview, iMovable* movable)
{
  iCamera* camera = rview->GetCamera ();

  // First create the transformation from object to camera space directly:
  //   W = Mow * O - Vow;
  //   C = Mwc * (W - Vwc)
  // ->
  //   C = Mwc * (Mow * O - Vow - Vwc)
  //   C = Mwc * Mow * O - Mwc * (Vow + Vwc)
  csReversibleTransform tr_o2c = camera->GetTransform ();
  if (!movable->IsFullTransformIdentity ())
    tr_o2c /= movable->GetFullTransform ();

  csSphere sphere;
  sphere.SetCenter (csVector3 (0, 0, 0));
  sphere.SetRadius (radius);
  int clip_portal, clip_plane, clip_z_plane;
  if (rview->ClipBSphere (tr_o2c, sphere, clip_portal, clip_plane,
  	clip_z_plane) == false)
    return false;
  return true;
}

void csNullmeshMeshObject::SetRadius (float radius)
{
  csNullmeshMeshObject::radius = radius;
  box.Set (-radius, -radius, -radius, radius, radius, radius);
  scfiObjectModel.ShapeChanged ();
}

void csNullmeshMeshObject::SetBoundingBox (const csBox3& box)
{
  csNullmeshMeshObject::box = box;
  radius = qsqrt (csSquaredDist::PointPoint (box.Max (), box.Min ())) / 2.0;
  scfiObjectModel.ShapeChanged ();
}

void csNullmeshMeshObject::UpdateLighting (iLight**, int, iMovable*)
{
  return;
}

bool csNullmeshMeshObject::Draw (iRenderView* rview, iMovable* /*movable*/,
	csZBufMode /*mode*/)
{
  if (vis_cb) if (!vis_cb->BeforeDrawing (this, rview)) return false;
  return true;
}

void csNullmeshMeshObject::GetObjectBoundingBox (csBox3& bbox, int /*type*/)
{
  bbox = box;
}

bool csNullmeshMeshObject::HitBeamOutline (const csVector3& /*start*/,
  const csVector3& /*end*/, csVector3& /*isect*/, float* /*pr*/)
{
  // @@@ TODO
  return false;
}

bool csNullmeshMeshObject::HitBeamObject (const csVector3& /*start*/,
  const csVector3& /*end*/, csVector3& /*isect*/, float* /*pr*/)
{
  // @@@ TODO
  return false;
}

void csNullmeshMeshObject::GetRadius (csVector3& rad, csVector3& cent)
{
  rad.Set (radius);
  cent.Set (box.GetCenter ());
}

csPtr<iMeshObject> csNullmeshMeshObject::MeshObjectFactory::NewInstance ()
{
  csNullmeshMeshObject* cm = new csNullmeshMeshObject (
  	(iMeshObjectFactory*)this);
  csRef<iMeshObject> im (SCF_QUERY_INTERFACE (cm, iMeshObject));
  cm->DecRef ();
  return csPtr<iMeshObject> (im);
}

//----------------------------------------------------------------------

SCF_IMPLEMENT_IBASE (csNullmeshMeshObjectType)
  SCF_IMPLEMENTS_INTERFACE (iMeshObjectType)
  SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iComponent)
SCF_IMPLEMENT_IBASE_END

SCF_IMPLEMENT_EMBEDDED_IBASE (csNullmeshMeshObjectType::eiComponent)
  SCF_IMPLEMENTS_INTERFACE (iComponent)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

SCF_IMPLEMENT_FACTORY (csNullmeshMeshObjectType)

csNullmeshMeshObjectType::csNullmeshMeshObjectType (iBase* pParent)
{
  SCF_CONSTRUCT_IBASE (pParent);
  SCF_CONSTRUCT_EMBEDDED_IBASE(scfiComponent);
}

csNullmeshMeshObjectType::~csNullmeshMeshObjectType ()
{
}

csPtr<iMeshObjectFactory> csNullmeshMeshObjectType::NewFactory ()
{
  csNullmeshMeshObject* cm = new csNullmeshMeshObject (0);
  csRef<iMeshObjectFactory> ifact (
  	SCF_QUERY_INTERFACE (cm, iMeshObjectFactory));
  cm->DecRef ();
  return csPtr<iMeshObjectFactory> (ifact);
}

<|endoftext|>


<commit_before>//
// Created by kerail on 10.07.16.
//

#ifndef DAISU_SO3_HPP
#define DAISU_SO3_HPP

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <cmath>
#include <limits>

template<class T>
class SO3 {
 public:
  typedef Eigen::Matrix<T, 3, 1> Vector3;
  typedef Eigen::Matrix<T, 3, 3> Matrix33;
  typedef Eigen::Quaternion<T> Quaternion;

  SO3(const T &x, const T &y, const T &z) {
    m_coeffs = Eigen::Vector3d(x, y, z);
  }

  SO3(const Vector3 &v) {
    m_coeffs = v;
  }

  SO3(T angle, const Vector3 &v) {
    m_coeffs = v * angle;
  }

  SO3(const Quaternion &q_) {
    const Quaternion &q = q_.w() >= T(0) ? q_ : Quaternion(-q_.coeffs());

    const Vector3 &qv = q.vec();
    T sinha = qv.norm();
    if (sinha > T(0)) {
      T angle = T(2) * atan2(sinha, q.w()); //NOTE: signed
      m_coeffs = qv * (angle / sinha);
    } else {
      // if l is too small, its norm can be equal 0 but norm_inf greater 0
      // probably w is much bigger that vec, use it as length
      m_coeffs = qv * (T(2) / q.w()); ////NOTE: signed
    }
  }

  Vector3 rotateVector(const Vector3 &v) const {
    T a = getAngle();
    Vector3 axis = getAxis();
    T c_a = cos(a);
    T s_a = sin(a);
    return v * c_a + (axis.cross(v)) * s_a + axis * (axis.dot(v)) * (1 - c_a);
  }

  Matrix33 getMatrix() const {
    Matrix33 m;
    const Vector3 &cfs = m_coeffs;

    T l2 = cfs.squaredNorm();
    T l = sqrt(l2);

    T sn_l, cs1_ll, cs;
    if (l == T(0)) {
      // if l is 0 sin(x)/x = 1
      sn_l = T(1);
    } else {
      sn_l = sin(l) / l;
    }

    cs = cos(l);
    static const T c_pi64 = cos(M_PI / T(64));
    if (cs > c_pi64) {//fabs(l) < M_PI/T(64)
      // when l is near nezo, we need to switch to more precise formula
      if (l2 == T(0)) {
        // when l2 is zero, we can precisely calculate limit
        cs1_ll = 1 / T(2);
      } else {
        // 1 - cos(x) = 2 * sin(x/2)^2
        T sn = sin(l / T(2));
        cs1_ll = T(2) * sn * sn / l2;
      }
    } else {
      // here l2 > 0 because abs(l) > pi/64
      cs1_ll = (T(1) - cs) / l2;
    }

    Vector3 sn_ax = sn_l * m_coeffs;
    Vector3 cs1_l_ax = cs1_ll * m_coeffs;

    T tmp;
    tmp = cs1_l_ax.x() * m_coeffs.y();
    m.coeffRef(0, 1) = tmp - sn_ax.z();
    m.coeffRef(1, 0) = tmp + sn_ax.z();

    tmp = cs1_l_ax.x() * m_coeffs.z();
    m.coeffRef(0, 2) = tmp + sn_ax.y();
    m.coeffRef(2, 0) = tmp - sn_ax.y();

    tmp = cs1_l_ax.y() * m_coeffs.z();
    m.coeffRef(1, 2) = tmp - sn_ax.x();
    m.coeffRef(2, 1) = tmp + sn_ax.x();

//    m.diagonal() = (cs1_l_ax.cwiseProduct(m_coeffs)).array() + cs;

    Vector3 sq = cs1_l_ax.cwiseProduct(m_coeffs);
    m.coeffRef(0, 0) = 1 + (-sq.y() - sq.z());
    m.coeffRef(1, 1) = 1 + (-sq.x() - sq.z());
    m.coeffRef(2, 2) = 1 + (-sq.x() - sq.y());

    // Rotation matrix checks
    assert((m * m.transpose() - Matrix33::Identity()).norm() < 1e-10);
    assert(fabs(fabs(m.determinant()) - 1) < 1e-10);
    return m;
  }

  Quaternion getQuaternion() const {
    T a = getAngle();
    Vector3 axis = getAxis();
    T c_a2 = cos(a / 2.0);
    T s_a2 = sin(a / 2.0);
    Vector3 im = axis * s_a2;
    T real = c_a2;

    return Quaternion(real, im.x(), im.y(), im.z());
  }

  Vector3 coeffs() const {
    return m_coeffs;
  }

  T getAngle() const {
    return m_coeffs.norm();
  }

  Vector3 getAxis() const {
    T a = getAngle();
    return a > 0 ? (Vector3) (m_coeffs / a) : m_coeffs;
  }

  SO3<T> inverted() const {
    return SO3<T>(-m_coeffs);
  }

 private:
  Vector3 m_coeffs;

  Matrix33 antiSymmMatrix() const {
    Vector3 axis = getAxis();
    Matrix33 m = Matrix33::Zero();
    m(0, 1) = -axis(2);
    m(1, 0) = axis(2);

    m(0, 2) = axis(1);
    m(2, 0) = -axis(1);

    m(2, 1) = axis(0);
    m(1, 2) = -axis(0);
    return m;
  }

};

typedef SO3<double> SO3d;
typedef SO3<float> SO3f;

#endif //DAISU_SO3_HPP
<commit_msg>More precise quaternion calculation<commit_after>//
// Created by kerail on 10.07.16.
//

#ifndef DAISU_SO3_HPP
#define DAISU_SO3_HPP

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <cmath>
#include <limits>

template<class T>
class SO3 {
 public:
  typedef Eigen::Matrix<T, 3, 1> Vector3;
  typedef Eigen::Matrix<T, 3, 3> Matrix33;
  typedef Eigen::Quaternion<T> Quaternion;

  SO3(const T &x, const T &y, const T &z) {
    m_coeffs = Eigen::Vector3d(x, y, z);
  }

  SO3(const Vector3 &v) {
    m_coeffs = v;
  }

  SO3(T angle, const Vector3 &v) {
    m_coeffs = v * angle;
  }

  SO3(const Quaternion &q_) {
    const Quaternion &q = q_.w() >= T(0) ? q_ : Quaternion(-q_.coeffs());

    const Vector3 &qv = q.vec();
    T sinha = qv.norm();
    if (sinha > T(0)) {
      T angle = T(2) * atan2(sinha, q.w()); //NOTE: signed
      m_coeffs = qv * (angle / sinha);
    } else {
      // if l is too small, its norm can be equal 0 but norm_inf greater 0
      // probably w is much bigger that vec, use it as length
      m_coeffs = qv * (T(2) / q.w()); ////NOTE: signed
    }
  }

  Vector3 rotateVector(const Vector3 &v) const {
    T a = getAngle();
    Vector3 axis = getAxis();
    T c_a = cos(a);
    T s_a = sin(a);
    return v * c_a + (axis.cross(v)) * s_a + axis * (axis.dot(v)) * (1 - c_a);
  }

  Matrix33 getMatrix() const {
    Matrix33 m;
    const Vector3 &cfs = m_coeffs;

    T l2 = cfs.squaredNorm();
    T l = sqrt(l2);

    T sn_l, cs1_ll, cs;
    if (l == T(0)) {
      // if l is 0 sin(x)/x = 1
      sn_l = T(1);
    } else {
      sn_l = sin(l) / l;
    }

    cs = cos(l);
    static const T c_pi64 = cos(M_PI / T(64));
    if (cs > c_pi64) {//fabs(l) < M_PI/T(64)
      // when l is near nezo, we need to switch to more precise formula
      if (l2 == T(0)) {
        // when l2 is zero, we can precisely calculate limit
        cs1_ll = 1 / T(2);
      } else {
        // 1 - cos(x) = 2 * sin(x/2)^2
        T sn = sin(l / T(2));
        cs1_ll = T(2) * sn * sn / l2;
      }
    } else {
      // here l2 > 0 because abs(l) > pi/64
      cs1_ll = (T(1) - cs) / l2;
    }

    Vector3 sn_ax = sn_l * m_coeffs;
    Vector3 cs1_l_ax = cs1_ll * m_coeffs;

    T tmp;
    tmp = cs1_l_ax.x() * m_coeffs.y();
    m.coeffRef(0, 1) = tmp - sn_ax.z();
    m.coeffRef(1, 0) = tmp + sn_ax.z();

    tmp = cs1_l_ax.x() * m_coeffs.z();
    m.coeffRef(0, 2) = tmp + sn_ax.y();
    m.coeffRef(2, 0) = tmp - sn_ax.y();

    tmp = cs1_l_ax.y() * m_coeffs.z();
    m.coeffRef(1, 2) = tmp - sn_ax.x();
    m.coeffRef(2, 1) = tmp + sn_ax.x();

//    m.diagonal() = (cs1_l_ax.cwiseProduct(m_coeffs)).array() + cs;

    Vector3 sq = cs1_l_ax.cwiseProduct(m_coeffs);
    m.coeffRef(0, 0) = 1 + (-sq.y() - sq.z());
    m.coeffRef(1, 1) = 1 + (-sq.x() - sq.z());
    m.coeffRef(2, 2) = 1 + (-sq.x() - sq.y());

    // Rotation matrix checks
    assert((m * m.transpose() - Matrix33::Identity()).norm() < 1e-10);
    assert(fabs(fabs(m.determinant()) - 1) < 1e-10);
    return m;
  }

  Quaternion getQuaternion() const {
    const Vector3 cf2 = m_coeffs / T(2);
    T a = cf2.norm();
    if (a > T(0)) {
      T sn = sin(a) / a;
      return Quaternion(cos(a), cf2.x() * sn, cf2.y() * sn, cf2.z() * sn);
    } else {
      return Quaternion(T(1), cf2.x(), cf2.y(), cf2.z());
    }
  }

  Vector3 coeffs() const {
    return m_coeffs;
  }

  T getAngle() const {
    return m_coeffs.norm();
  }

  Vector3 getAxis() const {
    T a = getAngle();
    return a > 0 ? (Vector3) (m_coeffs / a) : m_coeffs;
  }

  SO3<T> inverted() const {
    return SO3<T>(-m_coeffs);
  }

 private:
  Vector3 m_coeffs;

  Matrix33 antiSymmMatrix() const {
    Vector3 axis = getAxis();
    Matrix33 m = Matrix33::Zero();
    m(0, 1) = -axis(2);
    m(1, 0) = axis(2);

    m(0, 2) = axis(1);
    m(2, 0) = -axis(1);

    m(2, 1) = axis(0);
    m(1, 2) = -axis(0);
    return m;
  }

};

typedef SO3<double> SO3d;
typedef SO3<float> SO3f;

#endif //DAISU_SO3_HPP
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/detail/MemoryIdler.h>

#include <folly/Logging.h>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
#include <folly/concurrency/CacheLocality.h>
#include <folly/memory/MallctlHelper.h>
#include <folly/memory/Malloc.h>
#include <folly/portability/PThread.h>
#include <folly/portability/SysMman.h>
#include <folly/portability/Unistd.h>

#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <utility>

namespace folly { namespace detail {

AtomicStruct<std::chrono::steady_clock::duration>
MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5));

void MemoryIdler::flushLocalMallocCaches() {
  if (!usingJEMalloc()) {
    return;
  }
  if (!mallctl || !mallctlnametomib || !mallctlbymib) {
    FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed";
    return;
  }

  try {
    // Not using mallctlCall as this will fail if tcache is disabled.
    mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0);

    // By default jemalloc has 4 arenas per cpu, and then assigns each
    // thread to one of those arenas.  This means that in any service
    // that doesn't perform a lot of context switching, the chances that
    // another thread will be using the current thread's arena (and hence
    // doing the appropriate dirty-page purging) are low.  Some good
    // tuned configurations (such as that used by hhvm) use fewer arenas
    // and then pin threads to avoid contended access.  In that case,
    // purging the arenas is counter-productive.  We use the heuristic
    // that if narenas <= 2 * num_cpus then we shouldn't do anything here,
    // which detects when the narenas has been reduced from the default
    unsigned narenas;
    unsigned arenaForCurrent;
    size_t mib[3];
    size_t miblen = 3;

    mallctlRead("opt.narenas", &narenas);
    mallctlRead("thread.arena", &arenaForCurrent);
    if (narenas > 2 * CacheLocality::system().numCpus &&
        mallctlnametomib("arena.0.purge", mib, &miblen) == 0) {
      mib[1] = static_cast<size_t>(arenaForCurrent);
      mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);
    }
  } catch (const std::runtime_error& ex) {
    FB_LOG_EVERY_MS(WARNING, 10000) << ex.what();
  }
}

// Stack madvise isn't Linux or glibc specific, but the system calls
// and arithmetic (and bug compatibility) are not portable.  The set of
// platforms could be increased if it was useful.
#if (FOLLY_X64 || FOLLY_PPC64) && defined(_GNU_SOURCE) && \
    defined(__linux__) && !FOLLY_MOBILE && !FOLLY_SANITIZE_ADDRESS

static FOLLY_TLS uintptr_t tls_stackLimit;
static FOLLY_TLS size_t tls_stackSize;

static size_t pageSize() {
  static const size_t s_pageSize = sysconf(_SC_PAGESIZE);
  return s_pageSize;
}

static void fetchStackLimits() {
  pthread_attr_t attr;
  pthread_getattr_np(pthread_self(), &attr);
  SCOPE_EXIT { pthread_attr_destroy(&attr); };

  void* addr;
  size_t rawSize;
  int err;
  if ((err = pthread_attr_getstack(&attr, &addr, &rawSize))) {
    // unexpected, but it is better to continue in prod than do nothing
    FB_LOG_EVERY_MS(ERROR, 10000) << "pthread_attr_getstack error " << err;
    assert(false);
    tls_stackSize = 1;
    return;
  }
  assert(addr != nullptr);
  assert(rawSize >= PTHREAD_STACK_MIN);

  // glibc subtracts guard page from stack size, even though pthread docs
  // seem to imply the opposite
  size_t guardSize;
  if (pthread_attr_getguardsize(&attr, &guardSize) != 0) {
    guardSize = 0;
  }
  assert(rawSize > guardSize);

  // stack goes down, so guard page adds to the base addr
  tls_stackLimit = reinterpret_cast<uintptr_t>(addr) + guardSize;
  tls_stackSize = rawSize - guardSize;

  assert((tls_stackLimit & (pageSize() - 1)) == 0);
}

FOLLY_NOINLINE static uintptr_t getStackPtr() {
  char marker;
  auto rv = reinterpret_cast<uintptr_t>(&marker);
  return rv;
}

void MemoryIdler::unmapUnusedStack(size_t retain) {
  if (tls_stackSize == 0) {
    fetchStackLimits();
  }
  if (tls_stackSize <= std::max(static_cast<size_t>(1), retain)) {
    // covers both missing stack info, and impossibly large retain
    return;
  }

  auto sp = getStackPtr();
  assert(sp >= tls_stackLimit);
  assert(sp - tls_stackLimit < tls_stackSize);

  auto end = (sp - retain) & ~(pageSize() - 1);
  if (end <= tls_stackLimit) {
    // no pages are eligible for unmapping
    return;
  }

  size_t len = end - tls_stackLimit;
  assert((len & (pageSize() - 1)) == 0);
  if (madvise((void*)tls_stackLimit, len, MADV_DONTNEED) != 0) {
    // It is likely that the stack vma hasn't been fully grown.  In this
    // case madvise will apply dontneed to the present vmas, then return
    // errno of ENOMEM.  We can also get an EAGAIN, theoretically.
    // EINVAL means either an invalid alignment or length, or that some
    // of the pages are locked or shared.  Neither should occur.
    assert(errno == EAGAIN || errno == ENOMEM);
  }
}

#else

void MemoryIdler::unmapUnusedStack(size_t /* retain */) {}

#endif

} // namespace detail
} // namespace folly
<commit_msg>better error handling in MemoryIdler for inside jails<commit_after>/*
 * Copyright 2017 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/detail/MemoryIdler.h>

#include <folly/Logging.h>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
#include <folly/concurrency/CacheLocality.h>
#include <folly/memory/MallctlHelper.h>
#include <folly/memory/Malloc.h>
#include <folly/portability/PThread.h>
#include <folly/portability/SysMman.h>
#include <folly/portability/Unistd.h>
#include <folly/synchronization/CallOnce.h>

#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <utility>

namespace folly { namespace detail {

AtomicStruct<std::chrono::steady_clock::duration>
MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5));

void MemoryIdler::flushLocalMallocCaches() {
  if (!usingJEMalloc()) {
    return;
  }
  if (!mallctl || !mallctlnametomib || !mallctlbymib) {
    FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed";
    return;
  }

  try {
    // Not using mallctlCall as this will fail if tcache is disabled.
    mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0);

    // By default jemalloc has 4 arenas per cpu, and then assigns each
    // thread to one of those arenas.  This means that in any service
    // that doesn't perform a lot of context switching, the chances that
    // another thread will be using the current thread's arena (and hence
    // doing the appropriate dirty-page purging) are low.  Some good
    // tuned configurations (such as that used by hhvm) use fewer arenas
    // and then pin threads to avoid contended access.  In that case,
    // purging the arenas is counter-productive.  We use the heuristic
    // that if narenas <= 2 * num_cpus then we shouldn't do anything here,
    // which detects when the narenas has been reduced from the default
    unsigned narenas;
    unsigned arenaForCurrent;
    size_t mib[3];
    size_t miblen = 3;

    mallctlRead("opt.narenas", &narenas);
    mallctlRead("thread.arena", &arenaForCurrent);
    if (narenas > 2 * CacheLocality::system().numCpus &&
        mallctlnametomib("arena.0.purge", mib, &miblen) == 0) {
      mib[1] = static_cast<size_t>(arenaForCurrent);
      mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);
    }
  } catch (const std::runtime_error& ex) {
    FB_LOG_EVERY_MS(WARNING, 10000) << ex.what();
  }
}

// Stack madvise isn't Linux or glibc specific, but the system calls
// and arithmetic (and bug compatibility) are not portable.  The set of
// platforms could be increased if it was useful.
#if (FOLLY_X64 || FOLLY_PPC64) && defined(_GNU_SOURCE) && \
    defined(__linux__) && !FOLLY_MOBILE && !FOLLY_SANITIZE_ADDRESS

static FOLLY_TLS uintptr_t tls_stackLimit;
static FOLLY_TLS size_t tls_stackSize;

static size_t pageSize() {
  static const size_t s_pageSize = sysconf(_SC_PAGESIZE);
  return s_pageSize;
}

static void fetchStackLimits() {
  int err;
  pthread_attr_t attr;
  if ((err = pthread_getattr_np(pthread_self(), &attr))) {
    // some restricted environments can't access /proc
    static folly::once_flag flag;
    folly::call_once(flag, [err]() {
      LOG(WARNING) << "pthread_getaddr_np failed errno=" << err;
    });

    tls_stackSize = 1;
    return;
  }
  SCOPE_EXIT { pthread_attr_destroy(&attr); };

  void* addr;
  size_t rawSize;
  if ((err = pthread_attr_getstack(&attr, &addr, &rawSize))) {
    // unexpected, but it is better to continue in prod than do nothing
    FB_LOG_EVERY_MS(ERROR, 10000) << "pthread_attr_getstack error " << err;
    assert(false);
    tls_stackSize = 1;
    return;
  }
  assert(addr != nullptr);
  assert(rawSize >= PTHREAD_STACK_MIN);

  // glibc subtracts guard page from stack size, even though pthread docs
  // seem to imply the opposite
  size_t guardSize;
  if (pthread_attr_getguardsize(&attr, &guardSize) != 0) {
    guardSize = 0;
  }
  assert(rawSize > guardSize);

  // stack goes down, so guard page adds to the base addr
  tls_stackLimit = reinterpret_cast<uintptr_t>(addr) + guardSize;
  tls_stackSize = rawSize - guardSize;

  assert((tls_stackLimit & (pageSize() - 1)) == 0);
}

FOLLY_NOINLINE static uintptr_t getStackPtr() {
  char marker;
  auto rv = reinterpret_cast<uintptr_t>(&marker);
  return rv;
}

void MemoryIdler::unmapUnusedStack(size_t retain) {
  if (tls_stackSize == 0) {
    fetchStackLimits();
  }
  if (tls_stackSize <= std::max(static_cast<size_t>(1), retain)) {
    // covers both missing stack info, and impossibly large retain
    return;
  }

  auto sp = getStackPtr();
  assert(sp >= tls_stackLimit);
  assert(sp - tls_stackLimit < tls_stackSize);

  auto end = (sp - retain) & ~(pageSize() - 1);
  if (end <= tls_stackLimit) {
    // no pages are eligible for unmapping
    return;
  }

  size_t len = end - tls_stackLimit;
  assert((len & (pageSize() - 1)) == 0);
  if (madvise((void*)tls_stackLimit, len, MADV_DONTNEED) != 0) {
    // It is likely that the stack vma hasn't been fully grown.  In this
    // case madvise will apply dontneed to the present vmas, then return
    // errno of ENOMEM.  We can also get an EAGAIN, theoretically.
    // EINVAL means either an invalid alignment or length, or that some
    // of the pages are locked or shared.  Neither should occur.
    assert(errno == EAGAIN || errno == ENOMEM);
  }
}

#else

void MemoryIdler::unmapUnusedStack(size_t /* retain */) {}

#endif

} // namespace detail
} // namespace folly
<|endoftext|>


<commit_before>#include <cmath>
#include "mathematic_utils.hpp"

using namespace Eigen;

double distance2(const Vector3d& P1, const Vector3d& P2)
{
  Vector3d dP = P2 - P1;
  return dP.dot(dP);
}

Vector3d normalize(const Vector3d& X)
{
  Vector3d N = X;
  if (N == Vector3d(0.,0.,0.)) return N;
  N.normalize();
  return N;
}

Vector3d rotate(const Vector3d& X, const Vector3d& a, const double p)
{
  Vector3d n = normalize(a);
  return X * cos(p) + n * (1.-cos(p)) * X.dot(n) + n.cross(X*sin(p));
}

bool inner_elipsoid(const Vector3d& P, const Vector3d& O, const Vector3d& R)
{
  Vector3d Ps = (P - O).array() / R.array();
  return (Ps.array() * Ps.array()).sum() <= 1.;
}

Vector3dSet neighoring(const Vector3d& O, const Vector3dSet& P)
{
  Vector3dSet Q;
  double d;

  if ( P.size() == 0 ) return Q;

  Q.push_back(P[0]);
  d = distance2(P[0],O);
  for (size_t p = 1; p < P.size(); p++ ) {
    double d2 = distance2(P[p],O);
    if (d2 == d) {
      Q.push_back(P[p]);
    } else if (d2 < d) {
      Q.clear();
      Q.push_back(P[p]);
      d = d2;
    }
  }

  return Q;
}

Vector3d reflect(const Vector3d& X, const Vector3d& n)
{
  Vector3d Y;
  if (X.dot(n) == 0.) {
    // 特異点: Xとnが直交する
    Y = X;
  } else if ((X.cross(n).array() == 0.).all()) {
    // 特異点: Xとnが平行している
    Y = -X;
  } else {
    double lx = sqrt(X.dot(X));
    double ln = sqrt(n.dot(n));
    double p  = acos(X.dot(n)/(lx*ln));
    Vector3d a  = X.cross(n);
    Y = -rotate(X,a,2.*p);
  }

  return Y;
}

Vector3dSet intersection(const Vector3d& P, const Vector3d& v,
                         const Vector3d& O, const Vector3d& R)
{
  Vector3dSet Q;
  Vector3d P0 = P - O;

  // Q = P + v.t として
  // (Qx/a)**2 + (Qy/b)**2 + (Qz/c)**2 = 1
  // となる方程式 A.t**2 + B.t + C = 0 を t について解く
  double A = ((v.array() / R.array()) * (v.array() / R.array())).sum();
  double B = ( P0.array() * v.array() * 2. / (R.array() * R.array())).sum();
  double C = ((P.array() / R.array()) * (P0.array() / R.array())).sum() - 1.;

  // 解の判定
  double D = B*B - 4.*A*C;
  if ( D >= 0. ) {
    // 実数解になる
    double t1 = (-B + sqrt(D)) / (2. * A);
    double t2 = (-B - sqrt(D)) / (2. * A);

    // tをLの方程式に当てて交点Qとする
    Vector3d Q1 = P + v*t1;
    Vector3d Q2 = P + v*t2;

    if ( D == 0. ) {
      // 解はひとつ
      Q.push_back(Q1);
    } else {
      // 解は2つ
      Q.push_back(Q1);
      Q.push_back(Q2);
    }
  }

  return Q;
}

<commit_msg>式の実装記述ミスを修正<commit_after>#include <cmath>
#include "mathematic_utils.hpp"

using namespace Eigen;

double distance2(const Vector3d& P1, const Vector3d& P2)
{
  Vector3d dP = P2 - P1;
  return dP.dot(dP);
}

Vector3d normalize(const Vector3d& X)
{
  Vector3d N = X;
  if (N == Vector3d(0.,0.,0.)) return N;
  N.normalize();
  return N;
}

Vector3d rotate(const Vector3d& X, const Vector3d& a, const double p)
{
  Vector3d n = normalize(a);
  return X * cos(p) + n * (1.-cos(p)) * X.dot(n) + n.cross(X*sin(p));
}

bool inner_elipsoid(const Vector3d& P, const Vector3d& O, const Vector3d& R)
{
  Vector3d Ps = (P - O).array() / R.array();
  return (Ps.array() * Ps.array()).sum() <= 1.;
}

Vector3dSet neighoring(const Vector3d& O, const Vector3dSet& P)
{
  Vector3dSet Q;
  double d;

  if ( P.size() == 0 ) return Q;

  Q.push_back(P[0]);
  d = distance2(P[0],O);
  for (size_t p = 1; p < P.size(); p++ ) {
    double d2 = distance2(P[p],O);
    if (d2 == d) {
      Q.push_back(P[p]);
    } else if (d2 < d) {
      Q.clear();
      Q.push_back(P[p]);
      d = d2;
    }
  }

  return Q;
}

Vector3d reflect(const Vector3d& X, const Vector3d& n)
{
  Vector3d Y;
  if (X.dot(n) == 0.) {
    // 特異点: Xとnが直交する
    Y = X;
  } else if ((X.cross(n).array() == 0.).all()) {
    // 特異点: Xとnが平行している
    Y = -X;
  } else {
    double lx = sqrt(X.dot(X));
    double ln = sqrt(n.dot(n));
    double p  = acos(X.dot(n)/(lx*ln));
    Vector3d a  = X.cross(n);
    Y = -rotate(X,a,2.*p);
  }

  return Y;
}

Vector3dSet intersection(const Vector3d& P, const Vector3d& v,
                         const Vector3d& O, const Vector3d& R)
{
  Vector3dSet Q;
  Vector3d P0 = P - O;

  // Q = P + v.t として
  // (Qx/a)**2 + (Qy/b)**2 + (Qz/c)**2 = 1
  // となる方程式 A.t**2 + B.t + C = 0 を t について解く
  double A = ((v.array() / R.array()) * (v.array() / R.array())).sum();
  double B = ( P0.array() * v.array() * 2. / (R.array() * R.array())).sum();
  double C = ((P0.array() / R.array()) * (P0.array() / R.array())).sum() - 1.;

  // 解の判定
  double D = B*B - 4.*A*C;
  if ( D >= 0. ) {
    // 実数解になる
    double t1 = (-B + sqrt(D)) / (2. * A);
    double t2 = (-B - sqrt(D)) / (2. * A);

    // tをLの方程式に当てて交点Qとする
    Vector3d Q1 = P + v*t1;
    Vector3d Q2 = P + v*t2;

    if ( D == 0. ) {
      // 解はひとつ
      Q.push_back(Q1);
    } else {
      // 解は2つ
      Q.push_back(Q1);
      Q.push_back(Q2);
    }
  }

  return Q;
}

<|endoftext|>


<commit_before>#include "menu/option_menu.h"
#include "menu/menu.h"
#include "menu/tab_menu.h"
#include "util/token.h"
#include "return_exception.h"
#include "util/token_exception.h"
#include "util/funcs.h"

OptionMenu::OptionMenu(Token *token) throw (LoadException): MenuOption(token, Event), _menu(0)
{
    // Check whether we have a menu or tabmenu
    if ( *token == "menu" )
    {
	_menu = new Menu();
    } else if (*token == "tabmenu"){
	_menu = new TabMenu();
    } else {
	throw LoadException("Not a menu");
    }
    // Set this menu as an option
    _menu->setAsOption(true);

    // Lets try loading from a file
    std::string temp;
    // Filename
    *token >> temp;
    if(temp=="name")
    {
	    token->resetToken();
	    _menu->load(token);
    }
    else _menu->load(Util::getDataPath() + temp);
    this->setText(_menu->getName());

    // Lets check if this menu is going bye bye
    if ( _menu->checkRemoval() ) setForRemoval(true);
}

OptionMenu::~OptionMenu()
{
	// Delete our menu
	if(_menu)delete _menu;
}

void OptionMenu::logic()
{
	// Nothing
}

void OptionMenu::run(bool &endGame)
{
	// Do our new menu
	_menu->run();
}

<commit_msg>dont assume first token is "name"<commit_after>#include "menu/option_menu.h"
#include "menu/menu.h"
#include "menu/tab_menu.h"
#include "util/token.h"
#include "return_exception.h"
#include "util/token_exception.h"
#include "util/funcs.h"
#include "globals.h"

OptionMenu::OptionMenu(Token *token) throw (LoadException):
MenuOption(token, Event),
_menu(0){
    // Check whether we have a menu or tabmenu
    if ( *token == "menu" ){
	_menu = new Menu();
    } else if (*token == "tabmenu"){
	_menu = new TabMenu();
    } else {
	throw LoadException("Not a menu");
    }
    // Set this menu as an option
    _menu->setAsOption(true);

    /*
    // Lets try loading from a file
    std::string temp;
    // Filename
    *token >> temp;
    */

    if (token->numTokens() == 1){
        std::string temp;
        *token >> temp;
        _menu->load(Util::getDataPath() + temp);
    } else {
        _menu->load(token);
    }

    this->setText(_menu->getName());

    // Lets check if this menu is going bye bye
    if ( _menu->checkRemoval() ) setForRemoval(true);
}

OptionMenu::~OptionMenu()
{
	// Delete our menu
	if(_menu)delete _menu;
}

void OptionMenu::logic()
{
	// Nothing
}

void OptionMenu::run(bool &endGame)
{
	// Do our new menu
	_menu->run();
}

<|endoftext|>


<commit_before>/* Copyright 2019 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <cstdio>
#include <cstdlib>
#include <string>

#include "test.h"

namespace ruy {

using LhsScalar = RUY_TEST_LHSSCALAR;
using RhsScalar = RUY_TEST_RHSSCALAR;
using AccumScalar = RUY_TEST_ACCUMSCALAR;
using DstScalar = RUY_TEST_DSTSCALAR;
using TestSetType =
    TestSet<LhsScalar, RhsScalar, BasicSpec<AccumScalar, DstScalar>>;

struct BenchmarkShape {
  int rows;
  int depth;
  int cols;
  int symm_lhs;
  int symm_rhs;
};

template <typename TestSetType>
std::vector<std::unique_ptr<TestResult<DstScalar>>> BenchmarkRCC(
    const BenchmarkShape& shape) {
  TestSetType test_set;
  test_set.rows = shape.rows;
  test_set.depth = shape.depth;
  test_set.cols = shape.cols;
  test_set.lhs_order = Order::kRowMajor;
  test_set.rhs_order = Order::kColMajor;
  test_set.dst_order = Order::kColMajor;
  test_set.layout_style = LayoutStyle::kPackedLinear;
  test_set.benchmark = true;
  const int asymmetry_lhs = shape.symm_lhs ? 0 : 1;
  const int asymmetry_rhs = shape.symm_rhs ? 0 : 1;
  test_set.lhs_zero_point = SymmetricZeroPoint<LhsScalar>() + asymmetry_lhs;
  test_set.rhs_zero_point = SymmetricZeroPoint<RhsScalar>() + asymmetry_rhs;
  test_set.use_specified_zero_points = true;
  test_set.perchannel = GetBoolEnvVarOrFalse("PERCHANNEL");
  test_set.benchmark_prepack_lhs = GetBoolEnvVarOrFalse("PREPACK_LHS");
  test_set.benchmark_prepack_rhs = GetBoolEnvVarOrFalse("PREPACK_RHS");
  test_set.Run();
  return std::move(test_set.results);
}

std::vector<int> ParseCommaSeparatedInts(
    const std::string& comma_separated_ints) {
  std::vector<int> result;
  for (std::size_t pos = 0; pos < comma_separated_ints.size();) {
    std::size_t delim_pos = comma_separated_ints.find(',', pos);
    if (delim_pos == std::string::npos) {
      delim_pos = comma_separated_ints.size();
    }
    result.push_back(
        std::stoi(comma_separated_ints.substr(pos, delim_pos - pos)));
    pos = delim_pos + 1;
  }
  return result;
}

void Benchmark() {
  const bool symm_lhs = std::is_floating_point<LhsScalar>::value ||
                        GetBoolEnvVarOrFalse("SYMM_LHS");
  const bool symm_rhs = std::is_floating_point<RhsScalar>::value ||
                        GetBoolEnvVarOrFalse("SYMM_RHS");
  const bool benchmark_cubic = GetBoolEnvVarOrFalse("RUY_BENCHMARK_CUBIC") ||
                               GetBoolEnvVarOrFalse("RUY_BENCHMARK_CUBIC_LIST");
  const int explicit_rows = GetIntEnvVarOrZero("ROWS");
  const int explicit_cols = GetIntEnvVarOrZero("COLS");
  const int explicit_depth = GetIntEnvVarOrZero("DEPTH");

  std::vector<BenchmarkShape> shapes;

  if (benchmark_cubic) {
    std::vector<int> sizes;
    const char* benchmark_cubic_list_env = getenv("RUY_BENCHMARK_CUBIC_LIST");
    if (benchmark_cubic_list_env) {
      sizes = ParseCommaSeparatedInts(benchmark_cubic_list_env);
    } else {
      // Often 8 is used for this multiplier, but to check teeny sizes one can
      // use 1.
      static constexpr int cubic_size_multiplier = 8;
      for (int i = 2 * cubic_size_multiplier;
           i <= (512 * cubic_size_multiplier); i *= 2) {
        sizes.push_back(i);
        if (i < (512 * cubic_size_multiplier)) {
          sizes.push_back(i * 3 / 2);
        }
      }
    }
    for (int i : sizes) {
      BenchmarkShape shape;
      // Even in cubic mode, one may still override an individual dimension
      // to allow testing a batch of rectangular sizes.
      shape.rows = explicit_rows ? explicit_rows : i;
      shape.cols = explicit_cols ? explicit_cols : i;
      shape.depth = explicit_depth ? explicit_depth : i;
      shape.symm_lhs = symm_lhs;
      shape.symm_rhs = symm_rhs;
      shapes.push_back(shape);
    }
  } else {
    BenchmarkShape shape;
    shape.rows = explicit_rows;
    shape.cols = explicit_cols;
    shape.depth = explicit_depth;
    if (!shape.rows || !shape.depth || !shape.cols) {
      fprintf(stderr,
              "Please specify positive sizes with these env vars: ROWS, DEPTH, "
              "COLS.\n");
      exit(1);
    }
    shape.symm_lhs = symm_lhs;
    shape.symm_rhs = symm_rhs;
    shapes.push_back(shape);
  }

  for (int i = 0; i < shapes.size(); i++) {
    const auto& shape = shapes[i];
    const auto& results = BenchmarkRCC<TestSetType>(shape);
    if (i == 0) {
      if (benchmark_cubic) {
        printf("size");
        for (const auto& result : results) {
          printf(",%s", PathName(*result).c_str());
        }
        printf("\n");
      } else {
        printf("path,shape,Gop/s\n");
      }
      fflush(stdout);
    }
    if (benchmark_cubic) {
      printf("%d", shape.rows);
      for (const auto& result : results) {
        printf(",%.4g", 2.0e-9 * shape.rows * shape.cols * shape.depth /
                            result->latency);
        if (GetBoolEnvVarOrFalse("RUY_BENCHMARK_PMU")) {
          printf(",%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g",
                 result->l1_refill_rate, result->l2_refill_rate,
                 result->l3_refill_rate, result->l1tlb_refill_rate,
                 result->l2tlb_refill_rate, result->mispred_rate,
                 result->frontend_stall_rate, result->backend_stall_rate);
        }
      }
      printf("\n");
      fflush(stdout);
    } else {
      for (const auto& result : results) {
        printf(
            "%s,%dx%dx%d,%.4g", PathName(*result).c_str(), shape.rows,
            shape.depth, shape.cols,
            2.0e-9 * shape.rows * shape.cols * shape.depth / result->latency);
        if (GetBoolEnvVarOrFalse("RUY_BENCHMARK_PMU")) {
          printf(",%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g",
                 result->l1_refill_rate, result->l2_refill_rate,
                 result->l3_refill_rate, result->l1tlb_refill_rate,
                 result->l2tlb_refill_rate, result->mispred_rate,
                 result->frontend_stall_rate, result->backend_stall_rate);
        }
        printf("\n");
      }
      fflush(stdout);
    }
  }
}

}  // namespace ruy

int main() { ruy::Benchmark(); }
<commit_msg>better column headers in the benchmark output.<commit_after>/* Copyright 2019 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <cstdio>
#include <cstdlib>
#include <string>

#include "test.h"

namespace ruy {

using LhsScalar = RUY_TEST_LHSSCALAR;
using RhsScalar = RUY_TEST_RHSSCALAR;
using AccumScalar = RUY_TEST_ACCUMSCALAR;
using DstScalar = RUY_TEST_DSTSCALAR;
using TestSetType =
    TestSet<LhsScalar, RhsScalar, BasicSpec<AccumScalar, DstScalar>>;

struct BenchmarkShape {
  int rows;
  int depth;
  int cols;
  int symm_lhs;
  int symm_rhs;
};

template <typename TestSetType>
std::vector<std::unique_ptr<TestResult<DstScalar>>> BenchmarkRCC(
    const BenchmarkShape& shape) {
  TestSetType test_set;
  test_set.rows = shape.rows;
  test_set.depth = shape.depth;
  test_set.cols = shape.cols;
  test_set.lhs_order = Order::kRowMajor;
  test_set.rhs_order = Order::kColMajor;
  test_set.dst_order = Order::kColMajor;
  test_set.layout_style = LayoutStyle::kPackedLinear;
  test_set.benchmark = true;
  const int asymmetry_lhs = shape.symm_lhs ? 0 : 1;
  const int asymmetry_rhs = shape.symm_rhs ? 0 : 1;
  test_set.lhs_zero_point = SymmetricZeroPoint<LhsScalar>() + asymmetry_lhs;
  test_set.rhs_zero_point = SymmetricZeroPoint<RhsScalar>() + asymmetry_rhs;
  test_set.use_specified_zero_points = true;
  test_set.perchannel = GetBoolEnvVarOrFalse("PERCHANNEL");
  test_set.benchmark_prepack_lhs = GetBoolEnvVarOrFalse("PREPACK_LHS");
  test_set.benchmark_prepack_rhs = GetBoolEnvVarOrFalse("PREPACK_RHS");
  test_set.Run();
  return std::move(test_set.results);
}

std::vector<int> ParseCommaSeparatedInts(
    const std::string& comma_separated_ints) {
  std::vector<int> result;
  for (std::size_t pos = 0; pos < comma_separated_ints.size();) {
    std::size_t delim_pos = comma_separated_ints.find(',', pos);
    if (delim_pos == std::string::npos) {
      delim_pos = comma_separated_ints.size();
    }
    result.push_back(
        std::stoi(comma_separated_ints.substr(pos, delim_pos - pos)));
    pos = delim_pos + 1;
  }
  return result;
}

void Benchmark() {
  const bool symm_lhs = std::is_floating_point<LhsScalar>::value ||
                        GetBoolEnvVarOrFalse("SYMM_LHS");
  const bool symm_rhs = std::is_floating_point<RhsScalar>::value ||
                        GetBoolEnvVarOrFalse("SYMM_RHS");
  const bool benchmark_cubic = GetBoolEnvVarOrFalse("RUY_BENCHMARK_CUBIC") ||
                               GetBoolEnvVarOrFalse("RUY_BENCHMARK_CUBIC_LIST");
  const int explicit_rows = GetIntEnvVarOrZero("ROWS");
  const int explicit_cols = GetIntEnvVarOrZero("COLS");
  const int explicit_depth = GetIntEnvVarOrZero("DEPTH");

  std::vector<BenchmarkShape> shapes;

  if (benchmark_cubic) {
    std::vector<int> sizes;
    const char* benchmark_cubic_list_env = getenv("RUY_BENCHMARK_CUBIC_LIST");
    if (benchmark_cubic_list_env) {
      sizes = ParseCommaSeparatedInts(benchmark_cubic_list_env);
    } else {
      // Often 8 is used for this multiplier, but to check teeny sizes one can
      // use 1.
      static constexpr int cubic_size_multiplier = 8;
      for (int i = 2 * cubic_size_multiplier;
           i <= (512 * cubic_size_multiplier); i *= 2) {
        sizes.push_back(i);
        if (i < (512 * cubic_size_multiplier)) {
          sizes.push_back(i * 3 / 2);
        }
      }
    }
    for (int i : sizes) {
      BenchmarkShape shape;
      // Even in cubic mode, one may still override an individual dimension
      // to allow testing a batch of rectangular sizes.
      shape.rows = explicit_rows ? explicit_rows : i;
      shape.cols = explicit_cols ? explicit_cols : i;
      shape.depth = explicit_depth ? explicit_depth : i;
      shape.symm_lhs = symm_lhs;
      shape.symm_rhs = symm_rhs;
      shapes.push_back(shape);
    }
  } else {
    BenchmarkShape shape;
    shape.rows = explicit_rows;
    shape.cols = explicit_cols;
    shape.depth = explicit_depth;
    if (!shape.rows || !shape.depth || !shape.cols) {
      fprintf(stderr,
              "Please specify positive sizes with these env vars: ROWS, DEPTH, "
              "COLS.\n");
      exit(1);
    }
    shape.symm_lhs = symm_lhs;
    shape.symm_rhs = symm_rhs;
    shapes.push_back(shape);
  }

  for (int i = 0; i < shapes.size(); i++) {
    const auto& shape = shapes[i];
    const auto& results = BenchmarkRCC<TestSetType>(shape);
    if (i == 0) {
      if (benchmark_cubic) {
        printf("size");
        for (const auto& result : results) {
          if (results.size() > 1) {
            printf(",%s:Gop/s", PathName(*result).c_str());
          } else {
            printf(",Gop/s");
          }
          if (GetBoolEnvVarOrFalse("RUY_BENCHMARK_PMU")) {
            printf(
                ",l1_refill,l2_refill,l3_refill,l1tlb_refill,l2tlb_refill,"
                "mispred,frontend_stall,backend_stall");
          }
        }
        printf("\n");
      } else {
        printf("path,shape,Gop/s\n");
      }
      fflush(stdout);
    }
    if (benchmark_cubic) {
      printf("%d", shape.rows);
      for (const auto& result : results) {
        printf(",%.4g", 2.0e-9 * shape.rows * shape.cols * shape.depth /
                            result->latency);
        if (GetBoolEnvVarOrFalse("RUY_BENCHMARK_PMU")) {
          printf(",%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g",
                 result->l1_refill_rate, result->l2_refill_rate,
                 result->l3_refill_rate, result->l1tlb_refill_rate,
                 result->l2tlb_refill_rate, result->mispred_rate,
                 result->frontend_stall_rate, result->backend_stall_rate);
        }
      }
      printf("\n");
      fflush(stdout);
    } else {
      for (const auto& result : results) {
        printf(
            "%s,%dx%dx%d,%.4g", PathName(*result).c_str(), shape.rows,
            shape.depth, shape.cols,
            2.0e-9 * shape.rows * shape.cols * shape.depth / result->latency);
        if (GetBoolEnvVarOrFalse("RUY_BENCHMARK_PMU")) {
          printf(",%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g,%.3g",
                 result->l1_refill_rate, result->l2_refill_rate,
                 result->l3_refill_rate, result->l1tlb_refill_rate,
                 result->l2tlb_refill_rate, result->mispred_rate,
                 result->frontend_stall_rate, result->backend_stall_rate);
        }
        printf("\n");
      }
      fflush(stdout);
    }
  }
}

}  // namespace ruy

int main() { ruy::Benchmark(); }
<|endoftext|>


<commit_before>#include <functional>
#include <iostream>
#include <thread>

#include <rclcpp/rclcpp.hpp>

using rclcpp::callback_group::CallbackGroupType;
using std::chrono::steady_clock;
typedef std::chrono::duration<float> floating_seconds;

void on_timer_group1()
{
  auto this_id = rclcpp::thread_id;
  auto start = steady_clock::now();
  std::cout << "[1:" << this_id << "] Start" << std::endl;
  rclcpp::sleep_for(0.001_s);
  std::cout << "[1:" << this_id << "] Stop after "
            << std::chrono::duration_cast<floating_seconds>(
              steady_clock::now() - start).count()
            << std::endl;
}

void on_timer_group2()
{
  auto this_id = rclcpp::thread_id;
  auto start = steady_clock::now();
  std::cout << "[2:" << this_id << "] Start" << std::endl;
  rclcpp::sleep_for(0.001_s);
  std::cout << "[2:" << this_id << "] Stop after "
            << std::chrono::duration_cast<floating_seconds>(
              steady_clock::now() - start).count()
            << std::endl;
}

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);

  auto node = rclcpp::Node::make_shared("my_node");

  // auto g1 = node->create_callback_group(CallbackGroupType::MutuallyExclusive);
  auto g2 = node->create_callback_group(CallbackGroupType::MutuallyExclusive);
  // auto g2 = node->create_callback_group(CallbackGroupType::Reentrant);

  // auto timer1 = node->create_wall_timer(2.0_s, on_timer_group1, g1);
  // auto timer2 = node->create_wall_timer(0.25_s, on_timer_group2, g2);
  auto timer3 = node->create_wall_timer(0.001_s, on_timer_group1, g2);

  // rclcpp::executors::MultiThreadedExecutor executor;
  rclcpp::executors::SingleThreadedExecutor executor;
  executor.add_node(node);

  executor.spin();

  rclcpp::utilities::sleep_for(std::chrono::seconds(1));

  return 0;
}
<commit_msg>different_groups: remove vestigial code<commit_after>#include <functional>
#include <iostream>
#include <thread>

#include <rclcpp/rclcpp.hpp>

using rclcpp::callback_group::CallbackGroupType;
using std::chrono::steady_clock;
typedef std::chrono::duration<float> floating_seconds;

void on_timer_group1()
{
  auto this_id = rclcpp::thread_id;
  auto start = steady_clock::now();
  std::cout << "[1:" << this_id << "] Start" << std::endl;
  rclcpp::sleep_for(0.001_s);
  std::cout << "[1:" << this_id << "] Stop after "
            << std::chrono::duration_cast<floating_seconds>(
              steady_clock::now() - start).count()
            << std::endl;
}

void on_timer_group2()
{
  auto this_id = rclcpp::thread_id;
  auto start = steady_clock::now();
  std::cout << "[2:" << this_id << "] Start" << std::endl;
  rclcpp::sleep_for(0.001_s);
  std::cout << "[2:" << this_id << "] Stop after "
            << std::chrono::duration_cast<floating_seconds>(
              steady_clock::now() - start).count()
            << std::endl;
}

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);

  auto node = rclcpp::Node::make_shared("my_node");

  // auto g1 = node->create_callback_group(CallbackGroupType::MutuallyExclusive);
  auto g2 = node->create_callback_group(CallbackGroupType::MutuallyExclusive);
  // auto g2 = node->create_callback_group(CallbackGroupType::Reentrant);

  // auto timer1 = node->create_wall_timer(2.0_s, on_timer_group1, g1);
  // auto timer2 = node->create_wall_timer(0.25_s, on_timer_group2, g2);
  auto timer3 = node->create_wall_timer(0.001_s, on_timer_group1, g2);

  // rclcpp::executors::MultiThreadedExecutor executor;
  rclcpp::executors::SingleThreadedExecutor executor;
  executor.add_node(node);

  executor.spin();

  return 0;
}
<|endoftext|>