<commit_before>// Copyright 2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <unordered_set>

#include "elang/optimizer/validator.h"

#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "elang/optimizer/depth_first_traversal.h"
#include "elang/optimizer/error_code.h"
#include "elang/optimizer/factory.h"
#include "elang/optimizer/function.h"
#include "elang/optimizer/nodes.h"
#include "elang/optimizer/node_visitor.h"
#include "elang/optimizer/types.h"

namespace elang {
namespace optimizer {

//////////////////////////////////////////////////////////////////////
//
// Validator::Context
//
class Validator::Context : public NodeVisitor {
 public:
  explicit Context(Validator* validator);
  ~Context() = default;

  bool is_valid() const { return is_valid_; }

 private:
  void Error(ErrorCode error_code, Node* node, Thing* detail);
  void Error(ErrorCode error_code, Node* node);
  void ErrorInInput(Node* node, int index);

  void ValidatePhiInputs(Node* node,
                         PhiOwnerNode* owner,
                         const ZoneDeque<PhiInputHolder*>& phi_inputs);

  // NodeVisitor member functions
  void DoDefaultVisit(Node* node) final;
  void VisitCall(CallNode* node) final;
  void VisitEffectGet(EffectGetNode* node) final;
  void VisitEffectPhi(EffectPhiNode* node) final;
  void VisitElement(ElementNode* node) final;
  void VisitEntry(EntryNode* node) final;
  void VisitExit(ExitNode* node) final;
  void VisitGet(GetNode* node) final;
  void VisitIf(IfNode* node) final;
  void VisitIfFalse(IfFalseNode* node) final;
  void VisitIfTrue(IfTrueNode* node) final;
  void VisitLength(LengthNode* node) final;
  void VisitLoad(LoadNode* node) final;
  void VisitPhi(PhiNode* node) final;
  void VisitRet(RetNode* node) final;
  void VisitParameter(ParameterNode* node) final;

  bool is_valid_;
  Validator* const validator_;

  DISALLOW_COPY_AND_ASSIGN(Context);
};

Validator::Context::Context(Validator* validator)
    : is_valid_(true), validator_(validator) {
}

void Validator::Context::Error(ErrorCode error_code,
                               Node* node,
                               Thing* detail) {
  validator_->Error(error_code, node, detail);
  is_valid_ = false;
}

void Validator::Context::Error(ErrorCode error_code, Node* node) {
  validator_->Error(error_code, node);
  is_valid_ = false;
}

void Validator::Context::ErrorInInput(Node* node, int index) {
  Error(ErrorCode::ValidateNodeInvalidInput, node, validator_->NewInt32(index));
}

void Validator::Context::ValidatePhiInputs(
    Node* node,
    PhiOwnerNode* owner,
    const ZoneDeque<PhiInputHolder*>& phi_inputs) {
  std::unordered_set<Node*> predecessors;
  for (auto predecessor : owner->inputs())
    predecessors.insert(predecessor);

  std::unordered_set<Node*> controls;
  auto index = 0;
  for (auto const phi_input : phi_inputs) {
    auto const input = phi_input->value();
    if (input->output_type() != node->output_type())
      ErrorInInput(node, index);
    if (!input->IsValidEffect() && !input->IsValidData())
      ErrorInInput(node, index);
    auto const control = phi_input->control();
    if (controls.count(control))
      ErrorInInput(node, index);
    if (!predecessors.count(control))
      ErrorInInput(node, index);
    controls.insert(control);
    ++index;
  }

  for (auto predecessor : owner->inputs()) {
    if (controls.count(predecessor))
      continue;
    Error(ErrorCode::ValidatePhiNodeMissing, node, predecessor);
  }
}

// NodeVisitor functions
void Validator::Context::DoDefaultVisit(Node* node) {
}

void Validator::Context::VisitCall(CallNode* node) {
  auto const tuple_type = node->output_type()->as<TupleType>();
  if (!tuple_type || tuple_type->size() != 3)
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);
  if (!tuple_type->get(0)->is<ControlType>())
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);
  if (!tuple_type->get(1)->is<EffectType>())
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);

  if (!node->input(0)->output_type()->is<ControlType>())
    ErrorInInput(node, 0);

  if (!node->input(1)->output_type()->is<EffectType>())
    ErrorInInput(node, 1);

  auto const callee_type = node->input(2)->output_type()->as<FunctionType>();
  if (!callee_type)
    return ErrorInInput(node, 2);
  if (tuple_type->get(2) != callee_type->return_type())
    ErrorInInput(node, 2);

  if (node->input(3)->output_type() != callee_type->parameters_type())
    ErrorInInput(node, 3);
}

void Validator::Context::VisitEffectGet(EffectGetNode* node) {
  if (!node->output_type()->is<EffectType>())
    Error(ErrorCode::ValidateNodeInvalidOutput, node);
  if (!node->input(0)->IsValidEffectAt(node->field()))
    ErrorInInput(node, 0);
}

void Validator::Context::VisitEffectPhi(EffectPhiNode* node) {
  if (!node->owner()->IsValidControl())
    return Error(ErrorCode::ValidatePhiNodeInvalidOwner, node, node->owner());
  ValidatePhiInputs(node, node->owner(), node->phi_inputs());
}

void Validator::Context::VisitElement(ElementNode* node) {
  auto const pointer_type = node->input(0)->output_type()->as<PointerType>();
  if (!pointer_type)
    return ErrorInInput(node, 0);
  auto const array_type = pointer_type->pointee()->as<ArrayType>();
  if (!array_type)
    return ErrorInInput(node, 0);
  if (node->output_type() != array_type->element_type())
    return Error(ErrorCode::ValidateNodeInvalidOutput, node);
  if (array_type->rank() == 1) {
    if (!node->input(1)->output_type()->is<Int32Type>())
      ErrorInInput(node, 1);
    return;
  }

  auto const indexes_type = node->input(1)->output_type()->as<TupleType>();
  if (!indexes_type || array_type->rank() != indexes_type->size())
    return ErrorInInput(node, 1);
  for (auto const type : indexes_type->components()) {
    if (!type->is<Int32Type>())
      return ErrorInInput(node, 1);
  }
}

void Validator::Context::VisitEntry(EntryNode* node) {
  auto const tuple_type = node->output_type()->as<TupleType>();
  if (!tuple_type || tuple_type->size() != 3)
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);
  if (!tuple_type->get(0)->is<ControlType>())
    Error(ErrorCode::ValidateEntryNodeNoControlOutput, node);
  if (!tuple_type->get(1)->is<EffectType>())
    Error(ErrorCode::ValidateEntryNodeNoEffectOutput, node);
  if (node->users().empty())
    Error(ErrorCode::ValidateEntryNodeNoUsers, node);
}

void Validator::Context::VisitExit(ExitNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
}

void Validator::Context::VisitGet(GetNode* node) {
  auto const tuple_type = node->input(0)->output_type()->as<TupleType>();
  if (!tuple_type)
    return ErrorInInput(node, 0);
  if (node->field() >= tuple_type->size())
    return ErrorInInput(node, 0);
}

void Validator::Context::VisitIf(IfNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(1)->IsValidData())
    ErrorInInput(node, 1);
  if (!node->input(1)->output_type()->is<BoolType>())
    ErrorInInput(node, 1);
}

void Validator::Context::VisitIfFalse(IfFalseNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(0)->is<IfNode>())
    ErrorInInput(node, 0);
}

void Validator::Context::VisitIfTrue(IfTrueNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(0)->is<IfNode>())
    ErrorInInput(node, 0);
}

void Validator::Context::VisitLength(LengthNode* node) {
  auto const pointer_type = node->input(0)->output_type()->as<PointerType>();
  if (!pointer_type)
    return ErrorInInput(node, 0);
  auto const array_type = pointer_type->pointee()->as<ArrayType>();
  if (!array_type)
    return ErrorInInput(node, 0);
  if (node->output_type() != array_type->element_type())
    return Error(ErrorCode::ValidateNodeInvalidOutput, node);

  auto const rank_node = node->input(1)->as<Int32Node>();
  if (!rank_node)
    return ErrorInInput(node, 1);
  if (rank_node->data() >= base::checked_cast<int>(array_type->rank()))
    return ErrorInInput(node, 1);
}

void Validator::Context::VisitLoad(LoadNode* node) {
  if (!node->input(0)->IsValidEffect())
    ErrorInInput(node, 0);
  if (!node->input(1)->IsValidData())
    ErrorInInput(node, 1);
  if (!node->input(1)->output_type()->is<PointerType>())
    ErrorInInput(node, 1);
  if (!node->input(2)->IsValidData())
    ErrorInInput(node, 2);
  auto const pointer_type = node->input(2)->output_type()->as<PointerType>();
  if (!pointer_type || node->output_type() != pointer_type->pointee())
    ErrorInInput(node, 1);
}

void Validator::Context::VisitParameter(ParameterNode* node) {
  auto const entry_node = node->input(0)->as<EntryNode>();
  if (!entry_node) {
    ErrorInInput(node, 0);
    return;
  }
  if (node->output_type() != entry_node->parameter_type(node->field())) {
    Error(ErrorCode::ValidateNodeInvalidOutput, node);
    return;
  }
}

void Validator::Context::VisitPhi(PhiNode* node) {
  if (!node->owner()->IsValidControl())
    return Error(ErrorCode::ValidatePhiNodeInvalidOwner, node, node->owner());
  ValidatePhiInputs(node, node->owner(), node->phi_inputs());
}

void Validator::Context::VisitRet(RetNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(1)->IsValidEffect())
    ErrorInInput(node, 1);
  if (!node->input(2)->IsValidData())
    ErrorInInput(node, 2);
}

//////////////////////////////////////////////////////////////////////
//
// Validator
//
Validator::Validator(Factory* factory, const Function* function)
    : ErrorReporter(factory), factory_(factory), function_(function) {
}

Validator::~Validator() {
}

Node* Validator::NewInt32(int data) {
  return factory_->NewInt32(data);
}

bool Validator::Validate(Node* node) {
  Context context(this);
  node->Accept(&context);
  return context.is_valid();
}

bool Validator::Validate() {
  DepthFirstTraversal<OnInputEdge, const Function> walker;
  Context context(this);
  walker.Traverse(function_, &context);
  return context.is_valid();
}

}  // namespace optimizer
}  // namespace elang
<commit_msg>elang/optimizer: Make |Validator| to work with |ElementNode| and |LengthNode| correctlly.<commit_after>// Copyright 2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <unordered_set>

#include "elang/optimizer/validator.h"

#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "elang/optimizer/depth_first_traversal.h"
#include "elang/optimizer/error_code.h"
#include "elang/optimizer/factory.h"
#include "elang/optimizer/function.h"
#include "elang/optimizer/nodes.h"
#include "elang/optimizer/node_visitor.h"
#include "elang/optimizer/types.h"

namespace elang {
namespace optimizer {

//////////////////////////////////////////////////////////////////////
//
// Validator::Context
//
class Validator::Context : public NodeVisitor {
 public:
  explicit Context(Validator* validator);
  ~Context() = default;

  bool is_valid() const { return is_valid_; }

 private:
  void Error(ErrorCode error_code, Node* node, Thing* detail);
  void Error(ErrorCode error_code, Node* node);
  void ErrorInInput(Node* node, int index);

  void ValidatePhiInputs(Node* node,
                         PhiOwnerNode* owner,
                         const ZoneDeque<PhiInputHolder*>& phi_inputs);

  // NodeVisitor member functions
  void DoDefaultVisit(Node* node) final;
  void VisitCall(CallNode* node) final;
  void VisitEffectGet(EffectGetNode* node) final;
  void VisitEffectPhi(EffectPhiNode* node) final;
  void VisitElement(ElementNode* node) final;
  void VisitEntry(EntryNode* node) final;
  void VisitExit(ExitNode* node) final;
  void VisitGet(GetNode* node) final;
  void VisitIf(IfNode* node) final;
  void VisitIfFalse(IfFalseNode* node) final;
  void VisitIfTrue(IfTrueNode* node) final;
  void VisitLength(LengthNode* node) final;
  void VisitLoad(LoadNode* node) final;
  void VisitPhi(PhiNode* node) final;
  void VisitRet(RetNode* node) final;
  void VisitParameter(ParameterNode* node) final;

  bool is_valid_;
  Validator* const validator_;

  DISALLOW_COPY_AND_ASSIGN(Context);
};

Validator::Context::Context(Validator* validator)
    : is_valid_(true), validator_(validator) {
}

void Validator::Context::Error(ErrorCode error_code,
                               Node* node,
                               Thing* detail) {
  validator_->Error(error_code, node, detail);
  is_valid_ = false;
}

void Validator::Context::Error(ErrorCode error_code, Node* node) {
  validator_->Error(error_code, node);
  is_valid_ = false;
}

void Validator::Context::ErrorInInput(Node* node, int index) {
  Error(ErrorCode::ValidateNodeInvalidInput, node, validator_->NewInt32(index));
}

void Validator::Context::ValidatePhiInputs(
    Node* node,
    PhiOwnerNode* owner,
    const ZoneDeque<PhiInputHolder*>& phi_inputs) {
  std::unordered_set<Node*> predecessors;
  for (auto predecessor : owner->inputs())
    predecessors.insert(predecessor);

  std::unordered_set<Node*> controls;
  auto index = 0;
  for (auto const phi_input : phi_inputs) {
    auto const input = phi_input->value();
    if (input->output_type() != node->output_type())
      ErrorInInput(node, index);
    if (!input->IsValidEffect() && !input->IsValidData())
      ErrorInInput(node, index);
    auto const control = phi_input->control();
    if (controls.count(control))
      ErrorInInput(node, index);
    if (!predecessors.count(control))
      ErrorInInput(node, index);
    controls.insert(control);
    ++index;
  }

  for (auto predecessor : owner->inputs()) {
    if (controls.count(predecessor))
      continue;
    Error(ErrorCode::ValidatePhiNodeMissing, node, predecessor);
  }
}

// NodeVisitor functions
void Validator::Context::DoDefaultVisit(Node* node) {
}

void Validator::Context::VisitCall(CallNode* node) {
  auto const tuple_type = node->output_type()->as<TupleType>();
  if (!tuple_type || tuple_type->size() != 3)
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);
  if (!tuple_type->get(0)->is<ControlType>())
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);
  if (!tuple_type->get(1)->is<EffectType>())
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);

  if (!node->input(0)->output_type()->is<ControlType>())
    ErrorInInput(node, 0);

  if (!node->input(1)->output_type()->is<EffectType>())
    ErrorInInput(node, 1);

  auto const callee_type = node->input(2)->output_type()->as<FunctionType>();
  if (!callee_type)
    return ErrorInInput(node, 2);
  if (tuple_type->get(2) != callee_type->return_type())
    ErrorInInput(node, 2);

  if (node->input(3)->output_type() != callee_type->parameters_type())
    ErrorInInput(node, 3);
}

void Validator::Context::VisitEffectGet(EffectGetNode* node) {
  if (!node->output_type()->is<EffectType>())
    Error(ErrorCode::ValidateNodeInvalidOutput, node);
  if (!node->input(0)->IsValidEffectAt(node->field()))
    ErrorInInput(node, 0);
}

void Validator::Context::VisitEffectPhi(EffectPhiNode* node) {
  if (!node->owner()->IsValidControl())
    return Error(ErrorCode::ValidatePhiNodeInvalidOwner, node, node->owner());
  ValidatePhiInputs(node, node->owner(), node->phi_inputs());
}

void Validator::Context::VisitElement(ElementNode* node) {
  auto const array_pointer_type =
      node->input(0)->output_type()->as<PointerType>();
  if (!array_pointer_type)
    return ErrorInInput(node, 0);
  auto const array_type = array_pointer_type->pointee()->as<ArrayType>();
  if (!array_type)
    return ErrorInInput(node, 0);

  auto const pointer_type = node->output_type()->as<PointerType>();
  if (pointer_type && (pointer_type->pointee() != array_type->element_type()))
    return Error(ErrorCode::ValidateNodeInvalidOutput, node);

  if (array_type->rank() == 1) {
    if (!node->input(1)->output_type()->is<Int32Type>())
      ErrorInInput(node, 1);
    return;
  }

  auto const indexes_type = node->input(1)->output_type()->as<TupleType>();
  if (!indexes_type || array_type->rank() != indexes_type->size())
    return ErrorInInput(node, 1);
  for (auto const type : indexes_type->components()) {
    if (!type->is<Int32Type>())
      return ErrorInInput(node, 1);
  }
}

void Validator::Context::VisitEntry(EntryNode* node) {
  auto const tuple_type = node->output_type()->as<TupleType>();
  if (!tuple_type || tuple_type->size() != 3)
    return Error(ErrorCode::ValidateEntryNodeInvalidOutput, node);
  if (!tuple_type->get(0)->is<ControlType>())
    Error(ErrorCode::ValidateEntryNodeNoControlOutput, node);
  if (!tuple_type->get(1)->is<EffectType>())
    Error(ErrorCode::ValidateEntryNodeNoEffectOutput, node);
  if (node->users().empty())
    Error(ErrorCode::ValidateEntryNodeNoUsers, node);
}

void Validator::Context::VisitExit(ExitNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
}

void Validator::Context::VisitGet(GetNode* node) {
  auto const tuple_type = node->input(0)->output_type()->as<TupleType>();
  if (!tuple_type)
    return ErrorInInput(node, 0);
  if (node->field() >= tuple_type->size())
    return ErrorInInput(node, 0);
}

void Validator::Context::VisitIf(IfNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(1)->IsValidData())
    ErrorInInput(node, 1);
  if (!node->input(1)->output_type()->is<BoolType>())
    ErrorInInput(node, 1);
}

void Validator::Context::VisitIfFalse(IfFalseNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(0)->is<IfNode>())
    ErrorInInput(node, 0);
}

void Validator::Context::VisitIfTrue(IfTrueNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(0)->is<IfNode>())
    ErrorInInput(node, 0);
}

void Validator::Context::VisitLength(LengthNode* node) {
  auto const pointer_type = node->input(0)->output_type()->as<PointerType>();
  if (!pointer_type)
    return ErrorInInput(node, 0);
  auto const array_type = pointer_type->pointee()->as<ArrayType>();
  if (!array_type)
    return ErrorInInput(node, 0);

  auto const rank_node = node->input(1)->as<Int32Node>();
  if (!rank_node)
    return ErrorInInput(node, 1);
  if (rank_node->data() >= base::checked_cast<int>(array_type->rank()))
    return ErrorInInput(node, 1);
}

void Validator::Context::VisitLoad(LoadNode* node) {
  if (!node->input(0)->IsValidEffect())
    ErrorInInput(node, 0);
  if (!node->input(1)->IsValidData())
    ErrorInInput(node, 1);
  if (!node->input(1)->output_type()->is<PointerType>())
    ErrorInInput(node, 1);
  if (!node->input(2)->IsValidData())
    ErrorInInput(node, 2);
  auto const pointer_type = node->input(2)->output_type()->as<PointerType>();
  if (!pointer_type || node->output_type() != pointer_type->pointee())
    ErrorInInput(node, 1);
}

void Validator::Context::VisitParameter(ParameterNode* node) {
  auto const entry_node = node->input(0)->as<EntryNode>();
  if (!entry_node) {
    ErrorInInput(node, 0);
    return;
  }
  if (node->output_type() != entry_node->parameter_type(node->field())) {
    Error(ErrorCode::ValidateNodeInvalidOutput, node);
    return;
  }
}

void Validator::Context::VisitPhi(PhiNode* node) {
  if (!node->owner()->IsValidControl())
    return Error(ErrorCode::ValidatePhiNodeInvalidOwner, node, node->owner());
  ValidatePhiInputs(node, node->owner(), node->phi_inputs());
}

void Validator::Context::VisitRet(RetNode* node) {
  if (!node->input(0)->IsValidControl())
    ErrorInInput(node, 0);
  if (!node->input(1)->IsValidEffect())
    ErrorInInput(node, 1);
  if (!node->input(2)->IsValidData())
    ErrorInInput(node, 2);
}

//////////////////////////////////////////////////////////////////////
//
// Validator
//
Validator::Validator(Factory* factory, const Function* function)
    : ErrorReporter(factory), factory_(factory), function_(function) {
}

Validator::~Validator() {
}

Node* Validator::NewInt32(int data) {
  return factory_->NewInt32(data);
}

bool Validator::Validate(Node* node) {
  Context context(this);
  node->Accept(&context);
  return context.is_valid();
}

bool Validator::Validate() {
  DepthFirstTraversal<OnInputEdge, const Function> walker;
  Context context(this);
  walker.Traverse(function_, &context);
  return context.is_valid();
}

}  // namespace optimizer
}  // namespace elang
<|endoftext|>


<commit_before>#include <babylon/engines/extensions/multi_render_extension.h>

#include <babylon/core/logging.h>
#include <babylon/engines/thin_engine.h>
#include <babylon/materials/textures/imulti_render_target_options.h>
#include <babylon/materials/textures/internal_texture.h>
#include <babylon/maths/isize.h>

namespace BABYLON {

MultiRenderExtension::MultiRenderExtension(ThinEngine* engine) : _this{engine}
{
}

MultiRenderExtension::~MultiRenderExtension() = default;

void MultiRenderExtension::unBindMultiColorAttachmentFramebuffer(
  const std::vector<InternalTexturePtr>& textures, bool disableGenerateMipMaps,
  const std::function<void()>& onBeforeUnbind)
{
  _this->_currentRenderTarget = nullptr;

  // If MSAA, we need to bitblt back to main texture
  auto& gl = *_this->_gl;

  auto& attachments = textures[0]->_attachments;
  auto count        = attachments.size();

  if (textures[0]->_MSAAFramebuffer) {
    gl.bindFramebuffer(GL::READ_FRAMEBUFFER, textures[0]->_MSAAFramebuffer.get());
    gl.bindFramebuffer(GL::DRAW_FRAMEBUFFER, textures[0]->_framebuffer.get());

    for (size_t i = 0; i < count; ++i) {
      const auto iStr = std::to_string(i);
      auto& texture   = textures[i];

      for (size_t j = 0; j < count; ++j) {
        attachments[j] = GL::NONE;
      }

      attachments[i] = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                        "COLOR_ATTACHMENT" + iStr + "_WEBGL"];
      gl.readBuffer(attachments[i]);
      gl.drawBuffers(attachments);
      gl.blitFramebuffer(0, 0, texture->width, texture->height, 0, 0, texture->width,
                         texture->height, GL::COLOR_BUFFER_BIT, GL::NEAREST);
    }
    for (size_t i = 0; i < count; i++) {
      const auto iStr = std::to_string(i);
      attachments[i]  = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                        "COLOR_ATTACHMENT" + iStr + "_WEBGL"];
    }
    gl.drawBuffers(attachments);
  }

  for (size_t i = 0; i < count; ++i) {
    const auto& texture = textures[i];
    if (texture->generateMipMaps && !disableGenerateMipMaps && !texture->isCube) {
      _this->_bindTextureDirectly(GL::TEXTURE_2D, texture);
      gl.generateMipmap(GL::TEXTURE_2D);
      _this->_bindTextureDirectly(GL::TEXTURE_2D, nullptr);
    }
  }

  if (onBeforeUnbind) {
    if (textures[0]->_MSAAFramebuffer) {
      // Bind the correct framebuffer
      _this->_bindUnboundFramebuffer(textures[0]->_framebuffer);
    }
    onBeforeUnbind();
  }

  _this->_bindUnboundFramebuffer(nullptr);
}

std::vector<InternalTexturePtr>
MultiRenderExtension::createMultipleRenderTarget(ISize size,
                                                 const IMultiRenderTargetOptions& options)
{
  auto generateMipMaps       = options.generateMipMaps.value_or(false);
  auto generateDepthBuffer   = options.generateDepthBuffer.value_or(true);
  auto generateStencilBuffer = options.generateStencilBuffer.value_or(false);
  auto generateDepthTexture  = options.generateDepthTexture.value_or(false);
  auto textureCount          = options.textureCount.value_or(1);

  auto defaultType         = Constants::TEXTURETYPE_UNSIGNED_INT;
  auto defaultSamplingMode = Constants::TEXTURE_TRILINEAR_SAMPLINGMODE;

  const auto& types         = options.types;
  const auto& samplingModes = options.samplingModes;

  auto width  = size.width;
  auto height = size.height;

  auto& gl = *_this->_gl;
  // Create the framebuffer
  auto framebuffer = gl.createFramebuffer();
  _this->_bindUnboundFramebuffer(framebuffer);

  std::vector<InternalTexturePtr> textures;
  std::vector<GL::GLenum> attachments;

  auto depthStencilBuffer = _this->_setupFramebufferDepthAttachments(
    generateStencilBuffer, generateDepthBuffer, width, height);

  for (unsigned int i = 0; i < textureCount; ++i) {
    const auto iStr   = std::to_string(i);
    auto samplingMode = (i < samplingModes.size()) ? samplingModes[i] : defaultSamplingMode;
    auto type         = (i < types.size()) ? types[i] : defaultType;

    if (type == Constants::TEXTURETYPE_FLOAT && !_this->_caps.textureFloatLinearFiltering) {
      // if floating point linear (GL::FLOAT) then force to NEAREST_SAMPLINGMODE
      samplingMode = Constants::TEXTURE_NEAREST_SAMPLINGMODE;
    }
    else if (type == Constants::TEXTURETYPE_HALF_FLOAT
             && !_this->_caps.textureHalfFloatLinearFiltering) {
      // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
      samplingMode = Constants::TEXTURE_NEAREST_SAMPLINGMODE;
    }

    auto filters = _this->_getSamplingParameters(samplingMode, generateMipMaps);
    if (type == Constants::TEXTURETYPE_FLOAT && !_this->_caps.textureFloat) {
      type = Constants::TEXTURETYPE_UNSIGNED_INT;
      BABYLON_LOG_WARN(
        "MultiRenderExtension",
        "Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type")
    }

    auto texture    = InternalTexture::New(_this, InternalTextureSource::MultiRenderTarget);
    auto attachment = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                       "COLOR_ATTACHMENT" + iStr + "_WEBGL"];

    textures.emplace_back(texture);
    attachments.emplace_back(attachment);

    gl.activeTexture(gl["TEXTURE" + iStr]);
    gl.bindTexture(GL::TEXTURE_2D, texture->_webGLTexture.get());

    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MAG_FILTER, filters.mag);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MIN_FILTER, filters.min);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_S, GL::CLAMP_TO_EDGE);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_T, GL::CLAMP_TO_EDGE);

    gl.texImage2D(GL::TEXTURE_2D, 0,
                  static_cast<int>(_this->_getRGBABufferInternalSizedFormat(type)), width, height,
                  0, GL::RGBA, _this->_getWebGLTextureType(type), nullptr);

    gl.framebufferTexture2D(GL::DRAW_FRAMEBUFFER, attachment, GL::TEXTURE_2D,
                            texture->_webGLTexture.get(), 0);

    if (generateMipMaps) {
      gl.generateMipmap(GL::TEXTURE_2D);
    }

    // Unbind
    _this->_bindTextureDirectly(GL::TEXTURE_2D, nullptr);

    texture->_framebuffer           = std::move(framebuffer);
    texture->_depthStencilBuffer    = std::move(depthStencilBuffer);
    texture->baseWidth              = width;
    texture->baseHeight             = height;
    texture->width                  = width;
    texture->height                 = height;
    texture->isReady                = true;
    texture->samples                = 1;
    texture->generateMipMaps        = generateMipMaps;
    texture->samplingMode           = samplingMode;
    texture->type                   = type;
    texture->_generateDepthBuffer   = generateDepthBuffer;
    texture->_generateStencilBuffer = generateStencilBuffer;
    texture->_attachments           = attachments;
    texture->_textureArray          = textures;

    _this->_internalTexturesCache.emplace_back(texture);
  }

  if (generateDepthTexture && _this->_caps.depthTextureExtension) {
    // Depth texture
    auto depthTexture = InternalTexture::New(_this, InternalTextureSource::MultiRenderTarget);

    gl.activeTexture(GL::TEXTURE0);
    gl.bindTexture(GL::TEXTURE_2D, depthTexture->_webGLTexture.get());
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MAG_FILTER, GL::NEAREST);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MIN_FILTER, GL::NEAREST);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_S, GL::CLAMP_TO_EDGE);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_T, GL::CLAMP_TO_EDGE);
    gl.texImage2D(GL::TEXTURE_2D,                                                            //
                  0,                                                                         //
                  _this->webGLVersion() < 2.f ? GL::DEPTH_COMPONENT : GL::DEPTH_COMPONENT16, //
                  width,                                                                     //
                  height,                                                                    //
                  0,                                                                         //
                  GL::DEPTH_COMPONENT,                                                       //
                  GL::UNSIGNED_SHORT,                                                        //
                  nullptr                                                                    //
    );

    gl.framebufferTexture2D(GL::FRAMEBUFFER,                   //
                            GL::DEPTH_ATTACHMENT,              //
                            GL::TEXTURE_2D,                    //
                            depthTexture->_webGLTexture.get(), //
                            0                                  //
    );

    depthTexture->_framebuffer           = std::move(framebuffer);
    depthTexture->baseWidth              = width;
    depthTexture->baseHeight             = height;
    depthTexture->width                  = width;
    depthTexture->height                 = height;
    depthTexture->isReady                = true;
    depthTexture->samples                = 1;
    depthTexture->generateMipMaps        = generateMipMaps;
    depthTexture->samplingMode           = GL::NEAREST;
    depthTexture->_generateDepthBuffer   = generateDepthBuffer;
    depthTexture->_generateStencilBuffer = generateStencilBuffer;

    textures.emplace_back(depthTexture);
    _this->_internalTexturesCache.emplace_back(depthTexture);
  }

  gl.drawBuffers(attachments);
  _this->_bindUnboundFramebuffer(nullptr);

  _this->resetTextureCache();

  return textures;
}

unsigned int MultiRenderExtension::updateMultipleRenderTargetTextureSampleCount(
  const std::vector<InternalTexturePtr>& textures, unsigned int samples)
{
  if (_this->webGLVersion() < 2.f || textures.empty()) {
    return 1;
  }

  if (textures[0]->samples == samples) {
    return samples;
  }

  auto count = textures[0]->_attachments.size();

  if (count == 0) {
    return 1;
  }

  auto& gl = *_this->_gl;

  samples = std::min(samples, _this->getCaps().maxMSAASamples);

  // Dispose previous render buffers
  if (textures[0]->_depthStencilBuffer) {
    gl.deleteRenderbuffer(textures[0]->_depthStencilBuffer.get());
    textures[0]->_depthStencilBuffer = nullptr;
  }

  if (textures[0]->_MSAAFramebuffer) {
    gl.deleteFramebuffer(textures[0]->_MSAAFramebuffer.get());
    textures[0]->_MSAAFramebuffer = nullptr;
  }

  for (size_t i = 0; i < count; i++) {
    const auto& texture = textures[i];
    if (texture->_MSAARenderBuffer) {
      gl.deleteRenderbuffer(texture->_MSAARenderBuffer.get());
      texture->_MSAARenderBuffer = nullptr;
    }
  }

  if (samples > 1) {
    auto framebuffer = gl.createFramebuffer();

    if (!framebuffer) {
      throw std::runtime_error("Unable to create multi sampled framebuffer");
    }

    _this->_bindUnboundFramebuffer(framebuffer);

    auto depthStencilBuffer = _this->_setupFramebufferDepthAttachments(
      textures[0]->_generateStencilBuffer, textures[0]->_generateDepthBuffer, textures[0]->width,
      textures[0]->height, static_cast<int>(samples));

    Uint32Array attachments;

    for (size_t i = 0; i < count; ++i) {
      auto iStr       = std::to_string(i);
      auto& texture   = textures[i];
      auto attachment = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                         "COLOR_ATTACHMENT" + iStr + "_WEBGL"];

      auto colorRenderbuffer = gl.createRenderbuffer();

      if (!colorRenderbuffer) {
        throw std::runtime_error("Unable to create multi sampled framebuffer");
      }

      gl.bindRenderbuffer(GL::RENDERBUFFER, colorRenderbuffer.get());
      gl.renderbufferStorageMultisample(GL::RENDERBUFFER, static_cast<int>(samples),
                                        _this->_getRGBAMultiSampleBufferFormat(texture->type),
                                        texture->width, texture->height);

      gl.framebufferRenderbuffer(GL::FRAMEBUFFER, attachment, GL::RENDERBUFFER,
                                 colorRenderbuffer.get());

      texture->_MSAAFramebuffer    = framebuffer;
      texture->_MSAARenderBuffer   = std::move(colorRenderbuffer);
      texture->samples             = samples;
      texture->_depthStencilBuffer = std::move(depthStencilBuffer);
      gl.bindRenderbuffer(GL::RENDERBUFFER, nullptr);
      attachments.emplace_back(attachment);
    }
    gl.drawBuffers(attachments);
  }
  else {
    _this->_bindUnboundFramebuffer(textures[0]->_framebuffer);
  }

  _this->_bindUnboundFramebuffer(nullptr);

  return samples;
}

void MultiRenderExtension::bindAttachments(const std::vector<unsigned int>& attachments)
{
  auto& gl = *_this->_gl;

  gl.drawBuffers(attachments);
}

void MultiRenderExtension::restoreSingleAttachment()
{
  bindAttachments({GL::BACK});
}

std::vector<unsigned int>
MultiRenderExtension::buildTextureLayout(const std::vector<bool>& textureStatus) const
{
  auto& gl = *_this->_gl;

  std::vector<unsigned int> result;

  for (auto i = 0ull; i < textureStatus.size(); ++i) {
    if (textureStatus[i]) {
      result.emplace_back(gl["COLOR_ATTACHMENT" + std::to_string(i)]);
    }
    else {
      result.emplace_back(GL::NONE);
    }
  }

  return result;
}

} // end of namespace BABYLON
<commit_msg>Formatting and using hardware texture<commit_after>#include <babylon/engines/extensions/multi_render_extension.h>

#include <babylon/core/logging.h>
#include <babylon/engines/thin_engine.h>
#include <babylon/materials/textures/imulti_render_target_options.h>
#include <babylon/materials/textures/internal_texture.h>
#include <babylon/maths/isize.h>

namespace BABYLON {

MultiRenderExtension::MultiRenderExtension(ThinEngine* engine) : _this{engine}
{
}

MultiRenderExtension::~MultiRenderExtension() = default;

void MultiRenderExtension::unBindMultiColorAttachmentFramebuffer(
  const std::vector<InternalTexturePtr>& textures, bool disableGenerateMipMaps,
  const std::function<void()>& onBeforeUnbind)
{
  _this->_currentRenderTarget = nullptr;

  // If MSAA, we need to bitblt back to main texture
  auto& gl = *_this->_gl;

  auto& attachments = textures[0]->_attachments;
  auto count        = attachments.size();

  if (textures[0]->_MSAAFramebuffer) {
    gl.bindFramebuffer(GL::READ_FRAMEBUFFER, textures[0]->_MSAAFramebuffer.get());
    gl.bindFramebuffer(GL::DRAW_FRAMEBUFFER, textures[0]->_framebuffer.get());

    for (size_t i = 0; i < count; ++i) {
      const auto iStr = std::to_string(i);
      auto& texture   = textures[i];

      for (size_t j = 0; j < count; ++j) {
        attachments[j] = GL::NONE;
      }

      attachments[i] = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                        "COLOR_ATTACHMENT" + iStr + "_WEBGL"];
      gl.readBuffer(attachments[i]);
      gl.drawBuffers(attachments);
      gl.blitFramebuffer(0, 0, texture->width, texture->height, 0, 0, texture->width,
                         texture->height, GL::COLOR_BUFFER_BIT, GL::NEAREST);
    }
    for (size_t i = 0; i < count; i++) {
      const auto iStr = std::to_string(i);
      attachments[i]  = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                         "COLOR_ATTACHMENT" + iStr + "_WEBGL"];
    }
    gl.drawBuffers(attachments);
  }

  for (size_t i = 0; i < count; ++i) {
    const auto& texture = textures[i];
    if (texture->generateMipMaps && !disableGenerateMipMaps && !texture->isCube) {
      _this->_bindTextureDirectly(GL::TEXTURE_2D, texture);
      gl.generateMipmap(GL::TEXTURE_2D);
      _this->_bindTextureDirectly(GL::TEXTURE_2D, nullptr);
    }
  }

  if (onBeforeUnbind) {
    if (textures[0]->_MSAAFramebuffer) {
      // Bind the correct framebuffer
      _this->_bindUnboundFramebuffer(textures[0]->_framebuffer);
    }
    onBeforeUnbind();
  }

  _this->_bindUnboundFramebuffer(nullptr);
}

std::vector<InternalTexturePtr> MultiRenderExtension::createMultipleRenderTarget(
  ISize size, const IMultiRenderTargetOptions& options, bool initializeBuffers)
{
  auto generateMipMaps       = options.generateMipMaps.value_or(false);
  auto generateDepthBuffer   = options.generateDepthBuffer.value_or(true);
  auto generateStencilBuffer = options.generateStencilBuffer.value_or(false);
  auto generateDepthTexture  = options.generateDepthTexture.value_or(false);
  auto textureCount          = options.textureCount.value_or(1);

  auto defaultType         = Constants::TEXTURETYPE_UNSIGNED_INT;
  auto defaultSamplingMode = Constants::TEXTURE_TRILINEAR_SAMPLINGMODE;

  const auto& types         = options.types;
  const auto& samplingModes = options.samplingModes;

  auto width  = size.width;
  auto height = size.height;

  auto& gl = *_this->_gl;
  // Create the framebuffer
  auto framebuffer = gl.createFramebuffer();
  _this->_bindUnboundFramebuffer(framebuffer);

  std::vector<InternalTexturePtr> textures;
  std::vector<GL::GLenum> attachments;

  auto depthStencilBuffer = _this->_setupFramebufferDepthAttachments(
    generateStencilBuffer, generateDepthBuffer, width, height);

  for (unsigned int i = 0; i < textureCount; ++i) {
    const auto iStr   = std::to_string(i);
    auto samplingMode = (i < samplingModes.size()) ? samplingModes[i] : defaultSamplingMode;
    auto type         = (i < types.size()) ? types[i] : defaultType;

    if (type == Constants::TEXTURETYPE_FLOAT && !_this->_caps.textureFloatLinearFiltering) {
      // if floating point linear (GL::FLOAT) then force to NEAREST_SAMPLINGMODE
      samplingMode = Constants::TEXTURE_NEAREST_SAMPLINGMODE;
    }
    else if (type == Constants::TEXTURETYPE_HALF_FLOAT
             && !_this->_caps.textureHalfFloatLinearFiltering) {
      // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
      samplingMode = Constants::TEXTURE_NEAREST_SAMPLINGMODE;
    }

    auto filters = _this->_getSamplingParameters(samplingMode, generateMipMaps);
    if (type == Constants::TEXTURETYPE_FLOAT && !_this->_caps.textureFloat) {
      type = Constants::TEXTURETYPE_UNSIGNED_INT;
      BABYLON_LOG_WARN(
        "MultiRenderExtension",
        "Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type")
    }

    auto texture    = InternalTexture::New(_this, InternalTextureSource::MultiRenderTarget);
    auto attachment = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                       "COLOR_ATTACHMENT" + iStr + "_WEBGL"];

    textures.emplace_back(texture);
    attachments.emplace_back(attachment);

    gl.activeTexture(gl["TEXTURE" + iStr]);
    gl.bindTexture(GL::TEXTURE_2D, texture->_hardwareTexture ?
                                     texture->_hardwareTexture->underlyingResource().get() :
                                     nullptr);

    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MAG_FILTER, filters.mag);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MIN_FILTER, filters.min);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_S, GL::CLAMP_TO_EDGE);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_T, GL::CLAMP_TO_EDGE);

    gl.texImage2D(GL::TEXTURE_2D, 0,
                  static_cast<int>(_this->_getRGBABufferInternalSizedFormat(type)), width, height,
                  0, GL::RGBA, _this->_getWebGLTextureType(type), nullptr);

    gl.framebufferTexture2D(
      GL::DRAW_FRAMEBUFFER, attachment, GL::TEXTURE_2D,
      texture->_hardwareTexture ? texture->_hardwareTexture->underlyingResource().get() : nullptr,
      0);

    if (generateMipMaps) {
      gl.generateMipmap(GL::TEXTURE_2D);
    }

    // Unbind
    _this->_bindTextureDirectly(GL::TEXTURE_2D, nullptr);

    texture->_framebuffer           = std::move(framebuffer);
    texture->_depthStencilBuffer    = std::move(depthStencilBuffer);
    texture->baseWidth              = width;
    texture->baseHeight             = height;
    texture->width                  = width;
    texture->height                 = height;
    texture->isReady                = true;
    texture->samples                = 1;
    texture->generateMipMaps        = generateMipMaps;
    texture->samplingMode           = samplingMode;
    texture->type                   = type;
    texture->_generateDepthBuffer   = generateDepthBuffer;
    texture->_generateStencilBuffer = generateStencilBuffer;
    texture->_attachments           = attachments;
    texture->_textureArray          = textures;

    _this->_internalTexturesCache.emplace_back(texture);
  }

  if (generateDepthTexture && _this->_caps.depthTextureExtension) {
    // Depth texture
    auto depthTexture = InternalTexture::New(_this, InternalTextureSource::MultiRenderTarget);
    GL::IGLTexture* depthTextureHardwareTexture
      = depthTexture->_hardwareTexture ?
          depthTexture->_hardwareTexture->underlyingResource().get() :
          nullptr;

    gl.activeTexture(GL::TEXTURE0);
    gl.bindTexture(GL::TEXTURE_2D, depthTextureHardwareTexture);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MAG_FILTER, GL::NEAREST);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_MIN_FILTER, GL::NEAREST);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_S, GL::CLAMP_TO_EDGE);
    gl.texParameteri(GL::TEXTURE_2D, GL::TEXTURE_WRAP_T, GL::CLAMP_TO_EDGE);
    gl.texImage2D(GL::TEXTURE_2D,                                                            //
                  0,                                                                         //
                  _this->webGLVersion() < 2.f ? GL::DEPTH_COMPONENT : GL::DEPTH_COMPONENT16, //
                  width,                                                                     //
                  height,                                                                    //
                  0,                                                                         //
                  GL::DEPTH_COMPONENT,                                                       //
                  GL::UNSIGNED_SHORT,                                                        //
                  nullptr                                                                    //
    );

    gl.framebufferTexture2D(GL::FRAMEBUFFER,             //
                            GL::DEPTH_ATTACHMENT,        //
                            GL::TEXTURE_2D,              //
                            depthTextureHardwareTexture, //
                            0                            //
    );

    depthTexture->_framebuffer           = std::move(framebuffer);
    depthTexture->baseWidth              = width;
    depthTexture->baseHeight             = height;
    depthTexture->width                  = width;
    depthTexture->height                 = height;
    depthTexture->isReady                = true;
    depthTexture->samples                = 1;
    depthTexture->generateMipMaps        = generateMipMaps;
    depthTexture->samplingMode           = GL::NEAREST;
    depthTexture->_generateDepthBuffer   = generateDepthBuffer;
    depthTexture->_generateStencilBuffer = generateStencilBuffer;

    textures.emplace_back(depthTexture);
    _this->_internalTexturesCache.emplace_back(depthTexture);
  }

  if (initializeBuffers) {
    gl.drawBuffers(attachments);
  }

  _this->_bindUnboundFramebuffer(nullptr);

  _this->resetTextureCache();

  return textures;
}

unsigned int MultiRenderExtension::updateMultipleRenderTargetTextureSampleCount(
  const std::vector<InternalTexturePtr>& textures, unsigned int samples, bool initializeBuffers)
{
  if (_this->webGLVersion() < 2.f || textures.empty()) {
    return 1;
  }

  if (textures[0]->samples == samples) {
    return samples;
  }

  auto count = textures[0]->_attachments.size();

  if (count == 0) {
    return 1;
  }

  auto& gl = *_this->_gl;

  samples = std::min(samples, _this->getCaps().maxMSAASamples);

  // Dispose previous render buffers
  if (textures[0]->_depthStencilBuffer) {
    gl.deleteRenderbuffer(textures[0]->_depthStencilBuffer.get());
    textures[0]->_depthStencilBuffer = nullptr;
  }

  if (textures[0]->_MSAAFramebuffer) {
    gl.deleteFramebuffer(textures[0]->_MSAAFramebuffer.get());
    textures[0]->_MSAAFramebuffer = nullptr;
  }

  for (size_t i = 0; i < count; i++) {
    const auto& texture = textures[i];
    if (texture->_MSAARenderBuffer) {
      gl.deleteRenderbuffer(texture->_MSAARenderBuffer.get());
      texture->_MSAARenderBuffer = nullptr;
    }
  }

  if (samples > 1) {
    auto framebuffer = gl.createFramebuffer();

    if (!framebuffer) {
      throw std::runtime_error("Unable to create multi sampled framebuffer");
    }

    _this->_bindUnboundFramebuffer(framebuffer);

    auto depthStencilBuffer = _this->_setupFramebufferDepthAttachments(
      textures[0]->_generateStencilBuffer, textures[0]->_generateDepthBuffer, textures[0]->width,
      textures[0]->height, static_cast<int>(samples));

    Uint32Array attachments;

    for (size_t i = 0; i < count; ++i) {
      auto iStr       = std::to_string(i);
      auto& texture   = textures[i];
      auto attachment = gl[_this->webGLVersion() > 1.f ? "COLOR_ATTACHMENT" + iStr :
                                                         "COLOR_ATTACHMENT" + iStr + "_WEBGL"];

      auto colorRenderbuffer = gl.createRenderbuffer();

      if (!colorRenderbuffer) {
        throw std::runtime_error("Unable to create multi sampled framebuffer");
      }

      gl.bindRenderbuffer(GL::RENDERBUFFER, colorRenderbuffer.get());
      gl.renderbufferStorageMultisample(GL::RENDERBUFFER, static_cast<int>(samples),
                                        _this->_getRGBAMultiSampleBufferFormat(texture->type),
                                        texture->width, texture->height);

      gl.framebufferRenderbuffer(GL::FRAMEBUFFER, attachment, GL::RENDERBUFFER,
                                 colorRenderbuffer.get());

      texture->_MSAAFramebuffer    = framebuffer;
      texture->_MSAARenderBuffer   = std::move(colorRenderbuffer);
      texture->samples             = samples;
      texture->_depthStencilBuffer = std::move(depthStencilBuffer);
      gl.bindRenderbuffer(GL::RENDERBUFFER, nullptr);
      attachments.emplace_back(attachment);
    }
    if (initializeBuffers) {
      gl.drawBuffers(attachments);
    }
  }
  else {
    _this->_bindUnboundFramebuffer(textures[0]->_framebuffer);
  }

  _this->_bindUnboundFramebuffer(nullptr);

  return samples;
}

void MultiRenderExtension::bindAttachments(const std::vector<unsigned int>& attachments)
{
  auto& gl = *_this->_gl;

  gl.drawBuffers(attachments);
}

std::vector<unsigned int>
MultiRenderExtension::buildTextureLayout(const std::vector<bool>& textureStatus) const
{
  auto& gl = *_this->_gl;

  std::vector<unsigned int> result;

  for (auto i = 0ull; i < textureStatus.size(); ++i) {
    if (textureStatus[i]) {
      result.emplace_back(gl["COLOR_ATTACHMENT" + std::to_string(i)]);
    }
    else {
      result.emplace_back(GL::NONE);
    }
  }

  return result;
}

void MultiRenderExtension::restoreSingleAttachment()
{
  bindAttachments({GL::BACK});
}

void MultiRenderExtension::restoreSingleAttachmentForRenderTarget()
{
  bindAttachments({GL::COLOR_ATTACHMENT0});
}

void MultiRenderExtension::clearAttachments(Uint32Array& attachments,
                                            const std::optional<Color4>& colorMain,
                                            const std::optional<Color4>& colorOthers,
                                            bool clearDepth, bool clearStencil)
{
  if (attachments.empty()) {
    return;
  }

  auto& gl = *_this->_gl;

  gl.drawBuffers({attachments[0]});
  _this->clear(colorMain, colorMain.has_value(), clearDepth, clearStencil);

  const auto saveVal = attachments[0];
  attachments[0]     = GL::NONE;

  gl.drawBuffers(attachments);
  _this->clear(colorOthers, colorOthers.has_value(), false, false);

  attachments[0] = saveVal;
}

} // end of namespace BABYLON
<|endoftext|>


<commit_before>/*  This file is part of libDAI - http://www.libdai.org/
 *
 *  Copyright (c) 2006-2011, The libDAI 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 <dai/dai_config.h>
#ifdef DAI_WITH_BP

#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <algorithm>
#include <dai/bp.h>
#include <dai/util.h>
#include <dai/properties.h>

//#define DAI_VERBOSE
#ifdef DAI_VERBOSE
#   define DAI_LOG(MESSAGE) do { std::cout << MESSAGE << std::endl; } while(0)
#else
#   define DAI_LOG(MESSAGE)
#endif

namespace dai {

using namespace std;

void BP::setProperties( const PropertySet &opts ) {
    DAI_ASSERT( opts.hasKey("tol") );
    DAI_ASSERT( opts.hasKey("updates") );

    props.tol = opts.getStringAs<Real>("tol");

    if( opts.hasKey("maxiter") )
        props.maxiter = opts.getStringAs<size_t>("maxiter");
    else
        props.maxiter = 10000;
    if( opts.hasKey("maxtime") )
        props.maxtime = opts.getStringAs<Real>("maxtime");
    else
        props.maxtime = INFINITY;
    if( opts.hasKey("verbose") )
        props.verbose = opts.getStringAs<size_t>("verbose");
    else
        props.verbose = 0;
    if( opts.hasKey("damping") )
        props.damping = opts.getStringAs<Real>("damping");
    else
        props.damping = 0.0;
}

PropertySet BP::getProperties() const {
    PropertySet opts;
    opts.set( "tol", props.tol );
    opts.set( "maxiter", props.maxiter );
    opts.set( "maxtime", props.maxtime );
    opts.set( "verbose", props.verbose );
    opts.set( "damping", props.damping );
    return opts;
}

string BP::printProperties() const {
    stringstream s( stringstream::out );
    s << "[";
    s << "tol=" << props.tol << ",";
    s << "maxiter=" << props.maxiter << ",";
    s << "maxtime=" << props.maxtime << ",";
    s << "verbose=" << props.verbose << ",";
    s << "damping=" << props.damping << "]";
    return s.str();
}

void BP::construct() {
    // create edge properties
    _edges.clear();
    _edges.reserve( nrVars() );
    _oldProd.clear();
    _edge2lutNew.clear();
    _edge2lutNew.reserve( nrVars() );
    for( size_t i = 0; i < nrVars(); ++i ) {
        _edges.push_back( vector<EdgeProp>() );
        _edges[i].reserve( nbV(i).size() );
        _oldProd.push_back(vector<double>(var(i).states(), 1));
        _edge2lutNew.push_back( vector<heap_data_handle>() );
        _edge2lutNew[i].reserve( nbV(i).size() );
        for( const Neighbor &I : nbV(i) ) {
            EdgeProp newEP;
            newEP.message = Prob( var(i).states() );
            newEP.newMessage = Prob( var(i).states() );

            newEP.index.reserve( factor(I).nrStates() );
            for( IndexFor k( var(i), factor(I).vars() ); k.valid(); ++k )
                newEP.index.push_back( k );

            newEP.residual = 0.0;
            _edges[i].push_back( newEP );
            _edge2lutNew[i].push_back( _lutNew.push( make_pair( newEP.residual, make_pair( i, _edges[i].size() - 1 ))));
        }
    }

    // create old beliefs
    _oldBeliefsV.clear();
    _oldBeliefsV.reserve( nrVars() );
    for( size_t i = 0; i < nrVars(); ++i )
        _oldBeliefsV.push_back( Factor( var(i) ) );
    _oldBeliefsF.clear();
    _oldBeliefsF.reserve( nrFactors() );
    for( size_t I = 0; I < nrFactors(); ++I )
        _oldBeliefsF.push_back( Factor( factor(I).vars() ) );

    // create update sequence
    _updateSeq.clear();
    _updateSeq.reserve( nrEdges() );
    for( size_t I = 0; I < nrFactors(); I++ )
        for( const Neighbor &i : nbF(I) )
            _updateSeq.push_back( Edge( i, i.dual ) );
}

void BP::init() {
    Real c = 1.0;
    for( size_t i = 0; i < nrVars(); ++i ) {
        for( const Neighbor &I : nbV(i) ) {
            message( i, I.iter ).fill( c );
            newMessage( i, I.iter ).fill( c );
            updateResidual( i, I.iter, 0.0 );
        }
    }
    _iters = 0;
}

void BP::findMaxResidual( size_t &i, size_t &_I ) {
    DAI_ASSERT( !_lutNew.empty() );
    i  = _lutNew.top().second.first;
    _I = _lutNew.top().second.second;
}

// TODO: Optimize (in progress)
void BP::calcIncomingMessageProduct(Prob &prod, size_t I, bool without_i, size_t i) const {

    // Calculate product of incoming messages and factor I
    for(const Neighbor &j: nbF(I)) {
        if( !(without_i && (j == i)) ) {

            // TODO: the calculation in this loop got very cryptic.
            // One might want to have some explanations at some point...

            // The message that should not go into the product is the one from that
            // node that that message will be sent to. Conveniently, the value is
            // already available: j.dual.
            size_t _I = j.dual;

            // ind is the precalculated IndexFor(j,I) i.e. to x_I == k corresponds x_j == ind[k]
            const ind_t &ind = index(j, _I);
            for(size_t r = 0; r < prod.size(); ++r) {

                // Let's divide by that message that should not go into the product.
                Real prod_jk = _oldProd[j.node][ind[r]] / _edges[j][_I].message._p[ind[r]];

                // And multiply it with the target.
                prod._p[r] *= prod_jk;
            }
        }
    }
}

void BP::calcNewMessage( size_t i, size_t _I) {

    // load
    size_t I = _G.nb1(i)[_I].node;

    // The following applies only rarely  (uV2New1: 50x, uNew1 and u1: 135x)
    // TODO: investigate further, can this still be useful?
    // UPDATE: image segmentation example doesn't converge if this "optimization"
    // is removed. I don't fully get it though. NJU
    if( factor(I).vars().size() == 1 ) // optimization
        newMessage(i,_I) = factor(I).p();
    else {
        // calculate updated message I->i

        // The capacity of _prod is not changed here. malloc/free will be called
        // very rarely. However, this can be further improved, because
        //  _factors[I].p().size() cleanly toggles between 2 and 4:
        // TODO: create two containers _prod4 and _prod2 and cleverly call
        // calcNewMessage() with either one as argument.
        if (_prod.size() != _factors[I].p().size())
            _prod.resize(_factors[I].p().size());
        std::copy(_factors[I].p().begin(), _factors[I].p().end(), _prod.begin());
        calcIncomingMessageProduct(_prod, I, true, i);

        DAI_LOG("calcNewMessage " << I << " <-> " << i);

        // Marginalize onto i
        Prob &marg = newMessage(i,_I);
        std::fill(marg._p.begin(), marg._p.end(), 0.0);
        // ind is the precalculated IndexFor(i,I) i.e. to x_I == k corresponds x_i == ind[k]
        const ind_t ind = index(i,_I);
        for( size_t r = 0; r < _prod.size(); ++r )
            marg._p[ind[r]] = marg[ind[r]] + _prod[r];
        marg.normalizeFast();
    }

    // Update the residual if necessary
    updateResidual( i, _I , distFast( newMessage( i, _I ), message( i, _I ) ) );
}

// BP::run does not check for NANs for performance reasons
// Somehow NaNs do not often occur in BP...
Real BP::run() {
    if( props.verbose >= 1 )
        cerr << "Starting " << identify() << "...";
    if( props.verbose >= 3)
        cerr << endl;

    double tic = toc();

    // do several passes over the network until maximum number of iterations has
    // been reached or until the maximum belief difference is smaller than tolerance
    Real maxDiff = INFINITY;
    for( ; _iters < props.maxiter && maxDiff > props.tol && (toc() - tic) < props.maxtime; _iters++ ) {
        if( _iters == 0 ) {
            // do the first pass
            for( size_t i = 0; i < nrVars(); ++i )
                for( const Neighbor &I : nbV(i) ) {
                    calcNewMessage( i, I.iter);
                }
        }
        // Maximum-Residual BP [\ref EMK06]
        for( size_t t = 0; t < _updateSeq.size(); ++t ) {
            // update the message with the largest residual
            size_t i, _I;
            findMaxResidual( i, _I );
            DAI_LOG("updating message from " << i << " to " << _I);
            updateMessage( i, _I );

            // I->i has been updated, which means that residuals for all
            // J->j with J in nb[i]\I and j in nb[J]\i have to be updated
            for( const Neighbor &J: nbV(i) ) {
                if( J.iter != _I ) {
                    for( const Neighbor &j: nbF(J) ) {
                        size_t _J = j.dual;
                        if( j != i )
                            calcNewMessage( j, _J);
                    }
                }
            }
        }

        // calculate new beliefs and compare with old ones
        maxDiff = -INFINITY;
        for( size_t i = 0; i < nrVars(); ++i ) {
            Factor b( beliefV(i) );
            maxDiff = std::max( maxDiff, distFast( b.p(), _oldBeliefsV[i].p() ) );
            _oldBeliefsV[i] = b;
        }
        for( size_t I = 0; I < nrFactors(); ++I ) {
            Factor b( beliefF(I) );
            maxDiff = std::max( maxDiff, distFast( b.p(), _oldBeliefsF[I].p() ) );
            _oldBeliefsF[I] = b;
        }

        if( props.verbose >= 3 )
            cerr << name() << "::run:  maxdiff " << maxDiff << " after " << _iters+1 << " passes" << endl;
    }

    if( maxDiff > _maxdiff )
        _maxdiff = maxDiff;

    if( props.verbose >= 1 ) {
        if( maxDiff > props.tol ) {
            if( props.verbose == 1 )
                cerr << endl;
                cerr << name() << "::run:  WARNING: not converged after " << _iters << " passes (" << toc() - tic << " seconds)...final maxdiff:" << maxDiff << endl;
        } else {
            if( props.verbose >= 3 )
                cerr << name() << "::run:  ";
                cerr << "converged in " << _iters << " passes (" << toc() - tic << " seconds)." << endl;
        }
    }

    return maxDiff;
}

void BP::calcBeliefV( size_t i, Prob &p ) const {
    p = Prob( var(i).states(), 1.0);
    for ( const Neighbor &I : nbV(i) )
            p *= newMessage( i, I.iter );
}

Factor BP::beliefV( size_t i ) const {
    Prob p;
    calcBeliefV( i, p );
    p.normalize();

    return( Factor( var(i), p ) );
}

Factor BP::beliefF( size_t I ) const {
    Factor Fprod( factor(I) );
    Prob &p = Fprod.p();
    calcBeliefF( I, p );
    p.normalize();

    return( Factor( factor(I).vars(), p ) );
}

vector<Factor> BP::beliefs() const {
    vector<Factor> result;
    for( size_t i = 0; i < nrVars(); ++i )
        result.push_back( beliefV(i) );
    for( size_t I = 0; I < nrFactors(); ++I )
        result.push_back( beliefF(I) );
    return result;
}

Factor BP::belief( const VarSet &ns ) const {
    if( ns.size() == 0 )
        return Factor();
    else if( ns.size() == 1 )
        return beliefV( findVar( *(ns.begin() ) ) );
    else {
        size_t I;
        for( I = 0; I < nrFactors(); I++ )
            if( factor(I).vars() >> ns )
                break;
        if( I == nrFactors() )
            DAI_THROW(BELIEF_NOT_AVAILABLE);
        return beliefF(I).marginal(ns);
    }
}

Real BP::logZ() const {
    Real sum = 0.0;
    for( size_t i = 0; i < nrVars(); ++i )
        sum += (1.0 - nbV(i).size()) * beliefV(i).entropy();
    for( size_t I = 0; I < nrFactors(); ++I )
        sum -= dist( beliefF(I), factor(I), DISTKL );
    return sum;
}

void BP::init( const VarSet &ns ) {
    for( VarSet::const_iterator n = ns.begin(); n != ns.end(); ++n ) {
        size_t ni = findVar( *n );
        for( const Neighbor &I : nbV( ni ) ) {
            Real val = 1.0;
            message( ni, I.iter ).fill( val );
            newMessage( ni, I.iter ).fill( val );
            updateResidual( ni, I.iter, 0.0 );
        }
    }
    _iters = 0;
}

void BP::updateMessage( size_t i, size_t _I ) {
    for (size_t j=0; j<_oldProd[i].size(); ++j) {
        _oldProd[i][j] =  _edges[i][_I].newMessage._p[j] * _oldProd[i][j] / _edges[i][_I].message._p[j];
    }

    if( recordSentMessages )
        _sentMessages.push_back(make_pair(i,_I));
    if( props.damping == 0.0 ) {
        message(i,_I) = newMessage(i,_I);
        updateResidual( i, _I, 0.0 );
    } else {
        message(i,_I) = (message(i,_I) ^ props.damping) * (newMessage(i,_I) ^ (1.0 - props.damping));
        updateResidual( i, _I, distFast( newMessage(i,_I), message(i,_I) ) );
    }
}

// TODO: Optimize: We are using a heap now but this is not faster then the multimap solution. So we might have to revert to it.
void BP::updateResidual( size_t i, size_t _I, Real r ) {
    EdgeProp* pEdge = &_edges[i][_I];
    pEdge->residual = r;

    // rearrange look-up table (delete and reinsert new key)
    _lutNew.update(_edge2lutNew[i][_I], make_pair( r, make_pair(i, _I) ));
}

} // end of namespace dai

#endif
<commit_msg>Access storage more directly. (No speedup measured)<commit_after>/*  This file is part of libDAI - http://www.libdai.org/
 *
 *  Copyright (c) 2006-2011, The libDAI 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 <dai/dai_config.h>
#ifdef DAI_WITH_BP

#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <algorithm>
#include <dai/bp.h>
#include <dai/util.h>
#include <dai/properties.h>

//#define DAI_VERBOSE
#ifdef DAI_VERBOSE
#   define DAI_LOG(MESSAGE) do { std::cout << MESSAGE << std::endl; } while(0)
#else
#   define DAI_LOG(MESSAGE)
#endif

namespace dai {

using namespace std;

void BP::setProperties( const PropertySet &opts ) {
    DAI_ASSERT( opts.hasKey("tol") );
    DAI_ASSERT( opts.hasKey("updates") );

    props.tol = opts.getStringAs<Real>("tol");

    if( opts.hasKey("maxiter") )
        props.maxiter = opts.getStringAs<size_t>("maxiter");
    else
        props.maxiter = 10000;
    if( opts.hasKey("maxtime") )
        props.maxtime = opts.getStringAs<Real>("maxtime");
    else
        props.maxtime = INFINITY;
    if( opts.hasKey("verbose") )
        props.verbose = opts.getStringAs<size_t>("verbose");
    else
        props.verbose = 0;
    if( opts.hasKey("damping") )
        props.damping = opts.getStringAs<Real>("damping");
    else
        props.damping = 0.0;
}

PropertySet BP::getProperties() const {
    PropertySet opts;
    opts.set( "tol", props.tol );
    opts.set( "maxiter", props.maxiter );
    opts.set( "maxtime", props.maxtime );
    opts.set( "verbose", props.verbose );
    opts.set( "damping", props.damping );
    return opts;
}

string BP::printProperties() const {
    stringstream s( stringstream::out );
    s << "[";
    s << "tol=" << props.tol << ",";
    s << "maxiter=" << props.maxiter << ",";
    s << "maxtime=" << props.maxtime << ",";
    s << "verbose=" << props.verbose << ",";
    s << "damping=" << props.damping << "]";
    return s.str();
}

void BP::construct() {
    // create edge properties
    _edges.clear();
    _edges.reserve( nrVars() );
    _oldProd.clear();
    _edge2lutNew.clear();
    _edge2lutNew.reserve( nrVars() );
    for( size_t i = 0; i < nrVars(); ++i ) {
        _edges.push_back( vector<EdgeProp>() );
        _edges[i].reserve( nbV(i).size() );
        _oldProd.push_back(vector<double>(var(i).states(), 1));
        _edge2lutNew.push_back( vector<heap_data_handle>() );
        _edge2lutNew[i].reserve( nbV(i).size() );
        for( const Neighbor &I : nbV(i) ) {
            EdgeProp newEP;
            newEP.message = Prob( var(i).states() );
            newEP.newMessage = Prob( var(i).states() );

            newEP.index.reserve( factor(I).nrStates() );
            for( IndexFor k( var(i), factor(I).vars() ); k.valid(); ++k )
                newEP.index.push_back( k );

            newEP.residual = 0.0;
            _edges[i].push_back( newEP );
            _edge2lutNew[i].push_back( _lutNew.push( make_pair( newEP.residual, make_pair( i, _edges[i].size() - 1 ))));
        }
    }

    // create old beliefs
    _oldBeliefsV.clear();
    _oldBeliefsV.reserve( nrVars() );
    for( size_t i = 0; i < nrVars(); ++i )
        _oldBeliefsV.push_back( Factor( var(i) ) );
    _oldBeliefsF.clear();
    _oldBeliefsF.reserve( nrFactors() );
    for( size_t I = 0; I < nrFactors(); ++I )
        _oldBeliefsF.push_back( Factor( factor(I).vars() ) );

    // create update sequence
    _updateSeq.clear();
    _updateSeq.reserve( nrEdges() );
    for( size_t I = 0; I < nrFactors(); I++ )
        for( const Neighbor &i : nbF(I) )
            _updateSeq.push_back( Edge( i, i.dual ) );
}

void BP::init() {
    Real c = 1.0;
    for( size_t i = 0; i < nrVars(); ++i ) {
        for( const Neighbor &I : nbV(i) ) {
            message( i, I.iter ).fill( c );
            newMessage( i, I.iter ).fill( c );
            updateResidual( i, I.iter, 0.0 );
        }
    }
    _iters = 0;
}

void BP::findMaxResidual( size_t &i, size_t &_I ) {
    DAI_ASSERT( !_lutNew.empty() );
    i  = _lutNew.top().second.first;
    _I = _lutNew.top().second.second;
}

// TODO: Optimize (in progress)
void BP::calcIncomingMessageProduct(Prob &prod, size_t I, bool without_i, size_t i) const {

    // Calculate product of incoming messages and factor I
    for(const Neighbor &j: nbF(I)) {
        if( !(without_i && (j == i)) ) {

            // TODO: the calculation in this loop got very cryptic.
            // One might want to have some explanations at some point...

            // The message that should not go into the product is the one from that
            // node that that message will be sent to. Conveniently, the value is
            // already available: j.dual.
            size_t _I = j.dual;

            // ind is the precalculated IndexFor(j,I) i.e. to x_I == k corresponds x_j == ind[k]
            const ind_t &ind = index(j, _I);
            for(size_t r = 0; r < prod.size(); ++r) {

                // Let's divide by that message that should not go into the product.
                Real prod_jk = _oldProd[j.node][ind[r]] / _edges[j][_I].message._p[ind[r]];

                // And multiply it with the target.
                prod._p[r] *= prod_jk;
            }
        }
    }
}

void BP::calcNewMessage( size_t i, size_t _I) {

    // load
    size_t I = _G.nb1(i)[_I].node;

    // The following applies only rarely  (uV2New1: 50x, uNew1 and u1: 135x)
    // TODO: investigate further, can this still be useful?
    // UPDATE: image segmentation example doesn't converge if this "optimization"
    // is removed. I don't fully get it though. NJU
    if( _factors[I].vars().size() == 1 ) {    // optimization
        std::copy(_factors[I].p().begin(),
                  _factors[I].p().end(),
                  newMessage(i,_I)._p.begin());
    }
    else {
        // calculate updated message I->i

        // The capacity of _prod is not changed here. malloc/free will be called
        // very rarely. However, this can be further improved, because
        //  _factors[I].p().size() cleanly toggles between 2 and 4:
        // TODO: create two containers _prod4 and _prod2 and cleverly call
        // calcNewMessage() with either one as argument.
        if (_prod.size() != _factors[I].p().size())
            _prod.resize(_factors[I].p().size());
        std::copy(_factors[I].p().begin(), _factors[I].p().end(), _prod.begin());
        calcIncomingMessageProduct(_prod, I, true, i);

        DAI_LOG("calcNewMessage " << I << " <-> " << i);

        // Marginalize onto i
        Prob &marg = newMessage(i,_I);
        std::fill(marg._p.begin(), marg._p.end(), 0.0);
        // ind is the precalculated IndexFor(i,I) i.e. to x_I == k corresponds x_i == ind[k]
        const ind_t ind = index(i,_I);
        for( size_t r = 0; r < _prod.size(); ++r )
            marg._p[ind[r]] = marg[ind[r]] + _prod[r];
        marg.normalizeFast();
    }

    // Update the residual if necessary
    updateResidual( i, _I , distFast( newMessage( i, _I ), message( i, _I ) ) );
}

// BP::run does not check for NANs for performance reasons
// Somehow NaNs do not often occur in BP...
Real BP::run() {
    if( props.verbose >= 1 )
        cerr << "Starting " << identify() << "...";
    if( props.verbose >= 3)
        cerr << endl;

    double tic = toc();

    // do several passes over the network until maximum number of iterations has
    // been reached or until the maximum belief difference is smaller than tolerance
    Real maxDiff = INFINITY;
    for( ; _iters < props.maxiter && maxDiff > props.tol && (toc() - tic) < props.maxtime; _iters++ ) {
        if( _iters == 0 ) {
            // do the first pass
            for( size_t i = 0; i < nrVars(); ++i )
                for( const Neighbor &I : nbV(i) ) {
                    calcNewMessage( i, I.iter);
                }
        }
        // Maximum-Residual BP [\ref EMK06]
        for( size_t t = 0; t < _updateSeq.size(); ++t ) {
            // update the message with the largest residual
            size_t i, _I;
            findMaxResidual( i, _I );
            DAI_LOG("updating message from " << i << " to " << _I);
            updateMessage( i, _I );

            // I->i has been updated, which means that residuals for all
            // J->j with J in nb[i]\I and j in nb[J]\i have to be updated
            for( const Neighbor &J: nbV(i) ) {
                if( J.iter != _I ) {
                    for( const Neighbor &j: nbF(J) ) {
                        size_t _J = j.dual;
                        if( j != i )
                            calcNewMessage( j, _J);
                    }
                }
            }
        }

        // calculate new beliefs and compare with old ones
        maxDiff = -INFINITY;
        for( size_t i = 0; i < nrVars(); ++i ) {
            Factor b( beliefV(i) );
            maxDiff = std::max( maxDiff, distFast( b.p(), _oldBeliefsV[i].p() ) );
            _oldBeliefsV[i] = b;
        }
        for( size_t I = 0; I < nrFactors(); ++I ) {
            Factor b( beliefF(I) );
            maxDiff = std::max( maxDiff, distFast( b.p(), _oldBeliefsF[I].p() ) );
            _oldBeliefsF[I] = b;
        }

        if( props.verbose >= 3 )
            cerr << name() << "::run:  maxdiff " << maxDiff << " after " << _iters+1 << " passes" << endl;
    }

    if( maxDiff > _maxdiff )
        _maxdiff = maxDiff;

    if( props.verbose >= 1 ) {
        if( maxDiff > props.tol ) {
            if( props.verbose == 1 )
                cerr << endl;
                cerr << name() << "::run:  WARNING: not converged after " << _iters << " passes (" << toc() - tic << " seconds)...final maxdiff:" << maxDiff << endl;
        } else {
            if( props.verbose >= 3 )
                cerr << name() << "::run:  ";
                cerr << "converged in " << _iters << " passes (" << toc() - tic << " seconds)." << endl;
        }
    }

    return maxDiff;
}

void BP::calcBeliefV( size_t i, Prob &p ) const {
    p = Prob( var(i).states(), 1.0);
    for ( const Neighbor &I : nbV(i) )
            p *= newMessage( i, I.iter );
}

Factor BP::beliefV( size_t i ) const {
    Prob p;
    calcBeliefV( i, p );
    p.normalize();

    return( Factor( var(i), p ) );
}

Factor BP::beliefF( size_t I ) const {
    Factor Fprod( factor(I) );
    Prob &p = Fprod.p();
    calcBeliefF( I, p );
    p.normalize();

    return( Factor( factor(I).vars(), p ) );
}

vector<Factor> BP::beliefs() const {
    vector<Factor> result;
    for( size_t i = 0; i < nrVars(); ++i )
        result.push_back( beliefV(i) );
    for( size_t I = 0; I < nrFactors(); ++I )
        result.push_back( beliefF(I) );
    return result;
}

Factor BP::belief( const VarSet &ns ) const {
    if( ns.size() == 0 )
        return Factor();
    else if( ns.size() == 1 )
        return beliefV( findVar( *(ns.begin() ) ) );
    else {
        size_t I;
        for( I = 0; I < nrFactors(); I++ )
            if( factor(I).vars() >> ns )
                break;
        if( I == nrFactors() )
            DAI_THROW(BELIEF_NOT_AVAILABLE);
        return beliefF(I).marginal(ns);
    }
}

Real BP::logZ() const {
    Real sum = 0.0;
    for( size_t i = 0; i < nrVars(); ++i )
        sum += (1.0 - nbV(i).size()) * beliefV(i).entropy();
    for( size_t I = 0; I < nrFactors(); ++I )
        sum -= dist( beliefF(I), factor(I), DISTKL );
    return sum;
}

void BP::init( const VarSet &ns ) {
    for( VarSet::const_iterator n = ns.begin(); n != ns.end(); ++n ) {
        size_t ni = findVar( *n );
        for( const Neighbor &I : nbV( ni ) ) {
            Real val = 1.0;
            message( ni, I.iter ).fill( val );
            newMessage( ni, I.iter ).fill( val );
            updateResidual( ni, I.iter, 0.0 );
        }
    }
    _iters = 0;
}

void BP::updateMessage( size_t i, size_t _I ) {
    for (size_t j=0; j<_oldProd[i].size(); ++j) {
        _oldProd[i][j] =  _edges[i][_I].newMessage._p[j] * _oldProd[i][j] / _edges[i][_I].message._p[j];
    }

    if( recordSentMessages )
        _sentMessages.push_back(make_pair(i,_I));
    if( props.damping == 0.0 ) {
        message(i,_I) = newMessage(i,_I);
        updateResidual( i, _I, 0.0 );
    } else {
        message(i,_I) = (message(i,_I) ^ props.damping) * (newMessage(i,_I) ^ (1.0 - props.damping));
        updateResidual( i, _I, distFast( newMessage(i,_I), message(i,_I) ) );
    }
}

// TODO: Optimize: We are using a heap now but this is not faster then the multimap solution. So we might have to revert to it.
void BP::updateResidual( size_t i, size_t _I, Real r ) {
    EdgeProp* pEdge = &_edges[i][_I];
    pEdge->residual = r;

    // rearrange look-up table (delete and reinsert new key)
    _lutNew.update(_edge2lutNew[i][_I], make_pair( r, make_pair(i, _I) ));
}

} // end of namespace dai

#endif
<|endoftext|>


<commit_before>// This file is part of UDPipe <http://github.com/ufal/udpipe/>.
//
// Copyright 2016 Institute of Formal and Applied Linguistics, Faculty of
// Mathematics and Physics, Charles University in Prague, Czech Republic.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <fstream>
#include <sstream>

#include "common.h"
#include "model/evaluator.h"
#include "model/model.h"
#include "model/pipeline.h"
#include "sentence/input_format.h"
#include "trainer/trainer.h"
#include "utils/getpara.h"
#include "utils/iostreams.h"
#include "utils/options.h"
#include "utils/process_args.h"
#include "version/version.h"

using namespace ufal::udpipe;

bool append_conllu(istream& is, vector<sentence>& sentences, string& error) {
  unique_ptr<input_format> conllu_input(input_format::new_conllu_input_format());

  string block;
  while (conllu_input->read_block(is, block)) {
    conllu_input->set_text(block);
    while (sentences.emplace_back(), conllu_input->next_sentence(sentences.back(), error)) ;
    sentences.pop_back();
    if (!error.empty()) return false;
  }
  return true;
}

int main(int argc, char* argv[]) {
  iostreams_init();

  options::map options;
  if (!options::parse({{"accuracy", options::value::none},
                       {"method", options::value{"morphodita_parsito"}},
                       {"heldout", options::value::any},
                       {"input", options::value::any},
                       {"outfile", options::value::any},
                       {"output", options::value::any},
                       {"parse", options::value::none},
                       {"parser", options::value::any},
                       {"tag", options::value::none},
                       {"tagger", options::value::any},
                       {"tokenize", options::value::none},
                       {"tokenizer", options::value::any},
                       {"train", options::value::none},
                       {"version", options::value::none},
                       {"help", options::value::none}}, argc, argv, options) ||
      options.count("help") ||
      (argc < 2 && !options.count("version")))
    runtime_failure("Usage: " << argv[0] << " --train [training_options] model_file [input_files]\n"
                    "       " << argv[0] << " [running_options] model_file [input_files]\n"
                    "Running options: --accuracy (measure accuracy only)\n"
                    "                 --input=[conllu|horizontal|vertical]\n"
                    "                 --outfile=output file template\n"
                    "                 --output=[conllu|matxin|horizontal|vertical]\n"
                    "                 --tokenize (perform tokenization)\n"
                    "                 --tokenizer=tokenizer options, implies --tokenize\n"
                    "                 --tag (perform tagging)\n"
                    "                 --tagger=tagger options, implies --tag\n"
                    "                 --parse (perform parsing)\n"
                    "                 --parser=parser options, implies --parse\n"
                    "Training options: --method=[morphodita_parsito] which method to use\n"
                    "                  --heldout=heldout data file name\n"
                    "                  --tokenizer=tokenizer options\n"
                    "                  --tagger=tagger options\n"
                    "                  --parser=parser options\n"
                    "Generic options: --version\n"
                    "                 --help");
  if (options.count("version"))
    return cout << version::version_and_copyright() << endl, 0;

  if (options.count("train")) {
    string error;

    // Load training data
    cerr << "Loading training data: " << flush;
    vector<sentence> training;
    for (int i = 2; i < argc; i++) {
      ifstream input(argv[i]);
      if (!input.is_open()) runtime_failure("Cannot open input file '" << argv[i] << "'!");
      if (!append_conllu(input, training, error))
        runtime_failure("Cannot load training data from file '" << argv[i] << "': " << error);
    }
    if (argc == 2 && !append_conllu(cin, training, error)) runtime_failure("Cannot load training data: " << error);
    cerr << "done." << endl;

    // Load heldout data
    vector<sentence> heldout;
    if (options.count("heldout")) {
      ifstream input(options["heldout"]);
      if (!input.is_open()) runtime_failure("Cannot open heldout data file '" << options["heldout"] << "'!");
      if (!append_conllu(input, heldout, error))
        runtime_failure("Cannot load heldout data from file '" << options["heldout"] << "': " << error);
    }

    // Open output file
    ofstream model(argv[1], ofstream::binary);
    if (!model.is_open()) runtime_failure("Cannot open model file '" << argv[1] << "' for writing.");

    // Train the model
    cerr << "Training the UDPipe model." << endl;
    string method = options.count("method") ? options["method"] : "morphodita_parsito";
    if (!trainer::train(method, training, heldout, options["tokenizer"], options["tagger"], options["parser"], model, error))
      runtime_failure("An error occurred during model training: " << error);
    cerr << "The trained UDPipe model was saved." << endl;
  } else {
    // Load the model if needed
    unique_ptr<model> model;
    if (options.count("tokenizer") || options.count("tokenize") ||
        options.count("tagger") || options.count("tag") ||
        options.count("parser") || options.count("parse")) {
      cerr << "Loading UDPipe model: " << flush;
      model.reset(model::load(argv[1]));
      if (!model) runtime_failure("Cannot load UDPipe model '" << argv[1] << "'!");
      cerr << "done." << endl;
    }

    if (options.count("accuracy")) {
      if (options.count("input")) runtime_failure("The --input option is unsupported when --accuracy is used!");
      if (options.count("output")) runtime_failure("The --output option is unsupported when --accuracy is used!");

      // Prepare the evaluator
      evaluator evaluator(model.get(), options.count("tokenizer") ? options["tokenizer"] : options.count("tokenize") ? pipeline::DEFAULT : pipeline::NONE,
                          options.count("tagger") ? options["tagger"] : options.count("tag") ? pipeline::DEFAULT : pipeline::NONE,
                          options.count("parser") ? options["parser"] : options.count("parse") ? pipeline::DEFAULT : pipeline::NONE);

      // Process the data
      process_args_with_output_template(2, argc, argv, options["outfile"], [&evaluator](istream& is, ostream& os) {
        string error;
        if (!evaluator.evaluate(is, os, error))
            runtime_failure("An error occurred during UDPipe execution: " << error);
      });
    } else {
      // Prepare the pipeline
      pipeline pipeline(model.get(), options.count("tokenizer") ? "tokenizer=" + options["tokenizer"] : options.count("tokenize") ? "tokenizer" : options.count("input") ? options["input"] : "conllu",
                        options.count("tagger") ? options["tagger"] : options.count("tag") ? pipeline::DEFAULT : pipeline::NONE,
                        options.count("parser") ? options["parser"] : options.count("parse") ? pipeline::DEFAULT : pipeline::NONE,
                        options.count("output") ? options["output"] : "conllu");

      // Process the data
      process_args_with_output_template(2, argc, argv, options["outfile"], [&pipeline](istream& is, ostream& os) {
        string error;
        if (!pipeline.process(is, os, error))
          runtime_failure("An error occurred during UDPipe execution: " << error);
      });
    }
  }

  return 0;
}
<commit_msg>Add --detokenize mode for UDPipe.<commit_after>// This file is part of UDPipe <http://github.com/ufal/udpipe/>.
//
// Copyright 2016 Institute of Formal and Applied Linguistics, Faculty of
// Mathematics and Physics, Charles University in Prague, Czech Republic.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <fstream>
#include <sstream>

#include "common.h"
#include "model/evaluator.h"
#include "model/model.h"
#include "model/pipeline.h"
#include "sentence/input_format.h"
#include "sentence/output_format.h"
#include "tokenizer/detokenizer.h"
#include "trainer/trainer.h"
#include "utils/getpara.h"
#include "utils/iostreams.h"
#include "utils/options.h"
#include "utils/process_args.h"
#include "version/version.h"

using namespace ufal::udpipe;

bool append_conllu(istream& is, vector<sentence>& sentences, string& error) {
  unique_ptr<input_format> conllu_input(input_format::new_conllu_input_format());

  string block;
  while (conllu_input->read_block(is, block)) {
    conllu_input->set_text(block);
    while (sentences.emplace_back(), conllu_input->next_sentence(sentences.back(), error)) ;
    sentences.pop_back();
    if (!error.empty()) return false;
  }
  return true;
}

int main(int argc, char* argv[]) {
  iostreams_init();

  options::map options;
  if (!options::parse({{"accuracy", options::value::none},
                       {"detokenize", options::value::none},
                       {"method", options::value{"morphodita_parsito"}},
                       {"heldout", options::value::any},
                       {"input", options::value::any},
                       {"outfile", options::value::any},
                       {"output", options::value::any},
                       {"parse", options::value::none},
                       {"parser", options::value::any},
                       {"tag", options::value::none},
                       {"tagger", options::value::any},
                       {"tokenize", options::value::none},
                       {"tokenizer", options::value::any},
                       {"train", options::value::none},
                       {"version", options::value::none},
                       {"help", options::value::none}}, argc, argv, options) ||
      options.count("help") ||
      (argc < 2 && !options.count("version")))
    runtime_failure("Usage: " << argv[0] << " [running_opts] model_file [input_files]\n"
                    "       " << argv[0] << " --train [training_opts] model_file [input_files]\n"
                    "       " << argv[0] << " --detokenize [detokenize_opts] raw_text_file [input_files]\n"
                    "Running opts: --accuracy (measure accuracy only)\n"
                    "              --input=[conllu|horizontal|vertical]\n"
                    "              --outfile=output file template\n"
                    "              --output=[conllu|matxin|horizontal|vertical]\n"
                    "              --tokenize (perform tokenization)\n"
                    "              --tokenizer=tokenizer options, implies --tokenize\n"
                    "              --tag (perform tagging)\n"
                    "              --tagger=tagger options, implies --tag\n"
                    "              --parse (perform parsing)\n"
                    "              --parser=parser options, implies --parse\n"
                    "Training opts: --method=[morphodita_parsito] which method to use\n"
                    "               --heldout=heldout data file name\n"
                    "               --tokenizer=tokenizer options\n"
                    "               --tagger=tagger options\n"
                    "               --parser=parser options\n"
                    "Detokenize opts: --outfile=output file template\n"
                    "Generic opts: --version\n"
                    "              --help");
  if (options.count("version"))
    return cout << version::version_and_copyright() << endl, 0;

  // TRAINING
  if (options.count("train")) {
    string error;

    // Load training data
    cerr << "Loading training data: " << flush;
    vector<sentence> training;
    for (int i = 2; i < argc; i++) {
      ifstream input(argv[i]);
      if (!input.is_open()) runtime_failure("Cannot open input file '" << argv[i] << "'!");
      if (!append_conllu(input, training, error))
        runtime_failure("Cannot load training data from file '" << argv[i] << "': " << error);
    }
    if (argc == 2 && !append_conllu(cin, training, error)) runtime_failure("Cannot load training data: " << error);
    cerr << "done." << endl;

    // Load heldout data
    vector<sentence> heldout;
    if (options.count("heldout")) {
      ifstream input(options["heldout"]);
      if (!input.is_open()) runtime_failure("Cannot open heldout data file '" << options["heldout"] << "'!");
      if (!append_conllu(input, heldout, error))
        runtime_failure("Cannot load heldout data from file '" << options["heldout"] << "': " << error);
    }

    // Open output file
    ofstream model(argv[1], ofstream::binary);
    if (!model.is_open()) runtime_failure("Cannot open model file '" << argv[1] << "' for writing.");

    // Train the model
    cerr << "Training the UDPipe model." << endl;
    string method = options.count("method") ? options["method"] : "morphodita_parsito";
    if (!trainer::train(method, training, heldout, options["tokenizer"], options["tagger"], options["parser"], model, error))
      runtime_failure("An error occurred during model training: " << error);
    cerr << "The trained UDPipe model was saved." << endl;
  } else
  // DETOKENIZE
  if (options.count("detokenize")) {
    // Detokenize CoNLL-U files
    ifstream raw_text_file(argv[1]);
    if (!raw_text_file.is_open()) runtime_failure("Cannot load raw text from file '" << argv[1] << "'.");

    string raw_text;
    for (char c; raw_text_file.get(c); )
      raw_text.push_back(c);
    raw_text_file.close();

    detokenizer detokenizer(raw_text);
    process_args_with_output_template(2, argc, argv, options["outfile"], [&detokenizer](istream& is, ostream& os) {
      unique_ptr<input_format> conllu_input(input_format::new_conllu_input_format());
      unique_ptr<output_format> conllu_output(output_format::new_conllu_output_format());

      sentence s;
      string block, error;
      while (conllu_input->read_block(is, block)) {
        conllu_input->set_text(block);
        while (conllu_input->next_sentence(s, error)) {
          detokenizer.detokenize(s);
          conllu_output->write_sentence(s, os);
        }
        if (!error.empty()) runtime_failure("An error occurred during UDPipe execution: " << error);
      }
      conllu_output->finish_document(os);
    });
  } else
  // RUN
  {
    // Load the model if needed
    unique_ptr<model> model;
    if (options.count("tokenizer") || options.count("tokenize") ||
        options.count("tagger") || options.count("tag") ||
        options.count("parser") || options.count("parse")) {
      cerr << "Loading UDPipe model: " << flush;
      model.reset(model::load(argv[1]));
      if (!model) runtime_failure("Cannot load UDPipe model '" << argv[1] << "'!");
      cerr << "done." << endl;
    }

    if (options.count("accuracy")) {
      if (options.count("input")) runtime_failure("The --input option is unsupported when --accuracy is used!");
      if (options.count("output")) runtime_failure("The --output option is unsupported when --accuracy is used!");

      // Prepare the evaluator
      evaluator evaluator(model.get(), options.count("tokenizer") ? options["tokenizer"] : options.count("tokenize") ? pipeline::DEFAULT : pipeline::NONE,
                          options.count("tagger") ? options["tagger"] : options.count("tag") ? pipeline::DEFAULT : pipeline::NONE,
                          options.count("parser") ? options["parser"] : options.count("parse") ? pipeline::DEFAULT : pipeline::NONE);

      // Process the data
      process_args_with_output_template(2, argc, argv, options["outfile"], [&evaluator](istream& is, ostream& os) {
        string error;
        if (!evaluator.evaluate(is, os, error))
            runtime_failure("An error occurred during UDPipe execution: " << error);
      });
    } else {
      // Prepare the pipeline
      pipeline pipeline(model.get(), options.count("tokenizer") ? "tokenizer=" + options["tokenizer"] : options.count("tokenize") ? "tokenizer" : options.count("input") ? options["input"] : "conllu",
                        options.count("tagger") ? options["tagger"] : options.count("tag") ? pipeline::DEFAULT : pipeline::NONE,
                        options.count("parser") ? options["parser"] : options.count("parse") ? pipeline::DEFAULT : pipeline::NONE,
                        options.count("output") ? options["output"] : "conllu");

      // Process the data
      process_args_with_output_template(2, argc, argv, options["outfile"], [&pipeline](istream& is, ostream& os) {
        string error;
        if (!pipeline.process(is, os, error))
          runtime_failure("An error occurred during UDPipe execution: " << error);
      });
    }
  }

  return 0;
}
<|endoftext|>


<commit_before>#ifndef VSMC_RNG_SEED_HPP
#define VSMC_RNG_SEED_HPP

#include <vsmc/rng/common.hpp>
#include <ctime>
#include <cstdlib>

namespace vsmc { namespace rng {

class Seed
{
    public :

    typedef unsigned long result_type;

    static Seed &create ()
    {
        static Seed seed;

        return seed;
    }

    result_type get ()
    {
        if (seed_  >= std::numeric_limits<result_type>::max() - 1)
            seed_ = 0;

        return ++seed_;
    }

    void set (result_type seed)
    {
        seed_ = seed;
    }

    void skip (result_type steps)
    {
        if (seed_ >= std::numeric_limits<result_type>::max() - steps)
            seed_ = steps;
        else
            seed_ += steps;
    }

    private :

    result_type seed_;

    Seed () : seed_(VSMC_RNG_SEED)
    {
        if (!seed_)
            seed_ = std::rand();
    }

    Seed (const Seed &) {}

    const Seed &operator= (const Seed &) {return *this;}
};

} } // namespace vsmc::rng

#endif // VSMC_RNG_SEED_HPP
<commit_msg>document Seed<commit_after>#ifndef VSMC_RNG_SEED_HPP
#define VSMC_RNG_SEED_HPP

#include <vsmc/rng/common.hpp>
#include <ctime>
#include <cstdlib>

namespace vsmc { namespace rng {

/// \brief Seed generator
/// \ingroup RNG
///
/// \details
/// A Seed object cannot be created, copied, or assigned to/from by user.
/// Instead, user can only get a reference to a \c static Seed object through
/// \c Seed::create(). It is intended to generate distinct seed for the
/// Counter-based random number generator provided by Random123 library. The
/// seed generated from the Seed object shall not be used by other Pseudo
/// random number generator. The seed sequence is just a sequence of \c
/// unsigned integers. On most platforms this means it can used to create
/// \f$2^32 - 1\f$ independent random streams when used with Random123. All
/// public options are of constant complexity. For Pseudo random number
/// generators, independent seeds would need much higher computational
/// complexity to generate.
///
/// \note Note that currently all interface of Seed are \b not thread-safe.
class Seed
{
    public :

    typedef unsigned result_type;

    static Seed &create ()
    {
        static Seed seed;

        return seed;
    }

    result_type get ()
    {
        if (seed_  >= std::numeric_limits<result_type>::max() - 1)
            seed_ = 0;

        return ++seed_;
    }

    void set (result_type seed)
    {
        seed_ = seed;
    }

    void skip (result_type steps)
    {
        if (seed_ >= std::numeric_limits<result_type>::max() - steps)
            seed_ = steps;
        else
            seed_ += steps;
    }

    private :

    result_type seed_;

    Seed () : seed_(VSMC_RNG_SEED)
    {
        if (!seed_)
            seed_ = std::rand();
    }

    Seed (const Seed &) {}

    const Seed &operator= (const Seed &) {return *this;}
};

} } // namespace vsmc::rng

#endif // VSMC_RNG_SEED_HPP
<|endoftext|>