<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <vector>

#include <glog/logging.h>
#include <gtest/gtest.h>

#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/mounting/stubs.h>

#include "Entropy.h"
#include "shadowTreeGeneration.h"

namespace facebook {
namespace react {

static void testShadowNodeTreeLifeCycle(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithChildren,
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              FAIL();
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        FAIL();
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

static void testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });
      alterShadowTree(entropy, nextRootNode, &messWithNodeFlattenednessFlags);
      alterShadowTree(entropy, nextRootNode, &messWithChildren);

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              FAIL();
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        FAIL();
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

} // namespace react
} // namespace facebook

using namespace facebook::react;

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 1,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableSmallerTreeMoreIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 16,
      /* repeats */ 512,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableBiggerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 256,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 512,
      /* stages */ 32);
}
<commit_msg>Differ tests: use react_native_assert(false) instead of FAIL() to get logs upon test failure<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <vector>

#include <glog/logging.h>
#include <gtest/gtest.h>

#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/mounting/stubs.h>

#include "Entropy.h"
#include "shadowTreeGeneration.h"

namespace facebook {
namespace react {

static void testShadowNodeTreeLifeCycle(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithChildren,
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              react_native_assert(false);
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        react_native_assert(false);
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

static void testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });
      alterShadowTree(entropy, nextRootNode, &messWithNodeFlattenednessFlags);
      alterShadowTree(entropy, nextRootNode, &messWithChildren);

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              react_native_assert(false);
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        react_native_assert(false);
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

} // namespace react
} // namespace facebook

using namespace facebook::react;

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 1,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableSmallerTreeMoreIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 16,
      /* repeats */ 512,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableBiggerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 256,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 512,
      /* stages */ 32);
}
<|endoftext|>


<commit_before>// Copyright Hugh Perkins 2015 hughperkins at gmail
//
// 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 <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <stdexcept>

#include "util/FileHelper.h"
#include "util/stringhelper.h"
#include "ManifestLoaderv1.h"
#include "util/JpegHelper.h"

#include "DeepCLDllExport.h"

using namespace std;

#undef STATIC
#undef VIRTUAL
#define STATIC
#define VIRTUAL

PUBLIC STATIC bool ManifestLoaderv1::isFormatFor(std::string imagesFilepath) {
    cout << "ManifestLoaderv1 checking format for " << imagesFilepath << endl;
    string sigString = "# format=deepcl-jpeg-list-v1 ";
    char *headerBytes = FileHelper::readBinaryChunk(imagesFilepath, 0, sigString.length() + 1);
    headerBytes[sigString.length()] = 0;
    bool matched = string(headerBytes) == sigString;
    cout << "matched: " << matched << endl;
    return matched;
}
PUBLIC ManifestLoaderv1::ManifestLoaderv1(std::string imagesFilepath) {
    init(imagesFilepath);
}
PRIVATE void ManifestLoaderv1::init(std::string imagesFilepath) {
    this->imagesFilepath = imagesFilepath;
    // by reading the number of lines in the manifest, we can get the number of examples, *p_N
    // number of planes is .... 1
    // imageSize is ...

    if(!isFormatFor(imagesFilepath) ) {
        throw runtime_error("file " + imagesFilepath + " is not a deepcl-jpeg-list-v1 manifest file");
    }

    N = 0;
    labels = 0;
    hasLabels = false;
    for(int it=0; it < 2; it++) {
        int n = 0;
        bool dryrun = true;
        if(it == 0) {
        } else {
            dryrun = false;
        }
        cout << "read file it=" << it << endl;
        ifstream infile(imagesFilepath);
        char lineChars[1024];
        infile.getline(lineChars, 1024); // skip first, header, line
        string firstLine = string(lineChars);
        vector<string> splitLine = split(firstLine, " ");
        planes = readIntValue(splitLine, "planes");
        size = readIntValue(splitLine, "width");
        int imageSizeRepeated = readIntValue(splitLine, "height");
        if(size != imageSizeRepeated) {
            throw runtime_error("file " + imagesFilepath + " contains non-square images.  Not handled for now.");
        }

        if(!dryrun) {
            cout << "doing alloc N=" << N << endl;
            files = new string[N];
            if(hasLabels) {
                labels = new int[N];
            }
        }
        // now we should load into memory, since the file is not fixed-size records, and cannot be loaded partially easily
        // we are going to read the file twice:
        // - first time, we just count how many examples
        // - second time, we allocate space, and read in the examples
        while(infile) {
            infile.getline(lineChars, 1024);
            if(!infile) {
                break;
            }
            string line = string(lineChars);
            if(line == "") {
                continue;
            }
            vector<string> splitLine = split(line, " ");
            if((int)splitLine.size() == 0) {
                continue;
            }
            if(n == 0) {
                int lineSize = (int)splitLine.size();
                if(lineSize == 2) {
                    hasLabels = true;
                }
            }
            if(!hasLabels && (int)splitLine.size() != 1) {
                throw runtime_error("Error reading " + imagesFilepath + ".  Following line not parseable:\n" + line);
            }
            if(hasLabels && (int)splitLine.size() != 2) {
                throw runtime_error("Error reading " + imagesFilepath + ".  Following line not parseable:\n" + line);
            }
            if(!dryrun) {
                string jpegFile = splitLine[0];
                #ifdef _WIN32
                jpegFile = replace(jpegFile, "\\", "/");
                if(jpegFile[1] != ':' && jpegFile[0] != '/') {  // I guess this means its a relative path?
                    vector<string> splitManifestPath = split(imagesFilepath, "/");
                    string dirPath = replace(imagesFilepath, splitManifestPath[splitManifestPath.size()-1], "");
                    jpegFile = dirPath + jpegFile;
                }
                #else
                if(jpegFile[0] != '/') {  // this is a bit hacky, but at least handles linux and mac for now...
                    vector<string> splitManifestPath = split(imagesFilepath, "/");
                    string dirPath = replace(imagesFilepath, splitManifestPath[splitManifestPath.size()-1], "");
                    jpegFile = dirPath + jpegFile;
                }
                #endif
                files[n] = jpegFile;
                if(hasLabels) {
                    int label = atoi(splitLine[1]);
                    labels[n] = label;
                }
            }
    //        cout << "file " << jpegFile << " label=" << label << endl;
            n++;
        }
        infile.close();
        if(dryrun) {
            N = n;
            cout << "N is: " << N << endl;
        }
    }

    cout << "manifest " << imagesFilepath << " read. N=" << N << " planes=" << planes << " size=" << size << " labels? " << hasLabels << endl;
}
PUBLIC VIRTUAL std::string ManifestLoaderv1::getType() {
    return "ManifestLoaderv1";
}
PUBLIC VIRTUAL int ManifestLoaderv1::getImageCubeSize() {
    return planes * size * size;
}
PUBLIC VIRTUAL int ManifestLoaderv1::getN() {
    return N;
}
PUBLIC VIRTUAL int ManifestLoaderv1::getPlanes() {
    return planes;
}
PUBLIC VIRTUAL int ManifestLoaderv1::getImageSize() {
    return size;
}
int ManifestLoaderv1::readIntValue(std::vector< std::string > splitLine, std::string key) {
    for(int i = 0; i < (int)splitLine.size(); i++) {
        vector<string> splitPair = split(splitLine[i], "=");
        if((int)splitPair.size() == 2) {
            if(splitPair[0] == key) {
                return atoi(splitPair[1]);
            }
        }
    }
    throw runtime_error("Key " + key + " not found in file header");
}
PUBLIC VIRTUAL void ManifestLoaderv1::load(unsigned char *data, int *labels, int startRecord, int numRecords) {
    int imageCubeSize = planes * size * size;
//    cout << "ManifestLoaderv1, loading " << numRecords << " jpegs" << endl;
    for(int localN = 0; localN < numRecords; localN++) {
        int globalN = localN + startRecord;
        if(globalN >= N) {
            return;
        }
        JpegHelper::read(files[globalN], planes, size, size, data + localN * imageCubeSize);
        if(labels != 0) {
            if(!hasLabels) {
                throw runtime_error("ManifestLoaderv1: labels reqested in load() method, but none found in file");
            }
            labels[localN] = this->labels[globalN];
        }
    }
}

<commit_msg>ManifestLoader bug fix<commit_after>// Copyright Hugh Perkins 2015 hughperkins at gmail
//
// 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 <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <stdexcept>

#include "util/FileHelper.h"
#include "util/stringhelper.h"
#include "ManifestLoaderv1.h"
#include "util/JpegHelper.h"

#include "DeepCLDllExport.h"

using namespace std;

#undef STATIC
#undef VIRTUAL
#define STATIC
#define VIRTUAL

PUBLIC STATIC bool ManifestLoaderv1::isFormatFor(std::string imagesFilepath) {
    cout << "ManifestLoaderv1 checking format for " << imagesFilepath << endl;
    string sigString = "# format=deepcl-jpeg-list-v1 ";
    char *headerBytes = FileHelper::readBinaryChunk(imagesFilepath, 0, sigString.length() + 1);
    headerBytes[sigString.length()] = 0;
    bool matched = string(headerBytes) == sigString;
    cout << "matched: " << matched << endl;
    return matched;
}
PUBLIC ManifestLoaderv1::ManifestLoaderv1(std::string imagesFilepath) {
    init(imagesFilepath);
}
PRIVATE void ManifestLoaderv1::init(std::string imagesFilepath) {
    this->imagesFilepath = imagesFilepath;
    // by reading the number of lines in the manifest, we can get the number of examples, *p_N
    // number of planes is .... 1
    // imageSize is ...

    if(!isFormatFor(imagesFilepath) ) {
        throw runtime_error("file " + imagesFilepath + " is not a deepcl-jpeg-list-v1 manifest file");
    }

    N = 0;
    labels = 0;
    hasLabels = false;
    for(int it=0; it < 2; it++) {
        int n = 0;
        bool dryrun = it == 0 ? true : false;

        cout << "read file int=" << it << endl;
        ifstream infile(imagesFilepath);
        char lineChars[1024];
        infile.getline(lineChars, 1024); // skip first, header, line
        string firstLine = string(lineChars);
        vector<string> splitLine = split(firstLine, " ");
        planes = readIntValue(splitLine, "planes");
        size = readIntValue(splitLine, "width");
        int imageSizeRepeated = readIntValue(splitLine, "height");
        if(size != imageSizeRepeated) {
            throw runtime_error("file " + imagesFilepath + " contains non-square images.  Not handled for now.");
        }

        if(!dryrun) {
            cout << "doing alloc N=" << N << endl;
            files = new string[N];
            if(hasLabels) {
                labels = new int[N];
            }
        }
        // now we should load into memory, since the file is not fixed-size records, and cannot be loaded partially easily
        // we are going to read the file twice:
        // - first time, we just count how many examples
        // - second time, we allocate space, and read in the examples
        while(infile) {
            infile.getline(lineChars, 1024);
            if(!infile)
                break;

            string line = string(lineChars);
			if(line == "")
				continue;

            vector<string> splitLine = split(line, " ");
			int splitSize = (int)splitLine.size();

			if (dryrun) {
				if (splitSize == 0)
					continue; // There are no spaces so no reason to check if final item is a valid number

				char* p;
				strtol(splitLine[splitSize - 1].c_str(), &p, 10); // If convertion from string to number is successful p will point to null/0

				if (n == 0)
					hasLabels = *p == 0 ? true : false; // If p points to null/0 we have labels

				if (hasLabels && *p != 0) // We were expecting labels but found none
					throw runtime_error("Error reading " + imagesFilepath + ".  Following line not parseable:\n" + line);
			}
			else {
				string jpegFile = line;

				if (hasLabels) {
					string tempValue = splitLine[splitSize - 1];
					labels[n] = atoi(tempValue);
					jpegFile = line.substr(0, line.size() - tempValue.size());
				}

                #ifdef _WIN32
                jpegFile = replace(jpegFile, "\\", "/");

                if(jpegFile[1] != ':' && jpegFile[0] != '/') {  // I guess this means its a relative path?
                    vector<string> splitManifestPath = split(imagesFilepath, "/");
                    string dirPath = replace(imagesFilepath, splitManifestPath[splitManifestPath.size()-1], "");
                    jpegFile = dirPath + jpegFile;
                }
                #else
                if(jpegFile[0] != '/') {  // this is a bit hacky, but at least handles linux and mac for now...
                    vector<string> splitManifestPath = split(imagesFilepath, "/");
                    string dirPath = replace(imagesFilepath, splitManifestPath[splitManifestPath.size()-1], "");
                    jpegFile = dirPath + jpegFile;
                }
                #endif
                files[n] = jpegFile;
            }

            n++;
        }
        infile.close();
        if(dryrun) {
            N = n;
            cout << "N is: " << N << endl;
        }
    }

    cout << "manifest " << imagesFilepath << " read. N=" << N << " planes=" << planes << " size=" << size << " labels? " << hasLabels << endl;
}
PUBLIC VIRTUAL std::string ManifestLoaderv1::getType() {
    return "ManifestLoaderv1";
}
PUBLIC VIRTUAL int ManifestLoaderv1::getImageCubeSize() {
    return planes * size * size;
}
PUBLIC VIRTUAL int ManifestLoaderv1::getN() {
    return N;
}
PUBLIC VIRTUAL int ManifestLoaderv1::getPlanes() {
    return planes;
}
PUBLIC VIRTUAL int ManifestLoaderv1::getImageSize() {
    return size;
}
int ManifestLoaderv1::readIntValue(std::vector< std::string > splitLine, std::string key) {
    for(int i = 0; i < (int)splitLine.size(); i++) {
        vector<string> splitPair = split(splitLine[i], "=");
        if((int)splitPair.size() == 2) {
            if(splitPair[0] == key) {
                return atoi(splitPair[1]);
            }
        }
    }
    throw runtime_error("Key " + key + " not found in file header");
}
PUBLIC VIRTUAL void ManifestLoaderv1::load(unsigned char *data, int *labels, int startRecord, int numRecords) {
    int imageCubeSize = planes * size * size;
//    cout << "ManifestLoaderv1, loading " << numRecords << " jpegs" << endl;
    for(int localN = 0; localN < numRecords; localN++) {
        int globalN = localN + startRecord;
        if(globalN >= N) {
            return;
        }
        JpegHelper::read(files[globalN], planes, size, size, data + localN * imageCubeSize);
        if(labels != 0) {
            if(!hasLabels) {
                throw runtime_error("ManifestLoaderv1: labels reqested in load() method, but none found in file");
            }
            labels[localN] = this->labels[globalN];
        }
    }
}<|endoftext|>


<commit_before>#include "../include/level_factory.hpp"
#include "../engine/include/persistence_dat.hpp"
#include "../engine/include/observable.hpp"
#include <unordered_map>
#include <vector>

using namespace mindscape;

typedef mindscape::GameObjectFactory::Options Opts;

/**
 * @brief Updates Game level.
 *
 * Initiates changing of level, such as changes of scenes.
 *
 * @param level Level to be changed.
 * @param path Path of new level.
 * @return void.
 */
void LevelFactory::update_level(engine::Level *level, std::string path) {
    std::vector<engine::GameObject *> new_objects = execute_dat(level, path);

    for(auto game_object : new_objects) {
        level->activate_game_object(game_object);
        game_object->load();
    }
}

/**
 * @brief Executes Level's data.
 *
 * Sets all dimension, coordinates, audios and game's objects in the level.
 *
 * @param level Level to be changed.
 * @param path Path of the level.
 * @return GameObject Vector of new game objects.
 */
std::vector<engine::GameObject *> LevelFactory::execute_dat(
    engine::Level *level, std::string path) {

    std::vector<std::string> includes;
    std::vector<engine::GameObject *> added_game_objects;

    GameObjectFactory mindscape_factory = GameObjectFactory();

    engine::PersistenceDat *persistence =
        engine::PersistenceDat::get_instance();
    engine::PersistenceMap *objects = persistence->load(path);

    if(!!objects) {

        for(auto it = objects->begin(); it < objects->end(); it++) {
            std::unordered_map<std::string, std::string> object = *it;
            Opts type = static_cast<Opts>(std::stoi(object["type"]));

            if(type == Opts::HITBOX) {

                std::pair<int, int> dimensions;
                std::pair<int, int> displacement;

                dimensions.first = std::stoi(object["size_x"]);
                dimensions.second = std::stoi(object["size_y"]);

                displacement.first = std::stoi(object["displ_x"]);
                displacement.second = std::stoi(object["displ_y"]);

                mindscape_factory.fabricate_hitbox(level->get_object_by_name(
                object["belongs_to"]), displacement, dimensions);

            }

            else if(type == Opts::IMAGE) {

                int priority = std::stoi(object["priority"]);

                std::pair<int, int> displacement;
                std::pair<int, int> dimensions_on_screen;
                std::pair<int, int> dimensions_on_texture;
                std::pair<int, int> coordinatesOnTexture;

                displacement.first = std::stoi(object["displ_x"]);
                displacement.second = std::stoi(object["displ_y"]);

                dimensions_on_screen.first = std::stoi(object["screen_x"]);
                dimensions_on_screen.second = std::stoi(object["screen_y"]);
                dimensions_on_texture.first = std::stoi(object["tex_x"]);
                dimensions_on_texture.second = std::stoi(object["tex_y"]);

                coordinatesOnTexture.first = std::stoi(object["tex_coord_x"]);
                coordinatesOnTexture.second = std::stoi(object["tex_coord_y"]);

                mindscape_factory.fabricate_image(
                    level->get_object_by_name(object["belongs_to"]),
                    object["path"], displacement, priority,
                    dimensions_on_screen, dimensions_on_texture,
                    coordinatesOnTexture
                );

            }

            else if(type == Opts::TEXT) {

                int priority = std::stoi(object["priority"]);
                std::string text = object["text"];
                std::string font_path = object["font_path"];

                int font_size = std::stoi(object["font_size"]);
                std::pair<int, int> displacement;

                displacement.first = std::stoi(object["displ_x"]);
                displacement.second = std::stoi(object["displ_y"]);

                mindscape_factory.fabricate_text(
                    level->get_object_by_name(object["belongs_to"]),
                    text, font_path, font_size, displacement, priority
                );

            }

            else if(type == Opts::AUDIO) {

                std::string name = object["id"];
                std::string path = object["path"];

                int audio_type = std::stoi(object["audio_type"]);

                mindscape_factory.fabricate_audio(
                    level->get_object_by_name(object["belongs_to"]),
                    name, path, audio_type
                );

            }

            else if(type == Opts::ACTION) {

                mindscape_factory.fabricate_action(
                    level->get_object_by_name(object["belongs_to"]),
                    std::stoi(object["command"]), object["param"]
                );

            }

            else if(type == Opts::TRANSLATION) {

                std::string event_name = object["event_name"];
                int key = std::stoi(object["key"]);
                mindscape_factory.fabricate_translation(
                    level->get_object_by_name(object["belongs_to"]),
                    key, event_name
                );

            }

            else {
                engine::GameObject * constructed_obj =
                    mindscape_factory.fabricate(
                        type, object["id"],
                        std::make_pair(std::stoi(object["x"]),
                        std::stoi(object["y"])), std::stoi(object["priority"])
                    );

                if(object.count("follows") > 0) { //if follow key is declared

                    engine::Observable * observable =
                        level->get_object_by_name(object["follows"]);

                    observable->attach_observer(constructed_obj);
                }

                level->add_object(constructed_obj);
                added_game_objects.push_back(constructed_obj);
            }

        }

    }

    return added_game_objects;
}

/**
 * @brief Fabricates Game Level.
 *
 * Realizes all the process to build the game level.
 *
 * @fn execute_dat Decribed above.
 * @param path Path of the level datas.
 * @return GameObject Vector of new game objects.
 */
engine::Level *LevelFactory::fabricate_level(std::string path) {
    engine::Level *level = new engine::Level();
    execute_dat(level, path);
    return level;
}

/**
 * @brief Fabricates Menu.
 *
 * Returns a level null for fabricates the menu.
 *
 * @return Level null.
 */
engine::Level* LevelFactory::fabricate_menu(){
    return NULL;
}

/**
 * @brief Fabricates Game Over.
 *
 * Returns a level null for fabricates game over.
 *
 * @return Level null.
 */
engine::Level* LevelFactory::fabricate_game_over(){
    return NULL;
}
<commit_msg>[LOG] Applies Logging in level_factory.cpp<commit_after>#include "../include/level_factory.hpp"
#include "../engine/include/persistence_dat.hpp"
#include "../engine/include/observable.hpp"
#include "../engine/include/log.hpp"
#include <unordered_map>
#include <vector>

using namespace mindscape;

typedef mindscape::GameObjectFactory::Options Opts;

/**
 * @brief Updates Game level.
 *
 * Initiates changing of level, such as changes of scenes.
 *
 * @param level Level to be changed.
 * @param path Path of new level.
 * @return void.
 */
void LevelFactory::update_level(engine::Level *level, std::string path) {
    DEBUG("Started: update_level()");

    std::vector<engine::GameObject *> new_objects = execute_dat(level, path);

    for (auto game_object : new_objects) {
        level->activate_game_object(game_object);
        game_object->load();
    }

    DEBUG("Ended: update_level()");
}

/**
 * @brief Executes Level's data.
 *
 * Sets all dimension, coordinates, audios and game's objects in the level.
 *
 * @param level Level to be changed.
 * @param path Path of the level.
 * @return GameObject Vector of new game objects.
 */
std::vector<engine::GameObject *> LevelFactory::execute_dat(
    engine::Level *level, std::string path) {
    DEBUG("Started: GameObject execute_dat()");

    std::vector<std::string> includes;
    std::vector<engine::GameObject *> added_game_objects;

    GameObjectFactory mindscape_factory = GameObjectFactory();

    engine::PersistenceDat *persistence =
        engine::PersistenceDat::get_instance();
    engine::PersistenceMap *objects = persistence->load(path);

    if (!!objects) {
        for (auto it = objects->begin(); it < objects->end(); it++) {
            std::unordered_map<std::string, std::string> object = *it;

            Opts type = static_cast<Opts>(std::stoi(object["type"]));

            if (type == Opts::HITBOX) {
                std::pair<int, int> dimensions;
                std::pair<int, int> displacement;

                dimensions.first = std::stoi(object["size_x"]);
                dimensions.second = std::stoi(object["size_y"]);

                displacement.first = std::stoi(object["displ_x"]);
                displacement.second = std::stoi(object["displ_y"]);

                mindscape_factory.fabricate_hitbox(level->get_object_by_name(
                object["belongs_to"]), displacement, dimensions);
            }

            else if (type == Opts::IMAGE) {
                int priority = 0;
                priority = std::stoi(object["priority"]);

                std::pair<int, int> displacement;
                std::pair<int, int> dimensions_on_screen;
                std::pair<int, int> dimensions_on_texture;
                std::pair<int, int> coordinatesOnTexture;

                displacement.first = std::stoi(object["displ_x"]);
                displacement.second = std::stoi(object["displ_y"]);

                dimensions_on_screen.first = std::stoi(object["screen_x"]);
                dimensions_on_screen.second = std::stoi(object["screen_y"]);
                dimensions_on_texture.first = std::stoi(object["tex_x"]);
                dimensions_on_texture.second = std::stoi(object["tex_y"]);

                coordinatesOnTexture.first = std::stoi(object["tex_coord_x"]);
                coordinatesOnTexture.second = std::stoi(object["tex_coord_y"]);

                mindscape_factory.fabricate_image(
                    level->get_object_by_name(object["belongs_to"]),
                    object["path"], displacement, priority,
                    dimensions_on_screen, dimensions_on_texture,
                    coordinatesOnTexture
                );
            }

            else if (type == Opts::TEXT) {
                int priority = 0;
                priority = std::stoi(object["priority"]);
                std::string text = object["text"];
                std::string font_path = object["font_path"];

                int font_size = 0;
                font_size = std::stoi(object["font_size"]);

                std::pair<int, int> displacement;

                displacement.first = std::stoi(object["displ_x"]);
                displacement.second = std::stoi(object["displ_y"]);

                mindscape_factory.fabricate_text(
                    level->get_object_by_name(object["belongs_to"]),
                    text, font_path, font_size, displacement, priority
                );
            }

            else if (type == Opts::AUDIO) {
                int audio_type = 0;
                std::string name = "";
                std::string path = "";

                audio_type = std::stoi(object["audio_type"]);
                name = object["id"];
                path = object["path"];

                mindscape_factory.fabricate_audio(
                    level->get_object_by_name(object["belongs_to"]),
                    name, path, audio_type
                );
            }

            else if (type == Opts::ACTION) {
                mindscape_factory.fabricate_action(
                    level->get_object_by_name(object["belongs_to"]),
                    std::stoi(object["command"]), object["param"]
                );
            }

            else if (type == Opts::TRANSLATION) {
                int key = 0;
                std::string event_name = "";

                event_name = object["event_name"];
                key = std::stoi(object["key"]);

                mindscape_factory.fabricate_translation(
                    level->get_object_by_name(object["belongs_to"]),
                    key, event_name
                );
            }

            else {
                engine::GameObject * constructed_obj =
                    mindscape_factory.fabricate(
                        type, object["id"],
                        std::make_pair(std::stoi(object["x"]),
                        std::stoi(object["y"])), std::stoi(object["priority"])
                    );

                if (object.count("follows") > 0) { //if follow key is declared

                    engine::Observable * observable =
                        level->get_object_by_name(object["follows"]);

                    observable->attach_observer(constructed_obj);
                }
                level->add_object(constructed_obj);
                added_game_objects.push_back(constructed_obj);
            }
        }
    }

    return added_game_objects;

    DEBUG("Ended: GameObject execute_dat()");
}

/**
 * @brief Fabricates Game Level.
 *
 * Realizes all the process to build the game level.
 *
 * @fn execute_dat Decribed above.
 * @param path Path of the level datas.
 * @return GameObject Vector of new game objects.
 */
engine::Level *LevelFactory::fabricate_level(std::string path) {
    INFO("Building Game Level");

    engine::Level *level = new engine::Level();
    execute_dat(level, path);

    return level;

    DEBUG("Ended: Building Game level");
}

/**
 * @brief Fabricates Menu.
 *
 * Returns a level null for fabricates the menu.
 *
 * @return Level null.
 */
engine::Level* LevelFactory::fabricate_menu(){
    INFO("Fabricates the menu");

    return NULL;
}

/**
 * @brief Fabricates Game Over.
 *
 * Returns a level null for fabricates game over.
 *
 * @return Level null.
 */
engine::Level* LevelFactory::fabricate_game_over(){
    INFO("Fabricates Game Over");

    return NULL;
}
<|endoftext|>


<commit_before>#include <string>
#include <iostream>

#include "THClDebug.h"
#include "THClTensorCopy.h"

using namespace std;

// stuff used during debugging only, not used for prod
// it doesnt clean up after itself or anything, will leak horribly (for now), but
// at least produces useful diagnostic info

void THClDebug_printTensor(THClState *state, THClTensor *target) {
//    cout << "indices" << endl;
//    cout << THClTensor_toString(state, target) << endl;
    THFloatTensor *probe = THFloatTensor_new();
  THLongStorage *idxsize = THClTensor_newSizeOf(state, target);
THLongStorage *idxstride = THClTensor_newStrideOf(state, target);
    THFloatTensor_resize(probe, idxsize, idxstride);
    THFloatTensor_copyCl(state, probe, target);

    int indices_dim = THFloatTensor_nDimension(probe);
//    cout << "indices_dim " << indices_dim << endl;
    if(indices_dim == 1) {
       cout << " ";
       long size0 = THFloatTensor_size(probe, 0);
       for(int i = 0; i < size0; i++) {
         cout << probe->storage->data[i] << "  ";
      }
//       cout << endl;
      cout << "[torch.ClTensor of size " << size0 << "]" << endl;
    } else if( indices_dim == 2) {
       long size0 = THFloatTensor_size(probe, 0);
       long size1 = THFloatTensor_size(probe, 1);
       for(int i = 0; i < size0; i++) {
         cout << " ";
         for(int j = 0; j < size1; j++) {
            cout << probe->storage->data[i * THFloatTensor_stride(probe, 0) + j * THFloatTensor_stride(probe, 1)] << "  ";
         }
         cout << endl;
      }
      cout << "[torch.ClTensor of size " << size0 << "x" << size1 << "]" << endl;
    } else if(indices_dim == 3) {
       long size0 = THFloatTensor_size(probe, 0);
       long size1 = THFloatTensor_size(probe, 1);
       long size2 = THFloatTensor_size(probe, 2);
       for(int d0=0; d0<size0; d0++) {
         cout << "(" << d0 << ",.,.) =" << endl;
         for(int i = 0; i < size1; i++) {
           cout << " ";
           for(int j = 0; j < size2; j++) {
              cout << probe->storage->data[i * THFloatTensor_stride(probe, 0) + j * THFloatTensor_stride(probe, 1)] << "  ";
           }
           cout << endl;
          }
       }
      cout << "[torch.ClTensor of size " << size0 << "x" << size1 << "x" << size2 << "]" << endl;
    } else {
       cout << "target dim > 3" << endl;
    }
}

void THClDebug_printSize(THClState *state, THClTensor *target) {
    int dim = THClTensor_nDimension(state, target);
    cout << "dim=" << dim << endl;

//    THFloatTensor *probe = THFloatTensor_new();
//  THLongStorage *idxsize = THClTensor_newSizeOf(state, target);
//THLongStorage *idxstride = THClTensor_newStrideOf(state, target);
//    THFloatTensor_resize(probe, idxsize, idxstride);
//    THFloatTensor_copyCl(state, probe, target);

    cout << "[torch.ClTensor of size ";
    for(int d = 0; d < dim; d++) {
      if( d > 0) {
         cout << "x";
      }
      cout << THClTensor_size(state, target, d);
    }
    cout << "]" << endl;
}

<commit_msg>more bugs in THClDebug for printTensor<commit_after>#include <string>
#include <iostream>

#include "THClDebug.h"
#include "THClTensorCopy.h"

using namespace std;

// stuff used during debugging only, not used for prod
// it doesnt clean up after itself or anything, will leak horribly (for now), but
// at least produces useful diagnostic info

void THClDebug_printTensor(THClState *state, THClTensor *target) {
//    cout << "indices" << endl;
//    cout << THClTensor_toString(state, target) << endl;
    THFloatTensor *probe = THFloatTensor_new();
  THLongStorage *idxsize = THClTensor_newSizeOf(state, target);
THLongStorage *idxstride = THClTensor_newStrideOf(state, target);
    THFloatTensor_resize(probe, idxsize, idxstride);
    THFloatTensor_copyCl(state, probe, target);

    int indices_dim = THFloatTensor_nDimension(probe);
//    cout << "indices_dim " << indices_dim << endl;
    if(indices_dim == 1) {
       cout << " ";
       long size0 = THFloatTensor_size(probe, 0);
       for(int i = 0; i < size0; i++) {
         cout << probe->storage->data[i] << "  ";
      }
//       cout << endl;
      cout << "[torch.ClTensor of size " << size0 << "]" << endl;
    } else if( indices_dim == 2) {
       long size0 = THFloatTensor_size(probe, 0);
       long size1 = THFloatTensor_size(probe, 1);
       for(int i = 0; i < size0; i++) {
         cout << " ";
         for(int j = 0; j < size1; j++) {
            cout << probe->storage->data[i * size1 + j] << "  ";
         }
         cout << endl;
      }
      cout << "[torch.ClTensor of size " << size0 << "x" << size1 << "]" << endl;
    } else if(indices_dim == 3) {
       long size0 = THFloatTensor_size(probe, 0);
       long size1 = THFloatTensor_size(probe, 1);
       long size2 = THFloatTensor_size(probe, 2);
       for(int d0=0; d0<size0; d0++) {
         cout << "(" << d0 << ",.,.) =" << endl;
         for(int i = 0; i < size1; i++) {
           cout << " ";
           for(int j = 0; j < size2; j++) {
              cout << probe->storage->data[d0 * size1 * size2 + i * size2 + j] << "  ";
           }
           cout << endl;
          }
       }
      cout << "[torch.ClTensor of size " << size0 << "x" << size1 << "x" << size2 << "]" << endl;
    } else {
       cout << "target dim > 3" << endl;
    }
}

void THClDebug_printSize(THClState *state, THClTensor *target) {
    int dim = THClTensor_nDimension(state, target);
    cout << "dim=" << dim << endl;

//    THFloatTensor *probe = THFloatTensor_new();
//  THLongStorage *idxsize = THClTensor_newSizeOf(state, target);
//THLongStorage *idxstride = THClTensor_newStrideOf(state, target);
//    THFloatTensor_resize(probe, idxsize, idxstride);
//    THFloatTensor_copyCl(state, probe, target);

    cout << "[torch.ClTensor of size ";
    for(int d = 0; d < dim; d++) {
      if( d > 0) {
         cout << "x";
      }
      cout << THClTensor_size(state, target, d);
    }
    cout << "]" << endl;
}

<|endoftext|>


<commit_before>#include <iostream>
#include "configurator.hpp"

using std::cout;
using namespace mesos::internal;

int main(int argc, char **argv) {
  if (argc == 1) {
    cout << "Usage:  mesos-getconf OPTION [arg1 arg2 ... ]\n\n";
    cout << "OPTION\t\tis the name option whose value is to be extracted\n";
    cout << "arg1 ...\tOptional arguments passed to mesos, e.g. --quiet\n\n";
    cout << "This utility gives shell scripts access Mesos parameters.\n\n";
    return 1;
  }
  Configurator conf;
  conf.load(argc, argv, true);
  string param(argv[1]);
  transform(param.begin(), param.end(), param.begin(), tolower);
  if (conf.getParams().contains(param)) {
    cout << conf.getParams()[param] << "\n";
  }
  return 0;
}
<commit_msg>Updated a comment and corrected some formating.<commit_after>#include <iostream>
#include "configurator.hpp"

using std::cout;
using namespace mesos::internal;

int main(int argc, char **argv)
{
  if (argc == 1) {
    cout << "Usage:  mesos-getconf OPTION [arg1 arg2 ... ]\n\n";
    cout << "OPTION\t\tis the name option whose value is to be extracted\n";
    cout << "arg1 ...\tOptional arguments passed to mesos, e.g. --quiet\n\n";
    cout << "This utility gives shell scripts access to Mesos parameters.\n\n";
    return 1;
  }
  Configurator conf;
  conf.load(argc, argv, true);
  string param(argv[1]);
  transform(param.begin(), param.end(), param.begin(), tolower);
  if (conf.getParams().contains(param)) {
    cout << conf.getParams()[param] << "\n";
  }
  return 0;
}
<|endoftext|>


<commit_before>/*
	Copyright 2022 Jordi SUBIRANA

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
	Software, and to permit persons to whom the Software is furnished to do so, subject
	to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
	PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
	CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
	OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <iostream>
#include <Atema/Shader/Spirv/SpirvShaderWriter.hpp>

#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
#include <glslang/Include/glslang_c_interface.h>

using namespace at;

namespace
{
	EShLanguage getEShLanguage(AstShaderStage stage)
	{
		switch (stage)
		{
			case AstShaderStage::Vertex: return EShLangVertex;
			case AstShaderStage::Fragment: return EShLangFragment;
			default:
			{
				ATEMA_ERROR("Invalid AstShaderStage");
			}
		}

		return EShLangVertex;
	}

	TBuiltInResource getDefaultResources()
	{
		TBuiltInResource resources;

		resources.maxLights = 32;
		resources.maxClipPlanes = 6;
		resources.maxTextureUnits = 32;
		resources.maxTextureCoords = 32;
		resources.maxVertexAttribs = 64;
		resources.maxVertexUniformComponents = 4096;
		resources.maxVaryingFloats = 64;
		resources.maxVertexTextureImageUnits = 32;
		resources.maxCombinedTextureImageUnits = 80;
		resources.maxTextureImageUnits = 32;
		resources.maxFragmentUniformComponents = 4096;
		resources.maxDrawBuffers = 32;
		resources.maxVertexUniformVectors = 128;
		resources.maxVaryingVectors = 8;
		resources.maxFragmentUniformVectors = 16;
		resources.maxVertexOutputVectors = 16;
		resources.maxFragmentInputVectors = 15;
		resources.minProgramTexelOffset = -8;
		resources.maxProgramTexelOffset = 7;
		resources.maxClipDistances = 8;
		resources.maxComputeWorkGroupCountX = 65535;
		resources.maxComputeWorkGroupCountY = 65535;
		resources.maxComputeWorkGroupCountZ = 65535;
		resources.maxComputeWorkGroupSizeX = 1024;
		resources.maxComputeWorkGroupSizeY = 1024;
		resources.maxComputeWorkGroupSizeZ = 64;
		resources.maxComputeUniformComponents = 1024;
		resources.maxComputeTextureImageUnits = 16;
		resources.maxComputeImageUniforms = 8;
		resources.maxComputeAtomicCounters = 8;
		resources.maxComputeAtomicCounterBuffers = 1;
		resources.maxVaryingComponents = 60;
		resources.maxVertexOutputComponents = 64;
		resources.maxGeometryInputComponents = 64;
		resources.maxGeometryOutputComponents = 128;
		resources.maxFragmentInputComponents = 128;
		resources.maxImageUnits = 8;
		resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
		resources.maxCombinedShaderOutputResources = 8;
		resources.maxImageSamples = 0;
		resources.maxVertexImageUniforms = 0;
		resources.maxTessControlImageUniforms = 0;
		resources.maxTessEvaluationImageUniforms = 0;
		resources.maxGeometryImageUniforms = 0;
		resources.maxFragmentImageUniforms = 8;
		resources.maxCombinedImageUniforms = 8;
		resources.maxGeometryTextureImageUnits = 16;
		resources.maxGeometryOutputVertices = 256;
		resources.maxGeometryTotalOutputComponents = 1024;
		resources.maxGeometryUniformComponents = 1024;
		resources.maxGeometryVaryingComponents = 64;
		resources.maxTessControlInputComponents = 128;
		resources.maxTessControlOutputComponents = 128;
		resources.maxTessControlTextureImageUnits = 16;
		resources.maxTessControlUniformComponents = 1024;
		resources.maxTessControlTotalOutputComponents = 4096;
		resources.maxTessEvaluationInputComponents = 128;
		resources.maxTessEvaluationOutputComponents = 128;
		resources.maxTessEvaluationTextureImageUnits = 16;
		resources.maxTessEvaluationUniformComponents = 1024;
		resources.maxTessPatchComponents = 120;
		resources.maxPatchVertices = 32;
		resources.maxTessGenLevel = 64;
		resources.maxViewports = 16;
		resources.maxVertexAtomicCounters = 0;
		resources.maxTessControlAtomicCounters = 0;
		resources.maxTessEvaluationAtomicCounters = 0;
		resources.maxGeometryAtomicCounters = 0;
		resources.maxFragmentAtomicCounters = 8;
		resources.maxCombinedAtomicCounters = 8;
		resources.maxAtomicCounterBindings = 1;
		resources.maxVertexAtomicCounterBuffers = 0;
		resources.maxTessControlAtomicCounterBuffers = 0;
		resources.maxTessEvaluationAtomicCounterBuffers = 0;
		resources.maxGeometryAtomicCounterBuffers = 0;
		resources.maxFragmentAtomicCounterBuffers = 1;
		resources.maxCombinedAtomicCounterBuffers = 1;
		resources.maxAtomicCounterBufferSize = 16384;
		resources.maxTransformFeedbackBuffers = 4;
		resources.maxTransformFeedbackInterleavedComponents = 64;
		resources.maxCullDistances = 8;
		resources.maxCombinedClipAndCullDistances = 8;
		resources.maxSamples = 4;
		resources.maxMeshOutputVerticesNV = 256;
		resources.maxMeshOutputPrimitivesNV = 512;
		resources.maxMeshWorkGroupSizeX_NV = 32;
		resources.maxMeshWorkGroupSizeY_NV = 1;
		resources.maxMeshWorkGroupSizeZ_NV = 1;
		resources.maxTaskWorkGroupSizeX_NV = 32;
		resources.maxTaskWorkGroupSizeY_NV = 1;
		resources.maxTaskWorkGroupSizeZ_NV = 1;
		resources.maxMeshViewCountNV = 4;

		resources.limits.nonInductiveForLoops = true;
		resources.limits.whileLoops = true;
		resources.limits.doWhileLoops = true;
		resources.limits.generalUniformIndexing = true;
		resources.limits.generalAttributeMatrixVectorIndexing = true;
		resources.limits.generalVaryingIndexing = true;
		resources.limits.generalSamplerIndexing = true;
		resources.limits.generalVariableIndexing = true;
		resources.limits.generalConstantMatrixVectorIndexing = true;

		return resources;
	}

	const TBuiltInResource defaultBuiltInResource = getDefaultResources();
}

SpirvShaderWriter::SpirvShaderWriter(AstShaderStage stage) :
	m_stage(stage),
	m_glslWriter(stage, m_glslStream)
{
}

SpirvShaderWriter::~SpirvShaderWriter()
{
}

void SpirvShaderWriter::compile(std::vector<uint32_t>& spirv)
{
	glslang::InitializeProcess();

	compileSpirv(spirv);

	glslang::FinalizeProcess();
}

void SpirvShaderWriter::compile(std::ostream& ostream)
{
	const auto eShLanguage = getEShLanguage(m_stage);

	std::vector<uint32_t> spirv;

	glslang::InitializeProcess();

	compileSpirv(spirv);

	//glslang::OutputSpvHex(spirv, eShLanguage, variableName);
	//glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage));

	const auto codeData = reinterpret_cast<char*>(spirv.data());
	const auto codeSize = spirv.size() * sizeof(uint32_t);

	for (size_t i = 0; i < codeSize; i++)
		ostream << codeData[i];

	glslang::FinalizeProcess();
}

void SpirvShaderWriter::visit(ConditionalStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ForLoopStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(WhileLoopStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(DoWhileLoopStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(VariableDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(StructDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(InputDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(OutputDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ExternalDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(OptionDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(FunctionDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(EntryFunctionDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ExpressionStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(BreakStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ContinueStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ReturnStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(SequenceStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ConstantExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(VariableExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(AccessIndexExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(AccessIdentifierExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(AssignmentExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(UnaryExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(BinaryExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(FunctionCallExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(BuiltInFunctionCallExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(CastExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(SwizzleExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(TernaryExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::compileSpirv(std::vector<uint32_t>& spirv)
{
	const auto eShLanguage = getEShLanguage(m_stage);

	// Create shader
	glslang::TShader shader(eShLanguage);

	const auto str = m_glslStream.str();
	const auto cstr = str.c_str();
	const int length = static_cast<int>(str.length());

	shader.setStringsWithLengths(&cstr, &length, 1);

	shader.setEnvInput(glslang::EShSourceGlsl, eShLanguage, glslang::EShClientVulkan, 100);
	shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
	shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_1);

	shader.parse(&defaultBuiltInResource, 100, ECoreProfile, false, false, EShMsgDefault);

	// Get info log
	const auto shaderInfoLog = shader.getInfoLog();

	std::cout << shaderInfoLog << std::endl;

	// Create program
	glslang::TProgram program;

	program.addShader(&shader);

	const auto linkResult = program.link(EShMsgDefault);

	const auto programInfoLog = program.getInfoLog();

	std::cout << programInfoLog << std::endl;

	auto intermediate = program.getIntermediate(eShLanguage);

	spv::SpvBuildLogger logger;

	glslang::SpvOptions spvOptions;

	spvOptions.generateDebugInfo = true;
	spvOptions.stripDebugInfo = true;
	spvOptions.disableOptimizer = false;
	spvOptions.optimizeSize = true;
	spvOptions.disassemble = false;
	spvOptions.validate = false;
	glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions);
}
<commit_msg>Shader - SpirvShaderWriter - Error handling when compilation or linking fail<commit_after>/*
	Copyright 2022 Jordi SUBIRANA

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
	Software, and to permit persons to whom the Software is furnished to do so, subject
	to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
	PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
	CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
	OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <Atema/Shader/Spirv/SpirvShaderWriter.hpp>

#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>

using namespace at;

namespace
{
	EShLanguage getEShLanguage(AstShaderStage stage)
	{
		switch (stage)
		{
			case AstShaderStage::Vertex: return EShLangVertex;
			case AstShaderStage::Fragment: return EShLangFragment;
			default:
			{
				ATEMA_ERROR("Invalid AstShaderStage");
			}
		}

		return EShLangVertex;
	}

	TBuiltInResource getDefaultResources()
	{
		TBuiltInResource resources;

		resources.maxLights = 32;
		resources.maxClipPlanes = 6;
		resources.maxTextureUnits = 32;
		resources.maxTextureCoords = 32;
		resources.maxVertexAttribs = 64;
		resources.maxVertexUniformComponents = 4096;
		resources.maxVaryingFloats = 64;
		resources.maxVertexTextureImageUnits = 32;
		resources.maxCombinedTextureImageUnits = 80;
		resources.maxTextureImageUnits = 32;
		resources.maxFragmentUniformComponents = 4096;
		resources.maxDrawBuffers = 32;
		resources.maxVertexUniformVectors = 128;
		resources.maxVaryingVectors = 8;
		resources.maxFragmentUniformVectors = 16;
		resources.maxVertexOutputVectors = 16;
		resources.maxFragmentInputVectors = 15;
		resources.minProgramTexelOffset = -8;
		resources.maxProgramTexelOffset = 7;
		resources.maxClipDistances = 8;
		resources.maxComputeWorkGroupCountX = 65535;
		resources.maxComputeWorkGroupCountY = 65535;
		resources.maxComputeWorkGroupCountZ = 65535;
		resources.maxComputeWorkGroupSizeX = 1024;
		resources.maxComputeWorkGroupSizeY = 1024;
		resources.maxComputeWorkGroupSizeZ = 64;
		resources.maxComputeUniformComponents = 1024;
		resources.maxComputeTextureImageUnits = 16;
		resources.maxComputeImageUniforms = 8;
		resources.maxComputeAtomicCounters = 8;
		resources.maxComputeAtomicCounterBuffers = 1;
		resources.maxVaryingComponents = 60;
		resources.maxVertexOutputComponents = 64;
		resources.maxGeometryInputComponents = 64;
		resources.maxGeometryOutputComponents = 128;
		resources.maxFragmentInputComponents = 128;
		resources.maxImageUnits = 8;
		resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
		resources.maxCombinedShaderOutputResources = 8;
		resources.maxImageSamples = 0;
		resources.maxVertexImageUniforms = 0;
		resources.maxTessControlImageUniforms = 0;
		resources.maxTessEvaluationImageUniforms = 0;
		resources.maxGeometryImageUniforms = 0;
		resources.maxFragmentImageUniforms = 8;
		resources.maxCombinedImageUniforms = 8;
		resources.maxGeometryTextureImageUnits = 16;
		resources.maxGeometryOutputVertices = 256;
		resources.maxGeometryTotalOutputComponents = 1024;
		resources.maxGeometryUniformComponents = 1024;
		resources.maxGeometryVaryingComponents = 64;
		resources.maxTessControlInputComponents = 128;
		resources.maxTessControlOutputComponents = 128;
		resources.maxTessControlTextureImageUnits = 16;
		resources.maxTessControlUniformComponents = 1024;
		resources.maxTessControlTotalOutputComponents = 4096;
		resources.maxTessEvaluationInputComponents = 128;
		resources.maxTessEvaluationOutputComponents = 128;
		resources.maxTessEvaluationTextureImageUnits = 16;
		resources.maxTessEvaluationUniformComponents = 1024;
		resources.maxTessPatchComponents = 120;
		resources.maxPatchVertices = 32;
		resources.maxTessGenLevel = 64;
		resources.maxViewports = 16;
		resources.maxVertexAtomicCounters = 0;
		resources.maxTessControlAtomicCounters = 0;
		resources.maxTessEvaluationAtomicCounters = 0;
		resources.maxGeometryAtomicCounters = 0;
		resources.maxFragmentAtomicCounters = 8;
		resources.maxCombinedAtomicCounters = 8;
		resources.maxAtomicCounterBindings = 1;
		resources.maxVertexAtomicCounterBuffers = 0;
		resources.maxTessControlAtomicCounterBuffers = 0;
		resources.maxTessEvaluationAtomicCounterBuffers = 0;
		resources.maxGeometryAtomicCounterBuffers = 0;
		resources.maxFragmentAtomicCounterBuffers = 1;
		resources.maxCombinedAtomicCounterBuffers = 1;
		resources.maxAtomicCounterBufferSize = 16384;
		resources.maxTransformFeedbackBuffers = 4;
		resources.maxTransformFeedbackInterleavedComponents = 64;
		resources.maxCullDistances = 8;
		resources.maxCombinedClipAndCullDistances = 8;
		resources.maxSamples = 4;
		resources.maxMeshOutputVerticesNV = 256;
		resources.maxMeshOutputPrimitivesNV = 512;
		resources.maxMeshWorkGroupSizeX_NV = 32;
		resources.maxMeshWorkGroupSizeY_NV = 1;
		resources.maxMeshWorkGroupSizeZ_NV = 1;
		resources.maxTaskWorkGroupSizeX_NV = 32;
		resources.maxTaskWorkGroupSizeY_NV = 1;
		resources.maxTaskWorkGroupSizeZ_NV = 1;
		resources.maxMeshViewCountNV = 4;

		resources.limits.nonInductiveForLoops = true;
		resources.limits.whileLoops = true;
		resources.limits.doWhileLoops = true;
		resources.limits.generalUniformIndexing = true;
		resources.limits.generalAttributeMatrixVectorIndexing = true;
		resources.limits.generalVaryingIndexing = true;
		resources.limits.generalSamplerIndexing = true;
		resources.limits.generalVariableIndexing = true;
		resources.limits.generalConstantMatrixVectorIndexing = true;

		return resources;
	}

	const TBuiltInResource defaultBuiltInResource = getDefaultResources();
}

SpirvShaderWriter::SpirvShaderWriter(AstShaderStage stage) :
	m_stage(stage),
	m_glslWriter(stage, m_glslStream)
{
}

SpirvShaderWriter::~SpirvShaderWriter()
{
}

void SpirvShaderWriter::compile(std::vector<uint32_t>& spirv)
{
	compileSpirv(spirv);
}

void SpirvShaderWriter::compile(std::ostream& ostream)
{
	std::vector<uint32_t> spirv;

	compileSpirv(spirv);

	const auto codeData = reinterpret_cast<char*>(spirv.data());
	const auto codeSize = spirv.size() * sizeof(uint32_t);

	for (size_t i = 0; i < codeSize; i++)
		ostream << codeData[i];
}

void SpirvShaderWriter::visit(ConditionalStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ForLoopStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(WhileLoopStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(DoWhileLoopStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(VariableDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(StructDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(InputDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(OutputDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ExternalDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(OptionDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(FunctionDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(EntryFunctionDeclarationStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ExpressionStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(BreakStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ContinueStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ReturnStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(SequenceStatement& statement)
{
	m_glslWriter.visit(statement);
}

void SpirvShaderWriter::visit(ConstantExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(VariableExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(AccessIndexExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(AccessIdentifierExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(AssignmentExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(UnaryExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(BinaryExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(FunctionCallExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(BuiltInFunctionCallExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(CastExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(SwizzleExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::visit(TernaryExpression& expression)
{
	m_glslWriter.visit(expression);
}

void SpirvShaderWriter::compileSpirv(std::vector<uint32_t>& spirv)
{
	glslang::InitializeProcess();

	const auto eShLanguage = getEShLanguage(m_stage);

	// Create shader
	glslang::TShader shader(eShLanguage);

	const auto str = m_glslStream.str();
	const auto cstr = str.c_str();
	const int length = static_cast<int>(str.length());

	shader.setStringsWithLengths(&cstr, &length, 1);

	shader.setEnvInput(glslang::EShSourceGlsl, eShLanguage, glslang::EShClientVulkan, 100);
	shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
	shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_1);

	auto success = shader.parse(&defaultBuiltInResource, 100, ECoreProfile, false, false, EShMsgDefault);

	// Compilation failed
	if (!success)
	{
		const auto infoLog = shader.getInfoLog();

		ATEMA_ERROR("Compilation failed :\n" + std::string(infoLog));
	}

	// Create program
	glslang::TProgram program;

	program.addShader(&shader);

	success = program.link(EShMsgDefault);

	// Linking failed
	if (!success)
	{
		const auto infoLog = program.getInfoLog();

		ATEMA_ERROR("Linking failed :\n" + std::string(infoLog));
	}

	const auto intermediate = program.getIntermediate(eShLanguage);

	spv::SpvBuildLogger logger;

	glslang::SpvOptions spvOptions;

	spvOptions.generateDebugInfo = true;
	spvOptions.stripDebugInfo = true;
	spvOptions.disableOptimizer = false;
	spvOptions.optimizeSize = true;
	spvOptions.disassemble = false;
	spvOptions.validate = false;
	glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions);

	glslang::FinalizeProcess();
}
<|endoftext|>