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

#undef NDEBUG

#define WARN_INCLUDE_EMBREE_FILENAME 1

// O_LARGEFILE is a GNU extension.
#ifdef __APPLE__
#define  O_LARGEFILE  0
#endif

// sg
#include "SceneGraph.h"
#include "sg/common/Texture2D.h"
#include "sg/geometry/TriangleMesh.h"
#include <cmath>

#ifdef _WIN32
#include <string.h>
int strncasecmp(const char *s1, const char *s2, size_t n)
{ return _strnicmp(s1, s2, n); }
#endif

namespace ospray {
  namespace sg {

    std::shared_ptr<Texture2D> loadTexture(const std::string &path,
                                           const std::string &fileName,
                                           const bool preferLinear = false)
    {
      FileName texFileName = path + "/" + fileName;
      std::shared_ptr<Texture2D> tex = Texture2D::load(texFileName,
                                                       preferLinear);
      if (!tex)
        std::cout << "could not load texture " << texFileName << " !\n";
      return tex;
    }

    /*! Three-index vertex, indexing start at 0, -1 means invalid vertex. */
    struct Vertex {
      int v, vt, vn;
      Vertex() {};
      Vertex(int v) : v(v), vt(v), vn(v) {};
      Vertex(int v, int vt, int vn) : v(v), vt(vt), vn(vn) {};
    };

    static inline bool operator < ( const Vertex& a, const Vertex& b ) {
      if (a.v  != b.v)  return a.v  < b.v;
      if (a.vn != b.vn) return a.vn < b.vn;
      if (a.vt != b.vt) return a.vt < b.vt;
      return false;
    }

    /*! Fill space at the end of the token with 0s. */
    static inline const char* trimEnd(const char* token) {
      size_t len = strlen(token);
      if (len == 0) return token;
      char* pe = (char*)(token + len - 1);
      while ((*pe == ' ' || *pe == '\t' || *pe == '\r') && pe >= token)
        *pe-- = 0;
      return token;
    }

    /*! Determine if character is a separator. */
    static inline bool isSep(const char c) {
      return (c == ' ') || (c == '\t');
    }

    /*! Parse separator. */
    static inline const char* parseSep(const char*& token) {
      size_t sep = strspn(token, " \t");
      if (!sep) throw std::runtime_error("separator expected");
      return token+=sep;
    }

    /*! Parse optional separator. */
    static inline const char* parseSepOpt(const char*& token) {
      return token+=strspn(token, " \t");
    }

    /*! Read float from a string. */
    static inline float getFloat(const char*& token) {
      token += strspn(token, " \t");
      float n = (float)atof(token);
      token += strcspn(token, " \t\r");
      return n;
    }

    /*! Read vec2f from a string. */
    static inline vec2f getVec2f(const char*& token) {
      float x = getFloat(token);
      float y = getFloat(token);
      return vec2f(x,y);
    }

    /*! Read vec3f from a string. */
    static inline vec3f getVec3f(const char*& token) {
      float x = getFloat(token);
      float y = getFloat(token);
      float z = getFloat(token);
      return vec3f(x,y,z);
    }

    class OBJLoader
    {
    public:

      OBJLoader(std::shared_ptr<Node> world, const FileName& fileName);

    private:

      std::shared_ptr<Node> world;
      std::map<std::string, std::shared_ptr<Material>> material;

      void loadMTL(const FileName& fileName);

      template<typename T>
      inline T parse(const char *&token, std::string& type);

      /*! try to parse given token stream as a float-type with given
        keyword; and if successful, assign to material. returns true
        if matched, false if not */
      template<typename T>
      bool tryToMatch(const char *&token,
                      const char *keyWord,
                      const std::shared_ptr<Material> &mat);

      /*! try to parse given token stream as a float-type with given
        keyword; and if successful, load the texture and assign to
        material. returns true if matched, false if not */
      bool tryToMatchTexture(const char *&token,
                             const char *keyWord,
                             const std::shared_ptr<Material> &mat,
                             bool preferLinear=false);

      FileName path;
      FileName fullPath;

      /*! Geometry buffer. */
      #if 0
      std::vector<vec3f> v;
      std::vector<vec3f> vn;
      std::vector<vec2f> vt;
      #else
      DataVector3f v;
      DataVector3f vn;
      DataVector2f vt;
      #endif
      std::vector<std::vector<Vertex>> curGroup;
      std::string curGroupName;

      /*! Material handling. */
      std::shared_ptr<Material> curMaterial;
      std::shared_ptr<Material> defaultMaterial;
      //std::map<std::string, Handle<Device::RTMaterial> > material;

      /*! Internal methods. */
      int fix_v (int index);
      int fix_vt(int index);
      int fix_vn(int index);
      void flushFaceGroup();
      Vertex getInt3(const char*& token);
      uint32_t getVertex(std::map<Vertex,uint32_t>& vertexMap,
                         const std::shared_ptr<TriangleMesh> &mesh,
                         const Vertex& i);
    };

    template<> inline vec3f OBJLoader::parse(const char *&token, std::string& type)
    { type="vec3f"; return getVec3f(token); }
    template<> inline float OBJLoader::parse(const char *&token, std::string& type)
    { type="float"; return getFloat(token); }
    template<> inline std::string OBJLoader::parse(const char *&token, std::string& type)
    { type="string"; return std::string(token); }

    OBJLoader::OBJLoader(std::shared_ptr<Node> world, const FileName &fileName)
      : world(world),
        path(fileName.path()),
        fullPath(fileName)
    {
      /* open file */
      std::ifstream cin;
      cin.open(fileName.c_str());
      if (!cin.is_open()) {
        std::cerr << "cannot open " << fileName.str() << std::endl;
        return;
      }

      // /* generate default material */
      defaultMaterial =
        std::static_pointer_cast<Material>(createNode("material","Material"));
      curMaterial = defaultMaterial;

      char line[1000000];
      memset(line, 0, sizeof(line));

      while (cin.peek() != -1) {
        /* load next multiline */
        char* pline = line;
        while (true) {
          cin.getline(pline, sizeof(line) - (pline - line) - 16, '\n');
          ssize_t last = strlen(pline) - 1;
          if (last < 0 || pline[last] != '\\') break;
          pline += last;
          *pline++ = ' ';
        }

        const char* token = trimEnd(line + strspn(line, " \t"));
        if (token[0] == 0) continue;

        if (token[0] == 'g')
          curGroupName = std::string(parseSep(token += 1));

        /*! parse position */
        if (token[0] == 'v' && isSep(token[1]))
        { v.push_back(getVec3f(token += 2)); continue; }

        /* parse normal */
        if (token[0] == 'v' && token[1] == 'n' && isSep(token[2]))
        { vn.push_back(getVec3f(token += 3)); continue; }

        /* parse texcoord */
        if (token[0] == 'v' && token[1] == 't' && isSep(token[2]))
        { vt.push_back(getVec2f(token += 3)); continue; }

        /*! parse face */
        if (token[0] == 'f' && isSep(token[1]))
        {
          parseSep(token += 1);

          std::vector<Vertex> face;
          while (token[0]) {
            face.push_back(getInt3(token));
            parseSepOpt(token);
          }
          curGroup.push_back(face);
          continue;
        }

        /*! use material */
        if (!strncmp(token, "usemtl", 6) && isSep(token[6]))
        {
          flushFaceGroup();
          std::string name(parseSep(token += 6));

          if (material.find(name) == material.end())
            curMaterial = defaultMaterial;
          else
            curMaterial = material[name];

          continue;
        }

        /* load material library */
        if (!strncmp(token, "mtllib", 6) && isSep(token[6])) {
          loadMTL(path + std::string(parseSep(token += 6)));
          continue;
        }

        // ignore unknown stuff
      }
      flushFaceGroup();
      cin.close();
    }

    /*! try to parse given token stream as a float-type with given
      keyword; and if successful, assign to material. returns true
      if matched, false if not */
    template<typename T>
    bool OBJLoader::tryToMatch(const char *&token,
                               const char *keyWord,
                               const std::shared_ptr<Material> &mat)
    {
      if (strncasecmp(token, keyWord, strlen(keyWord)))
        return false;

      parseSep(token+=strlen(keyWord));
      std::string type;
      auto val = parse<T>(token,type);
      mat->createChild(keyWord, type, val);

      return true;
    }

    /*! try to parse given token stream as a float-type with given
      keyword; and if successful, load the texture and assign to
      material. returns true if matched, false if not */
    bool OBJLoader::tryToMatchTexture(const char *&token,
                                      const char *keyWord,
                                      const std::shared_ptr<Material> &mat,
                                      bool preferLinear)
    {
      if (strncasecmp(token, keyWord, strlen(keyWord)))
        return false;

      parseSep(token+=strlen(keyWord));
      std::string type;
      auto node =  loadTexture(path,parse<std::string>(token,type),preferLinear);
      std::string name(keyWord);
      mat->setChild(keyWord, node);
      node->setParent(mat);
      return true;
    }

    /* load material file */
    void OBJLoader::loadMTL(const FileName &fileName)
    {
      std::ifstream cin;
      cin.open(fileName.c_str());
      if (!cin.is_open()) {
        std::cerr << "cannot open " << fileName.str() << std::endl;
        return;
      }

      char line[10000];
      memset(line, 0, sizeof(line));

      //      Handle<Device::RTMaterial> cur = null;
      std::shared_ptr<Material> cur = std::static_pointer_cast<Material>(createNode("material","Material"));
      while (cin.peek() != -1)
        {
          /* load next multiline */
          char* pline = line;
          while (true) {
            cin.getline(pline, sizeof(line) - (pline - line) - 16, '\n');
            ssize_t last = strlen(pline) - 1;
            if (last < 0 || pline[last] != '\\') break;
            pline += last;
            *pline++ = ' ';
          }
          const char* token = trimEnd(line + strspn(line, " \t"));

          if (token[0] == 0  ) continue; // ignore empty lines
          if (token[0] == '#') continue; // ignore comments

          if (!strncmp(token, "newmtl", 6)) {
            parseSep(token+=6);
            // if (cur) g_device->rtCommit(cur);
            std::string name(token);
            material[name] = cur = std::static_pointer_cast<Material>(createNode(name,"Material"));
             //g_device->rtNewMaterial("obj");
            //            model.material.push_back(cur);
            cur->name = name;
            cur->type = "OBJ";
            continue;
          }

          if (!cur) {
            std::cout << "#osp:sg:importOBJ: Ignoring line >> " << line << " <<"
                      << std::endl;
            continue;
          }//throw std::runtime_error("invalid material file: newmtl expected first");

          if (!strncmp(token, "illum_4",7)) {
            /*! iw: hack for VMD-exported OBJ files, working around a
              bug in VMD's OBJ exporter (VMD writes "illum_4" (with
              an underscore) rather than "illum 4" (with a
              whitespace) */
            parseSep(token += 7);
            continue;
          }

          if (tryToMatch<float>(token,"illum",cur)) continue;
          if (tryToMatch<float>(token,"d",cur)) continue;
          if (tryToMatch<float>(token,"Ns",cur)) continue;
          if (tryToMatch<float>(token,"Ni",cur)) continue;
          if (tryToMatch<vec3f>(token,"Ka",cur)) continue;
          if (tryToMatch<vec3f>(token,"Kd",cur)) continue;
          if (tryToMatch<vec3f>(token,"Ks",cur)) continue;
          //todo: Tf in path tracer often produces transparent walls
          // if (tryToMatch<vec3f>(token,"Tf",cur)) continue;

          if (tryToMatchTexture(token,"map_d",cur,true)) continue;
          if (tryToMatchTexture(token,"map_Ns",cur,true)) continue;
          if (tryToMatchTexture(token,"map_Ka",cur)) continue;
          if (tryToMatchTexture(token,"map_Kd",cur)) continue;
          if (tryToMatchTexture(token,"map_Ks",cur)) continue;

          /*! the following are extensions to the standard */
          if (tryToMatch<vec3f>(token,"color",cur)) continue;
          if (tryToMatchTexture(token,"map_Refl",cur)) continue;
          if (tryToMatchTexture(token,"map_Bump",cur)) continue;

          if (!strncmp(token, "type", 4)) {
            parseSep(token += 4);
            cur->child("type").setValue(std::string(token));
            continue;
          }

          //add anything else as float param
          const char * ident = token;
          token += strcspn(token, " \t");
          *(char*)token = 0;
          parseSepOpt(token += 1);
          cur->createChildWithValue(ident, "float", getFloat(token));
        }
      // if (cur) g_device->rtCommit(cur);
      cin.close();
    }

    /*! handles relative indices and starts indexing from 0 */
    int OBJLoader::fix_v (int index)
    { return(index > 0 ? index - 1 : (index == 0 ? 0 : (int)v.size()+index)); }
    int OBJLoader::fix_vt(int index)
    { return(index > 0 ? index - 1 : (index == 0 ? 0 : (int)vt.size()+index)); }
    int OBJLoader::fix_vn(int index)
    { return(index > 0 ? index - 1 : (index == 0 ? 0 : (int)vn.size()+index)); }

    /*! Parse differently formated triplets like: n0, n0/n1/n2, n0//n2, n0/n1.          */
    /*! All indices are converted to C-style (from 0). Missing entries are assigned -1. */
    Vertex OBJLoader::getInt3(const char*& token)
    {
      Vertex v(-1);
      v.v = fix_v(atoi(token));
      token += strcspn(token, "/ \t\r");
      if (token[0] != '/') return(v);
      token++;

      // it is i//n
      if (token[0] == '/') {
        token++;
        v.vn = fix_vn(atoi(token));
        token += strcspn(token, " \t\r");
        return(v);
      }

      // it is i/t/n or i/t
      v.vt = fix_vt(atoi(token));
      token += strcspn(token, "/ \t\r");
      if (token[0] != '/') return(v);
      token++;

      // it is i/t/n
      v.vn = fix_vn(atoi(token));
      token += strcspn(token, " \t\r");
      return(v);
    }

    uint32_t OBJLoader::getVertex(std::map<Vertex,uint32_t>& vertexMap,
                                  const std::shared_ptr<TriangleMesh> &mesh,
                                  const Vertex& i)
    {
      const std::map<Vertex, uint32_t>::iterator& entry = vertexMap.find(i);
      if (entry != vertexMap.end()) return(entry->second);

      if (std::isnan(v[i.v].x) || std::isnan(v[i.v].y) || std::isnan(v[i.v].z))
        return -1;

      if (i.vn >= 0 && (std::isnan(vn[i.vn].x) ||
                        std::isnan(vn[i.vn].y) ||
                        std::isnan(vn[i.vn].z)))
        return -1;

      if (i.vt >= 0 && (std::isnan(vt[i.vt].x) ||
                        std::isnan(vt[i.vt].y)))
        return -1;

      mesh->vertex().nodeAs<DataVector3f>()->push_back(v[i.v]);

      if (i.vn >= 0) //mesh->normal.cast<DataVector3f>()->v.push_back(vn[i.vn]);
        mesh->normal().nodeAs<DataVector3f>()->push_back(vn[i.vn]);
      if (i.vt >= 0) //mesh->texcoord.cast<DataVector2f>()->v.push_back(vt[i.vt]);
        mesh->texcoord().nodeAs<DataVector2f>()->push_back(vt[i.vt]);
      return(vertexMap[i] = int(mesh->vertex()->getSize()) - 1);
    }

    /*! end current facegroup and append to mesh */
    void OBJLoader::flushFaceGroup()
    {
      if (curGroup.empty()) return;

      std::map<Vertex, uint32_t> vertexMap;
      static int counter=0;
      std::stringstream ss;
      ss << fullPath.name() << "_" << counter++ << "_" << curGroupName;
      std::string name = ss.str();
      //scenegraph
      auto mesh = createNode(name, "TriangleMesh").nodeAs<TriangleMesh>();
      auto model = createNode(name+"_model", "Model");
      auto instance = createNode(name+"_instance", "Instance");
      model->add(mesh);
      instance->setChild("model", model);
      model->setParent(instance);
      world->add(instance);
      mesh->vertex = std::make_shared<DataVector3f>();
      mesh->normal = std::make_shared<DataVector3f>();
      mesh->texcoord = std::make_shared<DataVector2f>();
      mesh->index =  std::make_shared<DataVector3i>();
      mesh->setChild("material",curMaterial);
      curMaterial->setParent(mesh);

      // merge three indices into one
      for (size_t j=0; j < curGroup.size(); j++) {
        /* iterate over all faces */
        const std::vector<Vertex>& face = curGroup[j];
        Vertex i0 = face[0], i1 = Vertex(-1), i2 = face[1];

        /* triangulate the face with a triangle fan */
        for (size_t k=2; k < face.size(); k++) {
          i1 = i2; i2 = face[k];
          int32_t v0 = getVertex(vertexMap, mesh, i0);
          int32_t v1 = getVertex(vertexMap, mesh, i1);
          int32_t v2 = getVertex(vertexMap, mesh, i2);
          if (v0 < 0 || v1 < 0 || v2 < 0)
            continue;

          vec3i tri(v0,v1,v2);
          std::dynamic_pointer_cast<DataVector3i>(mesh->index)->push_back(tri); //Vec3i(v0, v1, v2));
        }
      }
      curGroup.clear();
    }

    void importOBJ(const std::shared_ptr<Node> &world, const FileName &fileName)
    {
      std::cout << "ospray::sg::importOBJ: importing from " << fileName
                << std::endl;
      OBJLoader loader(world, fileName);
    }
  } // ::ospray::sg
} // ::ospray

<commit_msg>remove old (unused) sg obj loader<commit_after><|endoftext|>


<commit_before>// -*- Mode:C++; Coding:us-ascii-unix; fill-column:158 -*-
/*******************************************************************************************************************************************************.H.S.**/
/**
 @file      mandelbrot_pickover.cpp
 @author    Mitch Richling <https://www.mitchr.me>
 @brief     Draw a Mandelbrot set with Pickover Stalks.@EOL
 @std       C++20
 @copyright
  @parblock
  Copyright (c) 1988-2015, Mitchell Jay Richling <https://www.mitchr.me> All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation
     and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software
     without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  DAMAGE.
  @endparblock
*/
/*******************************************************************************************************************************************************.H.E.**/
/** @cond exj */

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "ramCanvas.hpp"

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
typedef mjr::ramCanvas3c8b::colorType ct;

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const int    IMGSIZ = 7680/4;
  const int    MAXITR = 1024;
  const double MAXZSQ = 1008.0;
  mjr::ramCanvas3c8b theRamCanvas(IMGSIZ, IMGSIZ, -2.2, 0.8, -1.5, 1.5);

# pragma omp parallel for schedule(static,1)
  for(int y=0;y<theRamCanvas.getNumPixY();y++) {
    for(int x=0;x<theRamCanvas.getNumPixX();x++) {
      std::complex<double> c = theRamCanvas.int2real(x, y);
      std::complex<double> z(0.0, 0.0);
      double minX = theRamCanvas.getCanvasWidD();
      double minY = theRamCanvas.getCanvasWidD();
      int count = 0;
      while((std::norm(z)<MAXZSQ) && (count<=MAXITR)) {
        z=std::pow(z, 2) + c;
        if (std::abs(std::real(z)) < minX)
          minX = std::abs(std::real(z));
        if (std::abs(std::imag(z)) < minY)
          minY = std::abs(std::imag(z));
        count++;
      }
      if(count < MAXITR)
        theRamCanvas.drawPoint(x, y, ct::csCCfractalYB::c(static_cast<ct::csIntType>(std::log(1+std::min(minX, minY))*500)));
    }
  }
  theRamCanvas.writeTIFFfile("mandelbrot_pickover.tiff");
  std::chrono::duration<double> runTime = std::chrono::system_clock::now() - startTime;
  std::cout << "Total Runtime " << runTime.count() << " sec" << std::endl;
}
/** @endcond */
<commit_msg>Full size<commit_after>// -*- Mode:C++; Coding:us-ascii-unix; fill-column:158 -*-
/*******************************************************************************************************************************************************.H.S.**/
/**
 @file      mandelbrot_pickover.cpp
 @author    Mitch Richling <https://www.mitchr.me>
 @brief     Draw a Mandelbrot set with Pickover Stalks.@EOL
 @std       C++20
 @copyright
  @parblock
  Copyright (c) 1988-2015, Mitchell Jay Richling <https://www.mitchr.me> All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation
     and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software
     without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  DAMAGE.
  @endparblock
*/
/*******************************************************************************************************************************************************.H.E.**/
/** @cond exj */

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "ramCanvas.hpp"

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
typedef mjr::ramCanvas3c8b::colorType ct;

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const int    IMGSIZ = 7680;
  const int    MAXITR = 1024;
  const double MAXZSQ = 1008.0;
  mjr::ramCanvas3c8b theRamCanvas(IMGSIZ, IMGSIZ, -2.2, 0.8, -1.5, 1.5);

# pragma omp parallel for schedule(static,1)
  for(int y=0;y<theRamCanvas.getNumPixY();y++) {
    for(int x=0;x<theRamCanvas.getNumPixX();x++) {
      std::complex<double> c = theRamCanvas.int2real(x, y);
      std::complex<double> z(0.0, 0.0);
      double minX = theRamCanvas.getCanvasWidD();
      double minY = theRamCanvas.getCanvasWidD();
      int count = 0;
      while((std::norm(z)<MAXZSQ) && (count<=MAXITR)) {
        z=std::pow(z, 2) + c;
        if (std::abs(std::real(z)) < minX)
          minX = std::abs(std::real(z));
        if (std::abs(std::imag(z)) < minY)
          minY = std::abs(std::imag(z));
        count++;
      }
      if(count < MAXITR)
        theRamCanvas.drawPoint(x, y, ct::csCCfractalYB::c(static_cast<ct::csIntType>(std::log(1+std::min(minX, minY))*500)));
    }
  }
  theRamCanvas.writeTIFFfile("mandelbrot_pickover.tiff");
  std::chrono::duration<double> runTime = std::chrono::system_clock::now() - startTime;
  std::cout << "Total Runtime " << runTime.count() << " sec" << std::endl;
}
/** @endcond */
<|endoftext|>


<commit_before>// C++ source file - (C) 2003 Robert Osfield, released under the OSGPL.
//
// Simple example of use of Producer::RenderSurface to create an OpenGL
// graphics window, and OSG for rendering.

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <osgGA/AnimationPathManipulator>
#include <iostream>

void singleWindowMultipleCameras(osgViewer::Viewer& viewer)
{
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
    if (!wsi)
    {
        osg::notify(osg::NOTICE)<<"View::setUpViewAcrossAllScreens() : Error, no WindowSystemInterface available, cannot create windows."<<std::endl;
        return;
    }

    unsigned int width, height;
    wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);

    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
    traits->x = 0;
    traits->y = 0;
    traits->width = width;
    traits->height = height;
#if 0
    traits->windowDecoration = false;
#else
    traits->windowDecoration = true;
#endif
    traits->doubleBuffer = true;
    traits->sharedContext = 0;

    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(gc.get());
    if (gw)
    {
        osg::notify(osg::INFO)<<"  GraphicsWindow has been created successfully."<<gw<<std::endl;

        gw->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, width, height );
    }
    else
    {
        osg::notify(osg::NOTICE)<<"  GraphicsWindow has not been created successfully."<<std::endl;
    }

    unsigned int numCameras = 2;
    double aspectRatioScale = 1.0;///(double)numCameras;
    for(unsigned int i=0; i<numCameras;++i)
    {
        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
        camera->setGraphicsContext(gc.get());
        camera->setViewport(new osg::Viewport((i*width)/numCameras,(i*height)/numCameras, width/numCameras, height/numCameras));
        GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
        camera->setDrawBuffer(buffer);
        camera->setReadBuffer(buffer);

        viewer.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::scale(aspectRatioScale,1.0,1.0));
    }
}

void multipleWindowMultipleCameras(osgViewer::Viewer& viewer)
{
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
    if (!wsi)
    {
        osg::notify(osg::NOTICE)<<"View::setUpViewAcrossAllScreens() : Error, no WindowSystemInterface available, cannot create windows."<<std::endl;
        return;
    }

    unsigned int width, height;
    wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);

    unsigned int numCameras = 4;
    double aspectRatioScale = (double)numCameras;
    double translate_x = double(numCameras)-1;
    for(unsigned int i=0; i<numCameras;++i, translate_x -= 2.0)
    {
        osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
        traits->screenNum = i / 2;
        traits->x = (i*width)/numCameras;
        traits->y = 0;
        traits->width = width/numCameras-1;
        traits->height = height;
    #if 0
        traits->windowDecoration = false;
    #else
        traits->windowDecoration = true;
    #endif
        traits->doubleBuffer = true;
        traits->sharedContext = 0;

        osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

        osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(gc.get());
        if (gw)
        {
            osg::notify(osg::INFO)<<"  GraphicsWindow has been created successfully."<<gw<<std::endl;

            gw->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height );
        }
        else
        {
            osg::notify(osg::NOTICE)<<"  GraphicsWindow has not been created successfully."<<std::endl;
        }

        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
        camera->setGraphicsContext(gc.get());
        camera->setViewport(new osg::Viewport(0,0, width/numCameras, height));
        GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
        camera->setDrawBuffer(buffer);
        camera->setReadBuffer(buffer);

        viewer.addSlave(camera.get(), osg::Matrix::scale(aspectRatioScale, 1.0, 1.0)*osg::Matrix::translate(translate_x, 0.0, 0.0), osg::Matrix() );
    }
}

int main( int argc, char **argv )
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    if (argc<2)
    {
        std::cout << argv[0] <<": requires filename argument." << std::endl;
        return 1;
    }

    std::string pathfile;
    osg::ref_ptr<osgGA::AnimationPathManipulator> apm = 0;
    while (arguments.read("-p",pathfile))
    {
        apm = new osgGA::AnimationPathManipulator(pathfile);
        if(!apm.valid() || !(apm->valid()) )
        {
            apm = 0;
        }
    }

    // load the scene.
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
    if (!loadedModel)
    {
        std::cout << argv[0] <<": No data loaded." << std::endl;
        return 1;
    }

    osgViewer::Viewer viewer;

    while (arguments.read("-s")) { viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); }
    while (arguments.read("-g")) { viewer.setThreadingModel(osgViewer::Viewer::ThreadPerContext); }
    while (arguments.read("-c")) { viewer.setThreadingModel(osgViewer::Viewer::ThreadPerCamera); }

//    viewer.setSceneData(loadedModel.get());

    if (apm.valid()) viewer.setCameraManipulator(apm.get());
    else viewer.setCameraManipulator( new osgGA::TrackballManipulator() );

#if 1

    // singleWindowMultipleCameras(viewer);

    multipleWindowMultipleCameras(viewer);

#endif

    viewer.setSceneData(loadedModel.get());

    viewer.realize();

    bool limitNumberOfFrames = false;
    unsigned int numFrames = 0;
    unsigned int maxFrames = 10;

    while(!viewer.done() && !(limitNumberOfFrames && numFrames>=maxFrames))
    {
        viewer.frame();
        ++numFrames;
    }

    return 0;
}
<commit_msg>Changed compile paths into runtime selected paths using command line parameters<commit_after>// C++ source file - (C) 2003 Robert Osfield, released under the OSGPL.
//
// Simple example of use of Producer::RenderSurface to create an OpenGL
// graphics window, and OSG for rendering.

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <osgGA/AnimationPathManipulator>
#include <iostream>

void singleWindowMultipleCameras(osgViewer::Viewer& viewer)
{
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
    if (!wsi)
    {
        osg::notify(osg::NOTICE)<<"View::setUpViewAcrossAllScreens() : Error, no WindowSystemInterface available, cannot create windows."<<std::endl;
        return;
    }

    unsigned int width, height;
    wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);

    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
    traits->x = 0;
    traits->y = 0;
    traits->width = width;
    traits->height = height;
#if 0
    traits->windowDecoration = false;
#else
    traits->windowDecoration = true;
#endif
    traits->doubleBuffer = true;
    traits->sharedContext = 0;

    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(gc.get());
    if (gw)
    {
        osg::notify(osg::INFO)<<"  GraphicsWindow has been created successfully."<<gw<<std::endl;

        gw->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, width, height );
    }
    else
    {
        osg::notify(osg::NOTICE)<<"  GraphicsWindow has not been created successfully."<<std::endl;
    }

    unsigned int numCameras = 2;
    double aspectRatioScale = 1.0;///(double)numCameras;
    for(unsigned int i=0; i<numCameras;++i)
    {
        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
        camera->setGraphicsContext(gc.get());
        camera->setViewport(new osg::Viewport((i*width)/numCameras,(i*height)/numCameras, width/numCameras, height/numCameras));
        GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
        camera->setDrawBuffer(buffer);
        camera->setReadBuffer(buffer);

        viewer.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::scale(aspectRatioScale,1.0,1.0));
    }
}

void multipleWindowMultipleCameras(osgViewer::Viewer& viewer)
{
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
    if (!wsi)
    {
        osg::notify(osg::NOTICE)<<"View::setUpViewAcrossAllScreens() : Error, no WindowSystemInterface available, cannot create windows."<<std::endl;
        return;
    }

    unsigned int width, height;
    wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);

    unsigned int numCameras = 4;
    double aspectRatioScale = (double)numCameras;
    double translate_x = double(numCameras)-1;
    for(unsigned int i=0; i<numCameras;++i, translate_x -= 2.0)
    {
        osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
        traits->screenNum = i / 2;
        traits->x = (i*width)/numCameras;
        traits->y = 0;
        traits->width = width/numCameras-1;
        traits->height = height;
    #if 0
        traits->windowDecoration = false;
    #else
        traits->windowDecoration = true;
    #endif
        traits->doubleBuffer = true;
        traits->sharedContext = 0;

        osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

        osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(gc.get());
        if (gw)
        {
            osg::notify(osg::INFO)<<"  GraphicsWindow has been created successfully."<<gw<<std::endl;

            gw->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height );
        }
        else
        {
            osg::notify(osg::NOTICE)<<"  GraphicsWindow has not been created successfully."<<std::endl;
        }

        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
        camera->setGraphicsContext(gc.get());
        camera->setViewport(new osg::Viewport(0,0, width/numCameras, height));
        GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
        camera->setDrawBuffer(buffer);
        camera->setReadBuffer(buffer);

        viewer.addSlave(camera.get(), osg::Matrix::scale(aspectRatioScale, 1.0, 1.0)*osg::Matrix::translate(translate_x, 0.0, 0.0), osg::Matrix() );
    }
}

int main( int argc, char **argv )
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    if (argc<2)
    {
        std::cout << argv[0] <<": requires filename argument." << std::endl;
        return 1;
    }

    std::string pathfile;
    osg::ref_ptr<osgGA::AnimationPathManipulator> apm = 0;
    while (arguments.read("-p",pathfile))
    {
        apm = new osgGA::AnimationPathManipulator(pathfile);
        if(!apm.valid() || !(apm->valid()) )
        {
            apm = 0;
        }
    }

    osgViewer::Viewer viewer;

    while (arguments.read("-s")) { viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); }
    while (arguments.read("-g")) { viewer.setThreadingModel(osgViewer::Viewer::ThreadPerContext); }
    while (arguments.read("-c")) { viewer.setThreadingModel(osgViewer::Viewer::ThreadPerCamera); }

    bool limitNumberOfFrames = false;
    unsigned int maxFrames = 10;
    while (arguments.read("--run-till-frame-number",maxFrames)) { limitNumberOfFrames = true; }

    // alternative viewer window setups.
    while (arguments.read("-1")) { singleWindowMultipleCameras(viewer); }
    while (arguments.read("-2")) { multipleWindowMultipleCameras(viewer); }

    if (apm.valid()) viewer.setCameraManipulator(apm.get());
    else viewer.setCameraManipulator( new osgGA::TrackballManipulator() );

    // load the scene.
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
    if (!loadedModel)
    {
        std::cout << argv[0] <<": No data loaded." << std::endl;
        return 1;
    }

    viewer.setSceneData(loadedModel.get());

    viewer.realize();

    unsigned int numFrames = 0;
    while(!viewer.done() && !(limitNumberOfFrames && numFrames>=maxFrames))
    {
        viewer.frame();
        ++numFrames;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip>
#include <time.h>

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#include "Noise.h"
#include <noise/noise.h>

using namespace std;

const size_t THREAD_COUNT = 2;
const int MILLION_POINTS = 100;

double Timer_getSeconds ()
{
	double ticks;
#ifdef WIN32
	ticks=(double)GetTickCount() / 1000.0;
#else
	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC,&now);
	ticks=(now.tv_sec)+(now.tv_nsec)/1000000000.0;
#endif
	return(ticks);
}

/// 2D pipeline job generating a line
class LineJob2D : public noisepp::PipelineJob
{
	private:
		noisepp::Pipeline2D *mPipe;
		noisepp::PipelineElement2D *mElement;
		double x, y;
		int n;
		double xDelta;
		double *buffer;
		bool mProgress;

	public:
		LineJob2D (noisepp::Pipeline2D *pipe, noisepp::PipelineElement2D *element, double x, double y, int n, double xDelta, double *buffer, bool progress) :
			mPipe(pipe), mElement(element), x(x), y(y), n(n), xDelta(xDelta), buffer(buffer), mProgress(progress)
		{}
		void execute (noisepp::Cache *cache)
		{
			for (int i=0;i<n;++i)
			{
				// cleans the cache
				mPipe->cleanCache (cache);
				// calculates the value
				buffer[i] = mElement->getValue(x, y, cache);
				// move on
				x += xDelta;
			}
		}
		void finish ()
		{
			if (mProgress)
			{
				cout << ".";
				cout.flush ();
			}
		}
};

/// 3D pipeline job generating a line
class LineJob3D : public noisepp::PipelineJob
{
	private:
		noisepp::Pipeline3D *mPipe;
		noisepp::PipelineElement3D *mElement;
		double x, y, z;
		int n;
		double xDelta;
		double *buffer;
		bool mProgress;

	public:
		LineJob3D (noisepp::Pipeline3D *pipe, noisepp::PipelineElement3D *element, double x, double y, double z, int n, double xDelta, double *buffer, bool progress) :
			mPipe(pipe), mElement(element), x(x), y(y), z(z), n(n), xDelta(xDelta), buffer(buffer), mProgress(progress)
		{}
		void execute (noisepp::Cache *cache)
		{
			for (int i=0;i<n;++i)
			{
				// cleans the cache
				mPipe->cleanCache (cache);
				// calculates the value
				buffer[i] = mElement->getValue(x, y, z, cache);
				// move on
				x += xDelta;
			}
		}
		void finish ()
		{
			if (mProgress)
			{
				cout << ".";
				cout.flush ();
			}
		}
};

/// calculates the maximum difference between the two arrays specified
double calcMaxDifference (double *left, double *right, int n)
{
	double dif = 0;
	for (int i=0;i<n;++i)
	{
		const double d = fabs(left[i]-right[i]);
		if (d > dif)
			dif = d;
	}
	return dif;
}

int main ()
{
	double x, y;
	double xdelta = 0.1;
	double ydelta = 0.1;

	// generate output arrays
	double *libnoise_results = new double[1000000];
	double *noisepp_results = new double[1000000];

	// the libnoise module for comparision
	noise::module::Perlin libnoisePerlin;

	// the Noise++ modules
	noisepp::PerlinModule2D noiseppPerlin2D;
	noisepp::PerlinModule3D noiseppPerlin3D;

	// create default pipelines and fill them with our modules
	noisepp::Pipeline2D pipeline2D;
	noisepp::ElementID noiseID = noiseppPerlin2D.addToPipe (pipeline2D);
	noisepp::Pipeline3D pipeline3D;
	noisepp::ElementID noiseID3D = noiseppPerlin3D.addToPipe (pipeline3D);

	// create threaded pipelines and fill them with modules
#if NOISEPP_ENABLE_THREADS
	noisepp::ThreadedPipeline2D pipeline2D_mt(THREAD_COUNT);
	noisepp::ElementID noiseID2D_mt = noiseppPerlin2D.addToPipe (pipeline2D_mt);
	noisepp::ThreadedPipeline3D pipeline3D_mt(THREAD_COUNT);
	noisepp::ElementID noiseID3D_mt = noiseppPerlin3D.addToPipe (pipeline3D_mt);
#endif

	// creates the cache
	noisepp::Cache *cache = pipeline2D.createCache();
	noisepp::Cache *cache3D = pipeline3D.createCache();

	// calculate a perlin noise value
	cout << "libnoise: " << libnoisePerlin.GetValue (1.2, 0.2, 0) << std::endl;
	cout << "Noise++ (2D): " << pipeline2D.getElement(noiseID)->getValue (1.2, 0.2, cache) << std::endl;
	cout << "Noise++ (3D): " << pipeline3D.getElement(noiseID3D)->getValue (1.2, 0.2, 0, cache3D) << std::endl;

	// calculates some square roots to bring the CPU(s) to maximum frequency - i know, not really professional solution
	// (you can skip this step but at least on my PC I get more accurate benchmark results with it)
	cout << "prepare for generating " << MILLION_POINTS << " million points ... ";
	cout.flush ();

	double *test = new double[1000000];
	for (int n=0;n<100;++n)
	{
		for (int i=0;i<1000000;++i)
		{
			test[i] = sqrt((double)rand()/RAND_MAX);
		}
	}
	delete[] test;

	cout << "go!" << endl;

	// generate the points with libnoise
	double seconds;
	double mpix = MILLION_POINTS;
	cout << "generating using libnoise ";
	cout.flush ();
	double lnoiseStart = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		cout << ".";
		cout.flush ();
		double *res = libnoise_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			x = 0;
			for (int xx=0;xx<1000;++xx)
			{
				*res++ = libnoisePerlin.GetValue (x, y, 0);
				x += xdelta;
			}
			y += ydelta;
		}
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-lnoiseStart;
	double libnoiseTime = seconds * 100.0;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second" << endl;

	// get the pointers to the pipeline elements
	noisepp::PipelineElement2D *element = pipeline2D.getElement(noiseID);
	noisepp::PipelineElement3D *element3D = pipeline3D.getElement(noiseID3D);
#if NOISEPP_ENABLE_THREADS
	noisepp::PipelineElement2D *element2D_mt = pipeline2D.getElement(noiseID2D_mt);
	noisepp::PipelineElement3D *element3D_mt = pipeline3D.getElement(noiseID3D_mt);
#endif

	// generate using the default 3D pipeline
	cout << "generating using Pipeline3D ";
	cout.flush ();
	double noiseppStart3D = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		cout << ".";
		cout.flush ();
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			x = 0;
			for (int xx=0;xx<1000;++xx)
			{
				// clean cache
				pipeline3D.cleanCache(cache3D);
				// calculate value
				*res++ = element3D->getValue (x, y, 0, cache3D);
				// move on
				x += xdelta;
			}
			y += ydelta;
		}
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart3D;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;

	// generate using the default 2D pipeline
	cout << "generating using Pipeline2D ";
	cout.flush ();
	double noiseppStart2D = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		cout << ".";
		cout.flush ();
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			x = 0;
			for (int xx=0;xx<1000;++xx)
			{
				pipeline2D.cleanCache(cache);
				*res++ = element->getValue (x, y, cache);
				x += xdelta;
			}
			y += ydelta;
		}
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart2D;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	// check for difference
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;

#if NOISEPP_ENABLE_THREADS
	// generate using the threaded 3D pipeline
	cout << "generating using ThreadedPipeline3D " << endl;
	cout.flush ();
	double noiseppStart3D_mt = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			pipeline3D_mt.addJob (new LineJob3D(&pipeline3D_mt, element3D_mt, 0, y, 0, 1000, xdelta, res, (yy==0)));
			res += 1000;
			y += ydelta;
		}
		pipeline3D_mt.executeJobs ();
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart3D_mt;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	// check for difference
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;

	// generate using the threaded 2D pipeline
	cout << "generating using ThreadedPipeline2D " << endl;
	cout.flush ();
	double noiseppStart2D_mt = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			pipeline2D_mt.addJob (new LineJob2D(&pipeline2D_mt, element2D_mt, 0, y, 1000, xdelta, res, (yy==0)));
			res += 1000;
			y += ydelta;
		}
		pipeline2D_mt.executeJobs ();
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart2D_mt;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	// check for difference
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;
#endif

	delete[] libnoise_results;
	delete[] noisepp_results;

	// frees the cache
	pipeline2D.freeCache(cache);
	pipeline3D.freeCache(cache3D);

#ifdef WIN32
	cout << "Press any key ..." << endl;
	cin.get();
#endif

	return 0;
}
<commit_msg>removed wrong endl from example<commit_after>#include <iostream>
#include <iomanip>
#include <time.h>

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#include "Noise.h"
#include <noise/noise.h>

using namespace std;

const size_t THREAD_COUNT = 2;
const int MILLION_POINTS = 100;

double Timer_getSeconds ()
{
	double ticks;
#ifdef WIN32
	ticks=(double)GetTickCount() / 1000.0;
#else
	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC,&now);
	ticks=(now.tv_sec)+(now.tv_nsec)/1000000000.0;
#endif
	return(ticks);
}

/// 2D pipeline job generating a line
class LineJob2D : public noisepp::PipelineJob
{
	private:
		noisepp::Pipeline2D *mPipe;
		noisepp::PipelineElement2D *mElement;
		double x, y;
		int n;
		double xDelta;
		double *buffer;
		bool mProgress;

	public:
		LineJob2D (noisepp::Pipeline2D *pipe, noisepp::PipelineElement2D *element, double x, double y, int n, double xDelta, double *buffer, bool progress) :
			mPipe(pipe), mElement(element), x(x), y(y), n(n), xDelta(xDelta), buffer(buffer), mProgress(progress)
		{}
		void execute (noisepp::Cache *cache)
		{
			for (int i=0;i<n;++i)
			{
				// cleans the cache
				mPipe->cleanCache (cache);
				// calculates the value
				buffer[i] = mElement->getValue(x, y, cache);
				// move on
				x += xDelta;
			}
		}
		void finish ()
		{
			if (mProgress)
			{
				cout << ".";
				cout.flush ();
			}
		}
};

/// 3D pipeline job generating a line
class LineJob3D : public noisepp::PipelineJob
{
	private:
		noisepp::Pipeline3D *mPipe;
		noisepp::PipelineElement3D *mElement;
		double x, y, z;
		int n;
		double xDelta;
		double *buffer;
		bool mProgress;

	public:
		LineJob3D (noisepp::Pipeline3D *pipe, noisepp::PipelineElement3D *element, double x, double y, double z, int n, double xDelta, double *buffer, bool progress) :
			mPipe(pipe), mElement(element), x(x), y(y), z(z), n(n), xDelta(xDelta), buffer(buffer), mProgress(progress)
		{}
		void execute (noisepp::Cache *cache)
		{
			for (int i=0;i<n;++i)
			{
				// cleans the cache
				mPipe->cleanCache (cache);
				// calculates the value
				buffer[i] = mElement->getValue(x, y, z, cache);
				// move on
				x += xDelta;
			}
		}
		void finish ()
		{
			if (mProgress)
			{
				cout << ".";
				cout.flush ();
			}
		}
};

/// calculates the maximum difference between the two arrays specified
double calcMaxDifference (double *left, double *right, int n)
{
	double dif = 0;
	for (int i=0;i<n;++i)
	{
		const double d = fabs(left[i]-right[i]);
		if (d > dif)
			dif = d;
	}
	return dif;
}

int main ()
{
	double x, y;
	double xdelta = 0.1;
	double ydelta = 0.1;

	// generate output arrays
	double *libnoise_results = new double[1000000];
	double *noisepp_results = new double[1000000];

	// the libnoise module for comparision
	noise::module::Perlin libnoisePerlin;

	// the Noise++ modules
	noisepp::PerlinModule2D noiseppPerlin2D;
	noisepp::PerlinModule3D noiseppPerlin3D;

	// create default pipelines and fill them with our modules
	noisepp::Pipeline2D pipeline2D;
	noisepp::ElementID noiseID = noiseppPerlin2D.addToPipe (pipeline2D);
	noisepp::Pipeline3D pipeline3D;
	noisepp::ElementID noiseID3D = noiseppPerlin3D.addToPipe (pipeline3D);

	// create threaded pipelines and fill them with modules
#if NOISEPP_ENABLE_THREADS
	noisepp::ThreadedPipeline2D pipeline2D_mt(THREAD_COUNT);
	noisepp::ElementID noiseID2D_mt = noiseppPerlin2D.addToPipe (pipeline2D_mt);
	noisepp::ThreadedPipeline3D pipeline3D_mt(THREAD_COUNT);
	noisepp::ElementID noiseID3D_mt = noiseppPerlin3D.addToPipe (pipeline3D_mt);
#endif

	// creates the cache
	noisepp::Cache *cache = pipeline2D.createCache();
	noisepp::Cache *cache3D = pipeline3D.createCache();

	// calculate a perlin noise value
	cout << "libnoise: " << libnoisePerlin.GetValue (1.2, 0.2, 0) << std::endl;
	cout << "Noise++ (2D): " << pipeline2D.getElement(noiseID)->getValue (1.2, 0.2, cache) << std::endl;
	cout << "Noise++ (3D): " << pipeline3D.getElement(noiseID3D)->getValue (1.2, 0.2, 0, cache3D) << std::endl;

	// calculates some square roots to bring the CPU(s) to maximum frequency - i know, not really professional solution
	// (you can skip this step but at least on my PC I get more accurate benchmark results with it)
	cout << "prepare for generating " << MILLION_POINTS << " million points ... ";
	cout.flush ();

	double *test = new double[1000000];
	for (int n=0;n<100;++n)
	{
		for (int i=0;i<1000000;++i)
		{
			test[i] = sqrt((double)rand()/RAND_MAX);
		}
	}
	delete[] test;

	cout << "go!" << endl;

	// generate the points with libnoise
	double seconds;
	double mpix = MILLION_POINTS;
	cout << "generating using libnoise ";
	cout.flush ();
	double lnoiseStart = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		cout << ".";
		cout.flush ();
		double *res = libnoise_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			x = 0;
			for (int xx=0;xx<1000;++xx)
			{
				*res++ = libnoisePerlin.GetValue (x, y, 0);
				x += xdelta;
			}
			y += ydelta;
		}
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-lnoiseStart;
	double libnoiseTime = seconds * 100.0;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second" << endl;

	// get the pointers to the pipeline elements
	noisepp::PipelineElement2D *element = pipeline2D.getElement(noiseID);
	noisepp::PipelineElement3D *element3D = pipeline3D.getElement(noiseID3D);
#if NOISEPP_ENABLE_THREADS
	noisepp::PipelineElement2D *element2D_mt = pipeline2D.getElement(noiseID2D_mt);
	noisepp::PipelineElement3D *element3D_mt = pipeline3D.getElement(noiseID3D_mt);
#endif

	// generate using the default 3D pipeline
	cout << "generating using Pipeline3D ";
	cout.flush ();
	double noiseppStart3D = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		cout << ".";
		cout.flush ();
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			x = 0;
			for (int xx=0;xx<1000;++xx)
			{
				// clean cache
				pipeline3D.cleanCache(cache3D);
				// calculate value
				*res++ = element3D->getValue (x, y, 0, cache3D);
				// move on
				x += xdelta;
			}
			y += ydelta;
		}
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart3D;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;

	// generate using the default 2D pipeline
	cout << "generating using Pipeline2D ";
	cout.flush ();
	double noiseppStart2D = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		cout << ".";
		cout.flush ();
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			x = 0;
			for (int xx=0;xx<1000;++xx)
			{
				pipeline2D.cleanCache(cache);
				*res++ = element->getValue (x, y, cache);
				x += xdelta;
			}
			y += ydelta;
		}
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart2D;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	// check for difference
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;

#if NOISEPP_ENABLE_THREADS
	// generate using the threaded 3D pipeline
	cout << "generating using ThreadedPipeline3D ";
	cout.flush ();
	double noiseppStart3D_mt = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			pipeline3D_mt.addJob (new LineJob3D(&pipeline3D_mt, element3D_mt, 0, y, 0, 1000, xdelta, res, (yy==0)));
			res += 1000;
			y += ydelta;
		}
		pipeline3D_mt.executeJobs ();
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart3D_mt;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	// check for difference
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;

	// generate using the threaded 2D pipeline
	cout << "generating using ThreadedPipeline2D ";
	cout.flush ();
	double noiseppStart2D_mt = Timer_getSeconds();
	for (int i=0;i<MILLION_POINTS;++i)
	{
		double *res = noisepp_results;
		y = 0;
		for (int yy=0;yy<1000;++yy)
		{
			pipeline2D_mt.addJob (new LineJob2D(&pipeline2D_mt, element2D_mt, 0, y, 1000, xdelta, res, (yy==0)));
			res += 1000;
			y += ydelta;
		}
		pipeline2D_mt.executeJobs ();
	}
	cout << " done" << endl;
	// calculate stats
	seconds = Timer_getSeconds()-noiseppStart2D_mt;
	cout << "time: " << seconds << " sec" << endl;
	cout << mpix / seconds << " million pixels per second (" << libnoiseTime / seconds - 100.0 << "% faster than libnoise)" << endl;
	// check for difference
	if (calcMaxDifference(noisepp_results, libnoise_results, 1000*1000) > 1.0e-07)
		cout << "WARNING: different result!" << endl;
#endif

	delete[] libnoise_results;
	delete[] noisepp_results;

	// frees the cache
	pipeline2D.freeCache(cache);
	pipeline3D.freeCache(cache3D);

#ifdef WIN32
	cout << "Press any key ..." << endl;
	cin.get();
#endif

	return 0;
}
<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/compiler/xla/service/gpu/gpu_layout_assignment.h"

#include <memory>

#include "tensorflow/compiler/xla/layout_util.h"
#include "tensorflow/compiler/xla/service/gpu/gpu_options.h"
#include "tensorflow/compiler/xla/service/gpu/ir_emission_utils.h"
#include "tensorflow/compiler/xla/service/gpu/stream_executor_util.h"
#include "tensorflow/compiler/xla/service/hlo_computation.h"
#include "tensorflow/compiler/xla/service/hlo_instruction.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/compiler/xla/window_util.h"
#include "tensorflow/compiler/xla/xla_data.pb.h"
#include "tensorflow/core/lib/core/errors.h"

namespace xla {
namespace gpu {

using stream_executor::dnn::DataLayout;
using stream_executor::dnn::FilterLayout;

static bool IsVoltaOrLater(const se::StreamExecutor& stream_executor) {
  int major, minor;
  CHECK(stream_executor.GetDeviceDescription().cuda_compute_capability(&major,
                                                                       &minor));
  return major >= 7;
}

// Returns (input, filter, output) layouts.
static std::tuple<DataLayout, FilterLayout, DataLayout>
HeuristicLayoutAssignment(const HloInstruction* instr,
                          stream_executor::StreamExecutor* stream_executor) {
  // DataLayout and FilterLayout uses weird enum names. Translations:
  //   N <=> Batch or Output
  //   C <=> Depth or Input
  //   H <=> Y
  //   W <=> X
  //
  // Therefore kOutputInputYX and kBatchDepthYX mean NCHW.

  // As of today, our empirical evidence is that cudnn 7.0 is faster on V100 x
  // fp16 with the mostly-NHWC layout. The heuristic may change as cudnn version
  // changes, as well as the hardware updates.
  if (!(instr->operand(0)->shape().element_type() == xla::PrimitiveType::F16 &&
        IsVoltaOrLater(*stream_executor))) {
    return std::make_tuple(DataLayout::kBatchDepthYX,
                           FilterLayout::kOutputInputYX,
                           DataLayout::kBatchDepthYX);
  }
  VLOG(2) << "Using heuristic to figure out layouts for " << instr->ToString();
  // For BackwardInput that has stride, full NHWC layouts run significantly
  // slower than (NHWC, NCHW, NCHW) or (NHWC, NCHW, NHWC).
  //
  // TODO(timshen): more closely compare (NHWC, NCHW, NCHW) and (NHWC, NCHW,
  // NHWC).
  if (instr->custom_call_target() == kCudnnConvBackwardInputCallTarget &&
      window_util::HasStride(instr->window())) {
    return std::make_tuple(DataLayout::kBatchYXDepth,
                           FilterLayout::kOutputInputYX,
                           DataLayout::kBatchDepthYX);
  }
  return std::make_tuple(DataLayout::kBatchYXDepth,
                         FilterLayout::kOutputYXInput,
                         DataLayout::kBatchYXDepth);
}

// Adds layout constraints on the cudnn custom-call instruction. The layout
// constraints are represented in terms of minor_to_major fields of both
// operands and the output shape. Depending on the underlying algorithm, one of
// { NCHW, NHWC } ^ 3 = 8 different layout combinations may be chosen.
Status GpuLayoutAssignment::AddBackendConstraintsToDnnConvCustomCall(
    HloInstruction* instr, LayoutConstraints* constraints) {
  CHECK(IsCustomCallToDnnConvolution(*instr)) << instr->ToString();
  Shape input_shape;
  Shape filter_shape;
  Shape output_shape;
  const auto& target = instr->custom_call_target();
  if (target == kCudnnConvForwardCallTarget) {
    input_shape = instr->operand(0)->shape();
    filter_shape = instr->operand(1)->shape();
    output_shape = instr->shape().tuple_shapes(0);
  } else if (target == kCudnnConvBackwardInputCallTarget) {
    input_shape = instr->shape().tuple_shapes(0);
    filter_shape = instr->operand(1)->shape();
    output_shape = instr->operand(0)->shape();
  } else if (target == kCudnnConvBackwardFilterCallTarget) {
    input_shape = instr->operand(0)->shape();
    filter_shape = instr->shape().tuple_shapes(0);
    output_shape = instr->operand(1)->shape();
  } else {
    LOG(FATAL) << "Unexpected custom call target: "
               << instr->custom_call_target();
  }

  {
    DataLayout input;
    FilterLayout filter;
    DataLayout output;
    if (ConvUseLayoutHeuristic(instr->GetModule()->config())) {
      std::tie(input, filter, output) =
          HeuristicLayoutAssignment(instr, stream_executor_);
    } else {
      input = DataLayout::kBatchDepthYX;
      filter = FilterLayout::kOutputInputYX;
      output = DataLayout::kBatchDepthYX;
    }

    TF_ASSIGN_OR_RETURN(
        std::tie(*input_shape.mutable_layout(), *filter_shape.mutable_layout(),
                 *output_shape.mutable_layout()),
        StreamExecutorConvLayoutsToXlaLayouts(
            instr->convolution_dimension_numbers(), input, filter, output));
  }

  // The custom call returns a tuple of (actual_result, scratch_buffer);
  // call_result_buf is the logical buffer for actual_result, the thing that
  // contains the result of the conv call.
  TF_ASSIGN_OR_RETURN(const LogicalBuffer* call_result_buf,
                      constraints->points_to_analysis().GetBufferDefinedAt(
                          instr, /*index=*/{0}));

  // Set layouts of the instructions' shapes.
  if (target == kCudnnConvForwardCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(input_shape, instr, 0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(filter_shape, instr, 1));
    TF_RETURN_IF_ERROR(
        constraints->SetBufferLayout(output_shape.layout(), *call_result_buf));
  } else if (target == kCudnnConvBackwardInputCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(output_shape, instr, 0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(filter_shape, instr, 1));
    TF_RETURN_IF_ERROR(
        constraints->SetBufferLayout(input_shape.layout(), *call_result_buf));
  } else if (target == kCudnnConvBackwardFilterCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(input_shape, instr, 0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(output_shape, instr, 1));
    TF_RETURN_IF_ERROR(
        constraints->SetBufferLayout(filter_shape.layout(), *call_result_buf));
  } else {
    LOG(FATAL) << "Unexpected custom call target: "
               << instr->custom_call_target();
  }
  return Status::OK();
}

Status GpuLayoutAssignment::AddBackendConstraints(
    LayoutConstraints* constraints) {
  // Add convolution constraints in reverse postorder that the earliest
  // convolution layout propagates first. This reduces the likelihood of fusion
  // nodes with copies.
  auto post_order = constraints->computation()->MakeInstructionPostOrder();
  for (auto iterator = post_order.rbegin(); iterator != post_order.rend();
       ++iterator) {
    HloInstruction* instruction = *iterator;
    if (IsCustomCallToDnnConvolution(*instruction)) {
      TF_RETURN_IF_ERROR(
          AddBackendConstraintsToDnnConvCustomCall(instruction, constraints));
    }
  }
  return Status::OK();
}

bool GpuLayoutAssignment::CustomCallRequiresMajorFirstLayout(
    const HloInstruction* instruction) {
  // - Inputs to cudnn batchnorm custom calls don't need the major-first layout
  //   (i.e. {n, n-1, ...0}) -- we can handle any layout.
  // - Inputs to cudnn convolution require custom layouts handled in
  //   AddBackendConstraints.
  return !IsCustomCallToDnnBatchNorm(*instruction) &&
         !IsCustomCallToDnnConvolution(*instruction);
}

Status GpuLayoutAssignment::PropagateOperandConstraint(
    const OperandLayoutConstraint& layout_constraint,
    LayoutConstraints* constraints) {
  const HloInstruction* instruction = layout_constraint.instruction();

  // cudnn batchnorm forward inference's result must have the same layout as its
  // operand 0.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardInferenceCallTarget &&
      layout_constraint.operand_no() == 0) {
    TF_RETURN_IF_ERROR(constraints->SetInstructionLayout(
        layout_constraint.shape_layout().shape(), instruction));
  }

  // cudnn batchnorm forward training returns a tuple {output, mean,
  // inverse-stddev}.  mean and inverse-stddev are rank 1 and so have only one
  // possible layout, but output is not (necessarily) rank 1, and, like in
  // batchnorm forward inference, must have the same layout as operand 0.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardTrainingCallTarget &&
      layout_constraint.operand_no() == 0) {
    TF_ASSIGN_OR_RETURN(const LogicalBuffer* out_buf,
                        constraints->points_to_analysis().GetBufferDefinedAt(
                            instruction, /*index=*/{0}));
    TF_RETURN_IF_ERROR(constraints->SetBufferLayout(
        layout_constraint.shape_layout().layout(), *out_buf));
  }

  // Like forward training, cudnn batchnorm backward returns a tuple {output,
  // mean, inverse-stddev}, and its operand 0 and 'output' must have the same
  // layout.  In addition, its operand 0 and operand 4 -- the 'operand' and
  // 'grad_output' parameters -- must have the same layout.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() == kCudnnBatchNormBackwardCallTarget &&
      (layout_constraint.operand_no() == 0 ||
       layout_constraint.operand_no() == 4)) {
    TF_ASSIGN_OR_RETURN(const LogicalBuffer* out_buf,
                        constraints->points_to_analysis().GetBufferDefinedAt(
                            instruction, /*index=*/{0}));
    TF_RETURN_IF_ERROR(constraints->SetBufferLayout(
        layout_constraint.shape_layout().layout(), *out_buf));

    int64 operand_to_set = layout_constraint.operand_no() == 0 ? 4 : 0;
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        layout_constraint.shape_layout().shape(), instruction, operand_to_set));
  }

  return LayoutAssignment::PropagateOperandConstraint(layout_constraint,
                                                      constraints);
}

Status GpuLayoutAssignment::PropagateBufferConstraint(
    const BufferLayoutConstraint& buffer_constraint,
    LayoutConstraints* constraints) {
  const LogicalBuffer& buf = buffer_constraint.buffer();
  const HloInstruction* instruction = buf.instruction();

  Shape shape_with_layout = buf.shape();
  *shape_with_layout.mutable_layout() = buffer_constraint.layout();

  // Propagate output constraints to the operands of cudnn batchnorm ops.  This
  // is the same as PropagateOperandConstraint, just in the other direction.  We
  // need to both to fulfill our contract to LayoutAssignment.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardInferenceCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/0));
  }

  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardTrainingCallTarget &&
      buf.index() == ShapeIndex({0})) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/0));
  }
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() == kCudnnBatchNormBackwardCallTarget &&
      buf.index() == ShapeIndex({0})) {
    // batchnorm backward has two operands, "operand" and "grad_output" whose
    // layouts must both match that of the result at tuple-index 0.
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/4));
  }

  return LayoutAssignment::PropagateBufferConstraint(buffer_constraint,
                                                     constraints);
}

}  // namespace gpu
}  // namespace xla
<commit_msg>[XLA:GPU] Clarify HeuristicLayoutAssignment function.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/compiler/xla/service/gpu/gpu_layout_assignment.h"

#include <memory>

#include "tensorflow/compiler/xla/layout_util.h"
#include "tensorflow/compiler/xla/service/gpu/gpu_options.h"
#include "tensorflow/compiler/xla/service/gpu/ir_emission_utils.h"
#include "tensorflow/compiler/xla/service/gpu/stream_executor_util.h"
#include "tensorflow/compiler/xla/service/hlo_computation.h"
#include "tensorflow/compiler/xla/service/hlo_instruction.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/compiler/xla/window_util.h"
#include "tensorflow/compiler/xla/xla_data.pb.h"
#include "tensorflow/core/lib/core/errors.h"

namespace xla {
namespace gpu {

using stream_executor::dnn::DataLayout;
using stream_executor::dnn::FilterLayout;

static bool IsVoltaOrLater(const se::StreamExecutor& stream_executor) {
  int major, minor;
  CHECK(stream_executor.GetDeviceDescription().cuda_compute_capability(&major,
                                                                       &minor));
  return major >= 7;
}

// Returns (input, filter, output) layouts.
static std::tuple<DataLayout, FilterLayout, DataLayout>
HeuristicLayoutAssignment(const HloInstruction* instr,
                          stream_executor::StreamExecutor* stream_executor) {
  // DataLayout and FilterLayout uses weird enum names. Translations:
  //   N <=> Batch or Output
  //   C <=> Depth or Input
  //   H <=> Y
  //   W <=> X
  //
  // Therefore kOutputInputYX and kBatchDepthYX mean NCHW.
  //
  // If you have trouble keeping these straight, consider that all that matters
  // is the location of the channel dim: Is it major (NCHW), or minor (NHWC)?

  constexpr auto kAllNCHW =
      std::make_tuple(DataLayout::kBatchDepthYX, FilterLayout::kOutputInputYX,
                      DataLayout::kBatchDepthYX);
  constexpr auto kAllNHWC =
      std::make_tuple(DataLayout::kBatchYXDepth, FilterLayout::kOutputYXInput,
                      DataLayout::kBatchYXDepth);

  // If we're not Volta or not fp16, the decision is easy: Use NCHW.
  if (!(instr->operand(0)->shape().element_type() == xla::PrimitiveType::F16 &&
        IsVoltaOrLater(*stream_executor))) {
    return kAllNCHW;
  }

  VLOG(2) << "Using heuristic to figure out layouts for " << instr->ToString();

  // Empirically we've found with Volta and cudnn 7 that backward-input convs
  // with stride are significantly faster with input in NHWC and filter/output
  // in NCHW.
  if (instr->custom_call_target() == kCudnnConvBackwardInputCallTarget &&
      window_util::HasStride(instr->window())) {
    return std::make_tuple(DataLayout::kBatchYXDepth,     // NHWC
                           FilterLayout::kOutputInputYX,  // NCHW
                           DataLayout::kBatchDepthYX      // NCHW
    );
  }

  // For other Volta f16 convolutions, use NHWC.
  return kAllNHWC;
}

// Adds layout constraints on the cudnn custom-call instruction. The layout
// constraints are represented in terms of minor_to_major fields of both
// operands and the output shape. Depending on the underlying algorithm, one of
// { NCHW, NHWC } ^ 3 = 8 different layout combinations may be chosen.
Status GpuLayoutAssignment::AddBackendConstraintsToDnnConvCustomCall(
    HloInstruction* instr, LayoutConstraints* constraints) {
  CHECK(IsCustomCallToDnnConvolution(*instr)) << instr->ToString();
  Shape input_shape;
  Shape filter_shape;
  Shape output_shape;
  const auto& target = instr->custom_call_target();
  if (target == kCudnnConvForwardCallTarget) {
    input_shape = instr->operand(0)->shape();
    filter_shape = instr->operand(1)->shape();
    output_shape = instr->shape().tuple_shapes(0);
  } else if (target == kCudnnConvBackwardInputCallTarget) {
    input_shape = instr->shape().tuple_shapes(0);
    filter_shape = instr->operand(1)->shape();
    output_shape = instr->operand(0)->shape();
  } else if (target == kCudnnConvBackwardFilterCallTarget) {
    input_shape = instr->operand(0)->shape();
    filter_shape = instr->shape().tuple_shapes(0);
    output_shape = instr->operand(1)->shape();
  } else {
    LOG(FATAL) << "Unexpected custom call target: "
               << instr->custom_call_target();
  }

  {
    DataLayout input;
    FilterLayout filter;
    DataLayout output;
    if (ConvUseLayoutHeuristic(instr->GetModule()->config())) {
      std::tie(input, filter, output) =
          HeuristicLayoutAssignment(instr, stream_executor_);
    } else {
      input = DataLayout::kBatchDepthYX;
      filter = FilterLayout::kOutputInputYX;
      output = DataLayout::kBatchDepthYX;
    }

    TF_ASSIGN_OR_RETURN(
        std::tie(*input_shape.mutable_layout(), *filter_shape.mutable_layout(),
                 *output_shape.mutable_layout()),
        StreamExecutorConvLayoutsToXlaLayouts(
            instr->convolution_dimension_numbers(), input, filter, output));
  }

  // The custom call returns a tuple of (actual_result, scratch_buffer);
  // call_result_buf is the logical buffer for actual_result, the thing that
  // contains the result of the conv call.
  TF_ASSIGN_OR_RETURN(const LogicalBuffer* call_result_buf,
                      constraints->points_to_analysis().GetBufferDefinedAt(
                          instr, /*index=*/{0}));

  // Set layouts of the instructions' shapes.
  if (target == kCudnnConvForwardCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(input_shape, instr, 0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(filter_shape, instr, 1));
    TF_RETURN_IF_ERROR(
        constraints->SetBufferLayout(output_shape.layout(), *call_result_buf));
  } else if (target == kCudnnConvBackwardInputCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(output_shape, instr, 0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(filter_shape, instr, 1));
    TF_RETURN_IF_ERROR(
        constraints->SetBufferLayout(input_shape.layout(), *call_result_buf));
  } else if (target == kCudnnConvBackwardFilterCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(input_shape, instr, 0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(output_shape, instr, 1));
    TF_RETURN_IF_ERROR(
        constraints->SetBufferLayout(filter_shape.layout(), *call_result_buf));
  } else {
    LOG(FATAL) << "Unexpected custom call target: "
               << instr->custom_call_target();
  }
  return Status::OK();
}

Status GpuLayoutAssignment::AddBackendConstraints(
    LayoutConstraints* constraints) {
  // Add convolution constraints in reverse postorder that the earliest
  // convolution layout propagates first. This reduces the likelihood of fusion
  // nodes with copies.
  auto post_order = constraints->computation()->MakeInstructionPostOrder();
  for (auto iterator = post_order.rbegin(); iterator != post_order.rend();
       ++iterator) {
    HloInstruction* instruction = *iterator;
    if (IsCustomCallToDnnConvolution(*instruction)) {
      TF_RETURN_IF_ERROR(
          AddBackendConstraintsToDnnConvCustomCall(instruction, constraints));
    }
  }
  return Status::OK();
}

bool GpuLayoutAssignment::CustomCallRequiresMajorFirstLayout(
    const HloInstruction* instruction) {
  // - Inputs to cudnn batchnorm custom calls don't need the major-first layout
  //   (i.e. {n, n-1, ...0}) -- we can handle any layout.
  // - Inputs to cudnn convolution require custom layouts handled in
  //   AddBackendConstraints.
  return !IsCustomCallToDnnBatchNorm(*instruction) &&
         !IsCustomCallToDnnConvolution(*instruction);
}

Status GpuLayoutAssignment::PropagateOperandConstraint(
    const OperandLayoutConstraint& layout_constraint,
    LayoutConstraints* constraints) {
  const HloInstruction* instruction = layout_constraint.instruction();

  // cudnn batchnorm forward inference's result must have the same layout as its
  // operand 0.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardInferenceCallTarget &&
      layout_constraint.operand_no() == 0) {
    TF_RETURN_IF_ERROR(constraints->SetInstructionLayout(
        layout_constraint.shape_layout().shape(), instruction));
  }

  // cudnn batchnorm forward training returns a tuple {output, mean,
  // inverse-stddev}.  mean and inverse-stddev are rank 1 and so have only one
  // possible layout, but output is not (necessarily) rank 1, and, like in
  // batchnorm forward inference, must have the same layout as operand 0.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardTrainingCallTarget &&
      layout_constraint.operand_no() == 0) {
    TF_ASSIGN_OR_RETURN(const LogicalBuffer* out_buf,
                        constraints->points_to_analysis().GetBufferDefinedAt(
                            instruction, /*index=*/{0}));
    TF_RETURN_IF_ERROR(constraints->SetBufferLayout(
        layout_constraint.shape_layout().layout(), *out_buf));
  }

  // Like forward training, cudnn batchnorm backward returns a tuple {output,
  // mean, inverse-stddev}, and its operand 0 and 'output' must have the same
  // layout.  In addition, its operand 0 and operand 4 -- the 'operand' and
  // 'grad_output' parameters -- must have the same layout.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() == kCudnnBatchNormBackwardCallTarget &&
      (layout_constraint.operand_no() == 0 ||
       layout_constraint.operand_no() == 4)) {
    TF_ASSIGN_OR_RETURN(const LogicalBuffer* out_buf,
                        constraints->points_to_analysis().GetBufferDefinedAt(
                            instruction, /*index=*/{0}));
    TF_RETURN_IF_ERROR(constraints->SetBufferLayout(
        layout_constraint.shape_layout().layout(), *out_buf));

    int64 operand_to_set = layout_constraint.operand_no() == 0 ? 4 : 0;
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        layout_constraint.shape_layout().shape(), instruction, operand_to_set));
  }

  return LayoutAssignment::PropagateOperandConstraint(layout_constraint,
                                                      constraints);
}

Status GpuLayoutAssignment::PropagateBufferConstraint(
    const BufferLayoutConstraint& buffer_constraint,
    LayoutConstraints* constraints) {
  const LogicalBuffer& buf = buffer_constraint.buffer();
  const HloInstruction* instruction = buf.instruction();

  Shape shape_with_layout = buf.shape();
  *shape_with_layout.mutable_layout() = buffer_constraint.layout();

  // Propagate output constraints to the operands of cudnn batchnorm ops.  This
  // is the same as PropagateOperandConstraint, just in the other direction.  We
  // need to both to fulfill our contract to LayoutAssignment.
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardInferenceCallTarget) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/0));
  }

  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() ==
          kCudnnBatchNormForwardTrainingCallTarget &&
      buf.index() == ShapeIndex({0})) {
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/0));
  }
  if (instruction->opcode() == HloOpcode::kCustomCall &&
      instruction->custom_call_target() == kCudnnBatchNormBackwardCallTarget &&
      buf.index() == ShapeIndex({0})) {
    // batchnorm backward has two operands, "operand" and "grad_output" whose
    // layouts must both match that of the result at tuple-index 0.
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/0));
    TF_RETURN_IF_ERROR(constraints->SetOperandLayout(
        shape_with_layout, instruction, /*operand_no=*/4));
  }

  return LayoutAssignment::PropagateBufferConstraint(buffer_constraint,
                                                     constraints);
}

}  // namespace gpu
}  // namespace xla
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017, Yeolar
 */

#include "rddoc/net/AsyncClient.h"
#include "rddoc/net/EventPool.h"

namespace rdd {

AsyncClient::AsyncClient(const ClientOption& option)
  : peer_(option.peer), timeout_opt_(option.timeout) {
  RDDLOG(DEBUG) << "AsyncClient: " << peer_.str()
    << ", timeout=" << timeout_opt_;
}

void AsyncClient::close() {
  freeConnection();
}

bool AsyncClient::connect() {
  if (!initConnection()) {
    return false;
  }
  RDD_EVLOG(V2, event()) << "connect";
  if (!callback_mode_) {
    Task* task = ThreadTask::getCurrentThreadTask();
    event_->setTask(task);
    task->addBlockedCallback(
        std::bind(&Actor::addEvent, Singleton<Actor>::get(), event()));
  }
  return true;
}

void AsyncClient::callback() {
  RDDLOG(DEBUG) << "finish peer[" << peer_.str() << "]";
}

bool AsyncClient::initConnection() {
  if (keepalive_) {
    auto pool = Singleton<EventPoolManager>::get()->getPool(peer_.port);
    auto event = pool->get(peer_);
    if (event && event->socket()->connected()) {
      event->reset();
      event->setType(Event::TOWRITE);
      event_ = event;
      RDDLOG(DEBUG) << "connect peer[" << peer_.str() << "] (keep-alive)";
      return true;
    }
  }
  auto socket = std::make_shared<Socket>(0);
  if (*socket &&
      socket->setReuseAddr() &&
      (!keepalive_ || socket->setKeepAlive()) &&
      // socket->setLinger(0) &&
      socket->setTCPNoDelay() &&
      socket->setNonBlocking() &&
      socket->connect(peer_)) {
    auto event = std::make_shared<Event>(channel_, socket);
    event->setType(Event::CONNECT);
    event_ = event;
    RDDLOG(DEBUG) << "connect peer[" << peer_.str() << "]";
    return true;
  }
  return false;
}

void AsyncClient::freeConnection() {
  if (keepalive_) {
    auto pool = Singleton<EventPoolManager>::get()->getPool(peer_.port);
    pool->giveBack(event_);
  }
  event_ = nullptr;
}

}

<commit_msg>fix keep-alive connection bug<commit_after>/*
 * Copyright (C) 2017, Yeolar
 */

#include "rddoc/net/AsyncClient.h"
#include "rddoc/net/EventPool.h"

namespace rdd {

AsyncClient::AsyncClient(const ClientOption& option)
  : peer_(option.peer), timeout_opt_(option.timeout) {
  RDDLOG(DEBUG) << "AsyncClient: " << peer_.str()
    << ", timeout=" << timeout_opt_;
}

void AsyncClient::close() {
  freeConnection();
}

bool AsyncClient::connect() {
  if (!initConnection()) {
    return false;
  }
  RDD_EVLOG(V2, event()) << "connect";
  if (!callback_mode_) {
    Task* task = ThreadTask::getCurrentThreadTask();
    event_->setTask(task);
    task->addBlockedCallback(
        std::bind(&Actor::addEvent, Singleton<Actor>::get(), event()));
  }
  return true;
}

void AsyncClient::callback() {
  RDDLOG(DEBUG) << "finish peer[" << peer_.str() << "]";
}

bool AsyncClient::initConnection() {
  if (keepalive_) {
    auto pool = Singleton<EventPoolManager>::get()->getPool(peer_.port);
    auto event = pool->get(peer_);
    if (event && event->socket()->connected()) {
      event->reset();
      event->setType(Event::TOWRITE);
      event_ = event;
      RDDLOG(DEBUG) << "connect peer[" << peer_.str() << "] (keep-alive)";
      return true;
    }
  }
  auto socket = std::make_shared<Socket>(0);
  if (*socket &&
      socket->setReuseAddr() &&
      (!keepalive_ || socket->setKeepAlive()) &&
      // socket->setLinger(0) &&
      socket->setTCPNoDelay() &&
      socket->setNonBlocking() &&
      socket->connect(peer_)) {
    auto event = std::make_shared<Event>(channel_, socket);
    event->setType(Event::CONNECT);
    event_ = event;
    RDDLOG(DEBUG) << "connect peer[" << peer_.str() << "]";
    return true;
  }
  return false;
}

void AsyncClient::freeConnection() {
  if (keepalive_ && event_->type() != Event::FAIL) {
    auto pool = Singleton<EventPoolManager>::get()->getPool(peer_.port);
    pool->giveBack(event_);
  }
  event_ = nullptr;
}

}

<|endoftext|>