<commit_before>// ======================================================================== //
// Copyright 2009-2013 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.                                           //
// ======================================================================== //

#include "tutorial/tutorial.h"
#include "tutorial/obj_loader.h"
#include "tutorial/hair_loader.h"
#include "sys/taskscheduler.h"
#include "image/image.h"

namespace embree
{
  /* name of the tutorial */
  const char* tutorialName = "tutorial07";

  /* configuration */
  static std::string g_rtcore = "";

  /* output settings */
  static size_t g_width = 512;
  static size_t g_height = 512;
  static bool g_fullscreen = false;
  static size_t g_numThreads = 0;

  static int tessellation_segments = -1;
  static int tessellation_strips   = -1;

  /* scene */
  OBJScene g_obj_scene;
  static FileName objFilename = "";
  static FileName hairFilename = "";
  static FileName outFilename = "";
  Vec3fa offset = 0.0f;

  static void parseCommandLine(Ref<ParseStream> cin, const FileName& path)
  {
    while (true)
    {
      std::string tag = cin->getString();
      if (tag == "") return;

      /* parse command line parameters from a file */
      if (tag == "-c") {
        FileName file = path + cin->getFileName();
        parseCommandLine(new ParseStream(new LineCommentFilter(file, "#")), file.path());
      }

      /* load OBJ model */
      else if (tag == "-i") {
        objFilename = path + cin->getFileName();
      }

      /* load hair model */
      else if (tag == "--hair") {
        hairFilename = path + cin->getFileName();
      }

      /* scene offset */
      else if (tag == "--offset") {
        offset = cin->getVec3fa();
      }

      /* tessellation flags */
      else if (tag == "--tessellate") {
        tessellation_segments = cin->getInt();
        tessellation_strips   = cin->getInt();
      }

      /* output filename */
      else if (tag == "-o") {
        outFilename = cin->getFileName();
      }

      /* parse camera parameters */
      else if (tag == "-vp") g_camera.from = cin->getVec3fa();
      else if (tag == "-vi") g_camera.to = cin->getVec3fa();
      else if (tag == "-vd") g_camera.to = g_camera.from + cin->getVec3fa();
      else if (tag == "-vu") g_camera.up = cin->getVec3fa();
      else if (tag == "-fov") g_camera.fov = cin->getFloat();

      /* frame buffer size */
      else if (tag == "-size") {
        g_width = cin->getInt();
        g_height = cin->getInt();
      }

      /* full screen mode */
      else if (tag == "-fullscreen")
        g_fullscreen = true;

      /* rtcore configuration */
      else if (tag == "-rtcore")
        g_rtcore = cin->getString();

      /* number of threads to use */
      else if (tag == "-threads")
        g_numThreads = cin->getInt();

      /* skip unknown command line parameter */
      else {
        std::cerr << "unknown command line parameter: " << tag << " ";
        while (cin->peek() != "" && cin->peek()[0] != '-') std::cerr << cin->getString() << " ";
        std::cerr << std::endl;
      }
    }
  }

  void renderToFile(const FileName& fileName)
  {
    resize(g_width,g_height);
    AffineSpace3fa pixel2world = g_camera.pixel2world(g_width,g_height);

    /* render image using ISPC */
    double t0 = getSeconds();
    render(0.0f,
           pixel2world.l.vx,
           pixel2world.l.vy,
           pixel2world.l.vz,
           pixel2world.p);
    double dt0 = getSeconds()-t0;

    void* ptr = map();
    Ref<Image> image = new Image4c(g_width, g_height, (Col4c*)ptr);
    storeImage(image, fileName);
    unmap();
  }

  /* main function in embree namespace */
  int main(int argc, char** argv)
  {
    /* create stream for parsing */
    Ref<ParseStream> stream = new ParseStream(new CommandLineStream(argc, argv));

    /* parse command line */
    parseCommandLine(stream, FileName());
    if (g_numThreads)
      g_rtcore += ",threads=" + std::stringOf(g_numThreads);

    /* initialize task scheduler */
#if !defined(__EXPORT_ALL_SYMBOLS__)
    TaskScheduler::create(g_numThreads);
#endif

    /* initialize ray tracing core */
    init(g_rtcore.c_str());

    /* load scene */
    if (objFilename.str() != "")
      loadOBJ(objFilename,g_obj_scene,offset);

    /* load hair */
    if (hairFilename.str() != "")
      loadHair(hairFilename,g_obj_scene,offset);

    /* send model */
    if (objFilename.str() != "" && hairFilename.str() != "")
      set_scene(&g_obj_scene);

    /* render to disk */
    if (outFilename.str() != "") {
      renderToFile(outFilename);
      return 0;
    }

    /* initialize GLUT */
    initGlut(tutorialName,g_width,g_height,g_fullscreen,true);

    return 0;
  }
}

int main(int argc, char** argv)
{
  try {
    return embree::main(argc, argv);
  }
  catch (const std::exception& e) {
    std::cout << "Error: " << e.what() << std::endl;
    return 1;
  }
  catch (...) {
    std::cout << "Error: unknown exception caught." << std::endl;
    return 1;
  }
}
<commit_msg>started writing tessellation code for hair<commit_after>// ======================================================================== //
// Copyright 2009-2013 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.                                           //
// ======================================================================== //

#include "tutorial/tutorial.h"
#include "tutorial/obj_loader.h"
#include "tutorial/hair_loader.h"
#include "sys/taskscheduler.h"
#include "image/image.h"

namespace embree
{
  /* name of the tutorial */
  const char* tutorialName = "tutorial07";

  /* configuration */
  static std::string g_rtcore = "";

  /* output settings */
  static size_t g_width = 512;
  static size_t g_height = 512;
  static bool g_fullscreen = false;
  static size_t g_numThreads = 0;

  static int tessellation_subdivisions = -1;
  static int tessellation_strips   = -1;

  /* scene */
  OBJScene g_obj_scene;
  static FileName objFilename = "";
  static FileName hairFilename = "";
  static FileName outFilename = "";
  Vec3fa offset = 0.0f;

  static void tessellateHair(OBJScene &scene);

  static void parseCommandLine(Ref<ParseStream> cin, const FileName& path)
  {
    while (true)
    {
      std::string tag = cin->getString();
      if (tag == "") return;

      /* parse command line parameters from a file */
      if (tag == "-c") {
        FileName file = path + cin->getFileName();
        parseCommandLine(new ParseStream(new LineCommentFilter(file, "#")), file.path());
      }

      /* load OBJ model */
      else if (tag == "-i") {
        objFilename = path + cin->getFileName();
      }

      /* load hair model */
      else if (tag == "--hair") {
        hairFilename = path + cin->getFileName();
      }

      /* scene offset */
      else if (tag == "--offset") {
        offset = cin->getVec3fa();
      }

      /* tessellation flags */
      else if (tag == "--tessellate") {
        tessellation_subdivisions = cin->getInt();
        tessellation_strips   = cin->getInt();
      }

      /* output filename */
      else if (tag == "-o") {
        outFilename = cin->getFileName();
      }

      /* parse camera parameters */
      else if (tag == "-vp") g_camera.from = cin->getVec3fa();
      else if (tag == "-vi") g_camera.to = cin->getVec3fa();
      else if (tag == "-vd") g_camera.to = g_camera.from + cin->getVec3fa();
      else if (tag == "-vu") g_camera.up = cin->getVec3fa();
      else if (tag == "-fov") g_camera.fov = cin->getFloat();

      /* frame buffer size */
      else if (tag == "-size") {
        g_width = cin->getInt();
        g_height = cin->getInt();
      }

      /* full screen mode */
      else if (tag == "-fullscreen")
        g_fullscreen = true;

      /* rtcore configuration */
      else if (tag == "-rtcore")
        g_rtcore = cin->getString();

      /* number of threads to use */
      else if (tag == "-threads")
        g_numThreads = cin->getInt();

      /* skip unknown command line parameter */
      else {
        std::cerr << "unknown command line parameter: " << tag << " ";
        while (cin->peek() != "" && cin->peek()[0] != '-') std::cerr << cin->getString() << " ";
        std::cerr << std::endl;
      }
    }
  }

  void addHairSegment(OBJScene &scene,
                      const vec3f &p00,
                      const vec3f &p01)
  {
    /* todo */
  }
  void tessellateHair(OBJScene &scene,
                      const vec3f &p00,
                      const vec3f &p01,
                      const vec3f &p02,
                      const vec3f &p03,
                      int subdivisions)
  {
    vec3f p00 = hairSet.v[hair.vertex+0];
    vec3f p01 = hairSet.v[hair.vertex+1];
    vec3f p02 = hairSet.v[hair.vertex+2];
    vec3f p03 = hairSet.v[hair.vertex+3];
    if (subdivisions > 0) {
      const vec3f p10 = 0.5f*(p00+p01);
      const vec3f p11 = 0.5f*(p01+p02);
      const vec3f p12 = 0.5f*(p02+p03);

      const vec3f p20 = 0.5f*(p10+p11);
      const vec3f p21 = 0.5f*(p11+p12);

      const vec3f p30 = 0.5f*(p20+p21);

      tessellateHair(scene,p00,p10,p20,p30,subdivisions-1);
      tessellateHair(scene,p30,p21,p12,p03,subdivisions-1);
    } else {
      addHairSegment(scene,p00,p01);
      addHairSegment(scene,p01,p02);
      addHairSegment(scene,p02,p03);
    }
  }
  void tessellateHair(OBJScene &scene,
                      OBJScene::HairSet &hairSet)
  {
    for (int i=0;i<hairSet.hair.size();i++) {
      OBJScene::Hair hair = hairSet.hairs[i];
      vec3f p00 = hairSet.v[hair.vertex+0];
      vec3f p01 = hairSet.v[hair.vertex+1];
      vec3f p02 = hairSet.v[hair.vertex+2];
      vec3f p03 = hairSet.v[hair.vertex+3];
      tessellateHair(scene,p00,p01,p02,p03,tessellate_subdivisions);
    }
  }
  void tessellateHair(OBJScene &scene)
  {
    for (int i=0;i<scene.hairsets.size();i++)
      tessellateHair(scene,scene.hairsets[i]);
    scene.hairsets.clear();
  }

  void renderToFile(const FileName& fileName)
  {
    resize(g_width,g_height);
    AffineSpace3fa pixel2world = g_camera.pixel2world(g_width,g_height);

    /* render image using ISPC */
    double t0 = getSeconds();
    render(0.0f,
           pixel2world.l.vx,
           pixel2world.l.vy,
           pixel2world.l.vz,
           pixel2world.p);
    double dt0 = getSeconds()-t0;

    void* ptr = map();
    Ref<Image> image = new Image4c(g_width, g_height, (Col4c*)ptr);
    storeImage(image, fileName);
    unmap();
  }

  /* main function in embree namespace */
  int main(int argc, char** argv)
  {
    /* create stream for parsing */
    Ref<ParseStream> stream = new ParseStream(new CommandLineStream(argc, argv));

    /* parse command line */
    parseCommandLine(stream, FileName());
    if (g_numThreads)
      g_rtcore += ",threads=" + std::stringOf(g_numThreads);

    /* initialize task scheduler */
#if !defined(__EXPORT_ALL_SYMBOLS__)
    TaskScheduler::create(g_numThreads);
#endif

    /* initialize ray tracing core */
    init(g_rtcore.c_str());

    /* load scene */
    if (objFilename.str() != "")
      loadOBJ(objFilename,g_obj_scene,offset);

    /* load hair */
    if (hairFilename.str() != "") {
      loadHair(hairFilename,g_obj_scene,offset);
      if (tessellate_subdivisions > 0 && tessellate_strips > 0)
        tessellateHair(g_obj_scene);
    }

    /* send model */
    if (objFilename.str() != "" && hairFilename.str() != "")
      set_scene(&g_obj_scene);

    /* render to disk */
    if (outFilename.str() != "") {
      renderToFile(outFilename);
      return 0;
    }

    /* initialize GLUT */
    initGlut(tutorialName,g_width,g_height,g_fullscreen,true);

    return 0;
  }
}

int main(int argc, char** argv)
{
  try {
    return embree::main(argc, argv);
  }
  catch (const std::exception& e) {
    std::cout << "Error: " << e.what() << std::endl;
    return 1;
  }
  catch (...) {
    std::cout << "Error: unknown exception caught." << std::endl;
    return 1;
  }
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2009, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software 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 OWNER 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.
//
//////////////////////////////////////////////////////////////////////////

#include "OpenEXR/ImathFun.h"

#include "IECore/CurvesPrimitiveEvaluator.h"
#include "IECore/CurvesPrimitive.h"
#include "IECore/Exception.h"
#include "IECore/FastFloat.h"

using namespace IECore;
using namespace Imath;
using namespace std;

//////////////////////////////////////////////////////////////////////////
// Implementation of Result
//////////////////////////////////////////////////////////////////////////

CurvesPrimitiveEvaluator::Result::Result()
{
}

template<class T>
T CurvesPrimitiveEvaluator::Result::primVar( const PrimitiveVariable &primVar, const float *coefficients ) const
{
	switch( primVar.interpolation )
	{
		case PrimitiveVariable::Constant :
			{
				const TypedData<T> *d = static_cast<TypedData<T> *>( primVar.data.get() );
				return d->readable();
			}
			break;
		case PrimitiveVariable::Uniform :
			{
				const vector<T> &d = static_cast<TypedData<vector<T> > *>( primVar.data.get() )->readable();
				return d[m_curveIndex];
			}
		case PrimitiveVariable::Vertex :
			{
				const vector<T> &d = static_cast<TypedData<vector<T> > *>( primVar.data.get() )->readable();
				return	(T)( coefficients[0] * d[m_vertexDataIndices[0]] +
						coefficients[1] * d[m_vertexDataIndices[1]] +
						coefficients[2] * d[m_vertexDataIndices[2]] +
						coefficients[3] * d[m_vertexDataIndices[3]] );
			}
		case PrimitiveVariable::Varying :
		case PrimitiveVariable::FaceVarying :
			{
				const vector<T> &d = static_cast<TypedData<vector<T> > *>( primVar.data.get() )->readable();
				return lerp( d[0], d[1], m_segmentV );
			}
		default :
			throw InvalidArgumentException( "PrimitiveVariable has invalid interpolation" );
	}
}

Imath::V3f CurvesPrimitiveEvaluator::Result::point() const
{
	return primVar<V3f>( m_p, m_coefficients );
}

Imath::V3f CurvesPrimitiveEvaluator::Result::normal() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

Imath::V2f CurvesPrimitiveEvaluator::Result::uv() const
{
	return V2f( 0, m_v );
}

Imath::V3f CurvesPrimitiveEvaluator::Result::uTangent() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

Imath::V3f CurvesPrimitiveEvaluator::Result::vTangent() const
{
	return primVar<V3f>( m_p, m_derivativeCoefficients );
}

unsigned CurvesPrimitiveEvaluator::Result::curveIndex() const
{
	return m_curveIndex;
}

Imath::V3f CurvesPrimitiveEvaluator::Result::vectorPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<V3f>( pv, m_coefficients );
}

float CurvesPrimitiveEvaluator::Result::floatPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<float>( pv, m_coefficients );
}

int CurvesPrimitiveEvaluator::Result::intPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<int>( pv, m_coefficients );
}

const std::string &CurvesPrimitiveEvaluator::Result::stringPrimVar( const PrimitiveVariable &pv ) const
{
	switch( pv.interpolation )
	{
		case PrimitiveVariable::Constant :
			{
				const TypedData<string> *d = static_cast<TypedData<string> *>( pv.data.get() );
				return d->readable();
			}
			break;
		case PrimitiveVariable::Uniform :
			{
				const vector<string> &d = static_cast<TypedData<vector<string> > *>( pv.data.get() )->readable();
				return d[m_curveIndex];
			}
		default :
			{
				throw InvalidArgumentException( "Can only evaluate string PrimitiveVariables with Constant or Uniform interpolation." );
			}
	}
}

Imath::Color3f CurvesPrimitiveEvaluator::Result::colorPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<Color3f>( pv, m_coefficients );
}

half CurvesPrimitiveEvaluator::Result::halfPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<half>( pv, m_coefficients );
}

void CurvesPrimitiveEvaluator::Result::init( unsigned curveIndex, float v, const CurvesPrimitiveEvaluator *evaluator )
{
	m_curveIndex = curveIndex;
	m_v = v;

	unsigned numSegments = evaluator->m_curvesPrimitive->numSegments( m_curveIndex );
	float vv = v * numSegments;
	unsigned segment = min( (unsigned)fastFloatFloor( vv ), numSegments - 1 );
	m_segmentV = vv - segment;

	const CubicBasisf &basis = evaluator->m_curvesPrimitive->basis();
	basis.coefficients( m_segmentV, m_coefficients );
	basis.derivativeCoefficients( m_segmentV, m_derivativeCoefficients );

	if( evaluator->m_curvesPrimitive->periodic() )
	{
		int numVertices = evaluator->m_verticesPerCurve[curveIndex];
		unsigned o = evaluator->m_vertexDataOffsets[m_curveIndex];
		unsigned i = segment * basis.step;
		m_vertexDataIndices[0] = o + i;
		m_vertexDataIndices[1] = o + ( ( i + 1 ) % numVertices );
		m_vertexDataIndices[2] = o + ( ( i + 2 ) % numVertices );
		m_vertexDataIndices[3] = o + ( ( i + 3 ) % numVertices );

		m_varyingDataIndices[0] = evaluator->m_varyingDataOffsets[m_curveIndex] + segment;
		m_varyingDataIndices[0] = evaluator->m_varyingDataOffsets[m_curveIndex] + ( segment % numSegments );
	}
	else
	{
		m_vertexDataIndices[0] = evaluator->m_vertexDataOffsets[m_curveIndex] + segment * basis.step;
		m_vertexDataIndices[1] = m_vertexDataIndices[0] + 1;
		m_vertexDataIndices[2] = m_vertexDataIndices[1] + 1;
		m_vertexDataIndices[3] = m_vertexDataIndices[2] + 1;

		m_varyingDataIndices[0] = evaluator->m_varyingDataOffsets[m_curveIndex] + segment;
		m_varyingDataIndices[1] = m_varyingDataIndices[0] + 1;
	}
}

//////////////////////////////////////////////////////////////////////////
// Implementation of Evaluator
//////////////////////////////////////////////////////////////////////////

CurvesPrimitiveEvaluator::CurvesPrimitiveEvaluator( ConstCurvesPrimitivePtr curves )
	:	m_curvesPrimitive( curves->copy() ), m_verticesPerCurve( m_curvesPrimitive->verticesPerCurve()->readable() )
{
	m_vertexDataOffsets.reserve( m_verticesPerCurve.size() );
	m_varyingDataOffsets.reserve( m_verticesPerCurve.size() );
	int vertexDataOffset = 0;
	int varyingDataOffset = 0;
	for( unsigned i=0; i<m_verticesPerCurve.size(); i++ )
	{
		m_vertexDataOffsets.push_back( vertexDataOffset );
		vertexDataOffset += m_verticesPerCurve[i];

		m_varyingDataOffsets.push_back( varyingDataOffset );
		varyingDataOffset += m_curvesPrimitive->variableSize( PrimitiveVariable::Varying, i );
	}

	PrimitiveVariableMap::iterator pIt = m_curvesPrimitive->variables.find( "P" );
	if( pIt==m_curvesPrimitive->variables.end() )
	{
		throw InvalidArgumentException( "No PrimitiveVariable named P on CurvesPrimitive." );
	}
	m_p = pIt->second;
}

CurvesPrimitiveEvaluator::~CurvesPrimitiveEvaluator()
{
}

ConstPrimitivePtr CurvesPrimitiveEvaluator::primitive() const
{
	return m_curvesPrimitive;
}

PrimitiveEvaluator::ResultPtr CurvesPrimitiveEvaluator::createResult() const
{
	Result *result = new Result;
	result->m_p = m_p;
	return result;
}

void CurvesPrimitiveEvaluator::validateResult( const PrimitiveEvaluator::ResultPtr &result ) const
{
	if( ! boost::dynamic_pointer_cast<CurvesPrimitiveEvaluator::Result>( result ) )
	{
		throw InvalidArgumentException( "CurvesPrimitiveEvaluator: Invalid result type" );
	}
}

float CurvesPrimitiveEvaluator::surfaceArea() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

float CurvesPrimitiveEvaluator::volume() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

Imath::V3f CurvesPrimitiveEvaluator::centerOfGravity() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

bool CurvesPrimitiveEvaluator::closestPoint( const Imath::V3f &p, const PrimitiveEvaluator::ResultPtr &result ) const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

bool CurvesPrimitiveEvaluator::pointAtUV( const Imath::V2f &uv, const PrimitiveEvaluator::ResultPtr &result ) const
{
	return pointAtV( 0, uv[1], result );
}

bool CurvesPrimitiveEvaluator::intersectionPoint( const Imath::V3f &origin, const Imath::V3f &direction,
	const PrimitiveEvaluator::ResultPtr &result, float maxDistance ) const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

int CurvesPrimitiveEvaluator::intersectionPoints( const Imath::V3f &origin, const Imath::V3f &direction,
	std::vector<PrimitiveEvaluator::ResultPtr> &results, float maxDistance ) const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

bool CurvesPrimitiveEvaluator::pointAtV( unsigned curveIndex, float v, const PrimitiveEvaluator::ResultPtr &result ) const
{
	if( curveIndex > m_verticesPerCurve.size() || v < 0.0f || v > 1.0f )
	{
		return false;
	}
	Result *typedResult = static_cast<Result *>( result.get() );
	typedResult->init( curveIndex, v, this );
	return true;
}
<commit_msg>Fixed test failure caused by not registering the CurvesPrimitiveEvaluator with the RunTimeTyped base class.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2009, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software 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 OWNER 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.
//
//////////////////////////////////////////////////////////////////////////

#include "OpenEXR/ImathFun.h"

#include "IECore/CurvesPrimitiveEvaluator.h"
#include "IECore/CurvesPrimitive.h"
#include "IECore/Exception.h"
#include "IECore/FastFloat.h"

using namespace IECore;
using namespace Imath;
using namespace std;

IE_CORE_DEFINERUNTIMETYPED( CurvesPrimitiveEvaluator );

//////////////////////////////////////////////////////////////////////////
// Implementation of Result
//////////////////////////////////////////////////////////////////////////

CurvesPrimitiveEvaluator::Result::Result()
{
}

template<class T>
T CurvesPrimitiveEvaluator::Result::primVar( const PrimitiveVariable &primVar, const float *coefficients ) const
{
	switch( primVar.interpolation )
	{
		case PrimitiveVariable::Constant :
			{
				const TypedData<T> *d = static_cast<TypedData<T> *>( primVar.data.get() );
				return d->readable();
			}
			break;
		case PrimitiveVariable::Uniform :
			{
				const vector<T> &d = static_cast<TypedData<vector<T> > *>( primVar.data.get() )->readable();
				return d[m_curveIndex];
			}
		case PrimitiveVariable::Vertex :
			{
				const vector<T> &d = static_cast<TypedData<vector<T> > *>( primVar.data.get() )->readable();
				return	(T)( coefficients[0] * d[m_vertexDataIndices[0]] +
						coefficients[1] * d[m_vertexDataIndices[1]] +
						coefficients[2] * d[m_vertexDataIndices[2]] +
						coefficients[3] * d[m_vertexDataIndices[3]] );
			}
		case PrimitiveVariable::Varying :
		case PrimitiveVariable::FaceVarying :
			{
				const vector<T> &d = static_cast<TypedData<vector<T> > *>( primVar.data.get() )->readable();
				return lerp( d[0], d[1], m_segmentV );
			}
		default :
			throw InvalidArgumentException( "PrimitiveVariable has invalid interpolation" );
	}
}

Imath::V3f CurvesPrimitiveEvaluator::Result::point() const
{
	return primVar<V3f>( m_p, m_coefficients );
}

Imath::V3f CurvesPrimitiveEvaluator::Result::normal() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

Imath::V2f CurvesPrimitiveEvaluator::Result::uv() const
{
	return V2f( 0, m_v );
}

Imath::V3f CurvesPrimitiveEvaluator::Result::uTangent() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

Imath::V3f CurvesPrimitiveEvaluator::Result::vTangent() const
{
	return primVar<V3f>( m_p, m_derivativeCoefficients );
}

unsigned CurvesPrimitiveEvaluator::Result::curveIndex() const
{
	return m_curveIndex;
}

Imath::V3f CurvesPrimitiveEvaluator::Result::vectorPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<V3f>( pv, m_coefficients );
}

float CurvesPrimitiveEvaluator::Result::floatPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<float>( pv, m_coefficients );
}

int CurvesPrimitiveEvaluator::Result::intPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<int>( pv, m_coefficients );
}

const std::string &CurvesPrimitiveEvaluator::Result::stringPrimVar( const PrimitiveVariable &pv ) const
{
	switch( pv.interpolation )
	{
		case PrimitiveVariable::Constant :
			{
				const TypedData<string> *d = static_cast<TypedData<string> *>( pv.data.get() );
				return d->readable();
			}
			break;
		case PrimitiveVariable::Uniform :
			{
				const vector<string> &d = static_cast<TypedData<vector<string> > *>( pv.data.get() )->readable();
				return d[m_curveIndex];
			}
		default :
			{
				throw InvalidArgumentException( "Can only evaluate string PrimitiveVariables with Constant or Uniform interpolation." );
			}
	}
}

Imath::Color3f CurvesPrimitiveEvaluator::Result::colorPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<Color3f>( pv, m_coefficients );
}

half CurvesPrimitiveEvaluator::Result::halfPrimVar( const PrimitiveVariable &pv ) const
{
	return primVar<half>( pv, m_coefficients );
}

void CurvesPrimitiveEvaluator::Result::init( unsigned curveIndex, float v, const CurvesPrimitiveEvaluator *evaluator )
{
	m_curveIndex = curveIndex;
	m_v = v;

	unsigned numSegments = evaluator->m_curvesPrimitive->numSegments( m_curveIndex );
	float vv = v * numSegments;
	unsigned segment = min( (unsigned)fastFloatFloor( vv ), numSegments - 1 );
	m_segmentV = vv - segment;

	const CubicBasisf &basis = evaluator->m_curvesPrimitive->basis();
	basis.coefficients( m_segmentV, m_coefficients );
	basis.derivativeCoefficients( m_segmentV, m_derivativeCoefficients );

	if( evaluator->m_curvesPrimitive->periodic() )
	{
		int numVertices = evaluator->m_verticesPerCurve[curveIndex];
		unsigned o = evaluator->m_vertexDataOffsets[m_curveIndex];
		unsigned i = segment * basis.step;
		m_vertexDataIndices[0] = o + i;
		m_vertexDataIndices[1] = o + ( ( i + 1 ) % numVertices );
		m_vertexDataIndices[2] = o + ( ( i + 2 ) % numVertices );
		m_vertexDataIndices[3] = o + ( ( i + 3 ) % numVertices );

		m_varyingDataIndices[0] = evaluator->m_varyingDataOffsets[m_curveIndex] + segment;
		m_varyingDataIndices[0] = evaluator->m_varyingDataOffsets[m_curveIndex] + ( segment % numSegments );
	}
	else
	{
		m_vertexDataIndices[0] = evaluator->m_vertexDataOffsets[m_curveIndex] + segment * basis.step;
		m_vertexDataIndices[1] = m_vertexDataIndices[0] + 1;
		m_vertexDataIndices[2] = m_vertexDataIndices[1] + 1;
		m_vertexDataIndices[3] = m_vertexDataIndices[2] + 1;

		m_varyingDataIndices[0] = evaluator->m_varyingDataOffsets[m_curveIndex] + segment;
		m_varyingDataIndices[1] = m_varyingDataIndices[0] + 1;
	}
}

//////////////////////////////////////////////////////////////////////////
// Implementation of Evaluator
//////////////////////////////////////////////////////////////////////////

CurvesPrimitiveEvaluator::CurvesPrimitiveEvaluator( ConstCurvesPrimitivePtr curves )
	:	m_curvesPrimitive( curves->copy() ), m_verticesPerCurve( m_curvesPrimitive->verticesPerCurve()->readable() )
{
	m_vertexDataOffsets.reserve( m_verticesPerCurve.size() );
	m_varyingDataOffsets.reserve( m_verticesPerCurve.size() );
	int vertexDataOffset = 0;
	int varyingDataOffset = 0;
	for( unsigned i=0; i<m_verticesPerCurve.size(); i++ )
	{
		m_vertexDataOffsets.push_back( vertexDataOffset );
		vertexDataOffset += m_verticesPerCurve[i];

		m_varyingDataOffsets.push_back( varyingDataOffset );
		varyingDataOffset += m_curvesPrimitive->variableSize( PrimitiveVariable::Varying, i );
	}

	PrimitiveVariableMap::iterator pIt = m_curvesPrimitive->variables.find( "P" );
	if( pIt==m_curvesPrimitive->variables.end() )
	{
		throw InvalidArgumentException( "No PrimitiveVariable named P on CurvesPrimitive." );
	}
	m_p = pIt->second;
}

CurvesPrimitiveEvaluator::~CurvesPrimitiveEvaluator()
{
}

ConstPrimitivePtr CurvesPrimitiveEvaluator::primitive() const
{
	return m_curvesPrimitive;
}

PrimitiveEvaluator::ResultPtr CurvesPrimitiveEvaluator::createResult() const
{
	Result *result = new Result;
	result->m_p = m_p;
	return result;
}

void CurvesPrimitiveEvaluator::validateResult( const PrimitiveEvaluator::ResultPtr &result ) const
{
	if( ! boost::dynamic_pointer_cast<CurvesPrimitiveEvaluator::Result>( result ) )
	{
		throw InvalidArgumentException( "CurvesPrimitiveEvaluator: Invalid result type" );
	}
}

float CurvesPrimitiveEvaluator::surfaceArea() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

float CurvesPrimitiveEvaluator::volume() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

Imath::V3f CurvesPrimitiveEvaluator::centerOfGravity() const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

bool CurvesPrimitiveEvaluator::closestPoint( const Imath::V3f &p, const PrimitiveEvaluator::ResultPtr &result ) const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

bool CurvesPrimitiveEvaluator::pointAtUV( const Imath::V2f &uv, const PrimitiveEvaluator::ResultPtr &result ) const
{
	return pointAtV( 0, uv[1], result );
}

bool CurvesPrimitiveEvaluator::intersectionPoint( const Imath::V3f &origin, const Imath::V3f &direction,
	const PrimitiveEvaluator::ResultPtr &result, float maxDistance ) const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

int CurvesPrimitiveEvaluator::intersectionPoints( const Imath::V3f &origin, const Imath::V3f &direction,
	std::vector<PrimitiveEvaluator::ResultPtr> &results, float maxDistance ) const
{
	throw NotImplementedException( __PRETTY_FUNCTION__ );
}

bool CurvesPrimitiveEvaluator::pointAtV( unsigned curveIndex, float v, const PrimitiveEvaluator::ResultPtr &result ) const
{
	if( curveIndex > m_verticesPerCurve.size() || v < 0.0f || v > 1.0f )
	{
		return false;
	}
	Result *typedResult = static_cast<Result *>( result.get() );
	typedResult->init( curveIndex, v, this );
	return true;
}
<|endoftext|>


<commit_before>#include "FuseGPUThreadLoops.h"
#include "CodeGen_GPU_Dev.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Bounds.h"
#include "Substitute.h"
#include "IREquality.h"
#include "Simplify.h"

namespace Halide {
namespace Internal {

using std::vector;
using std::string;
using std::set;

namespace {
string thread_names[] = {"threadidx", "threadidy", "threadidz", "threadidw"};
string shared_mem_name = "__shared__";
}

class InjectThreadBarriers : public IRMutator {
    bool in_threads;

    using IRMutator::visit;

    void visit(const For *op) {
        bool old_in_threads = in_threads;

        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            in_threads = true;
        }

        IRMutator::visit(op);

        in_threads = old_in_threads;
    }

    Stmt barrier() {
        return Evaluate::make(Call::make(Int(32), Call::gpu_thread_barrier, vector<Expr>(), Call::Intrinsic));
    }

    void visit(const Pipeline *op) {
        if (!in_threads) {
            Stmt produce = mutate(op->produce);
            produce = Block::make(produce, barrier());

            Stmt update;
            if (op->update.defined()) {
                update = mutate(op->update);
                update = Block::make(update, barrier());
            }

            Stmt consume = mutate(op->consume);

            stmt = Pipeline::make(op->name, produce, update, consume);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Block *op) {
        if (!in_threads && op->rest.defined()) {
            Stmt first = mutate(op->first);
            Stmt rest = mutate(op->rest);
            stmt = Block::make(Block::make(first, barrier()), rest);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    InjectThreadBarriers() : in_threads(false) {}
};

class ExtractBlockSize : public IRVisitor {
    Expr block_extent[4];

    using IRVisitor::visit;

    void found_for(int dim, Expr extent) {
        internal_assert(dim >= 0 && dim < 4);
        if (!block_extent[dim].defined()) {
            block_extent[dim] = extent;
        } else {
            block_extent[dim] = simplify(Max::make(extent, block_extent[dim]));
        }
    }

    void visit(const For *op) {
        for (int i = 0; i < 4; i++) {
            if (ends_with(op->name, thread_names[i])) {
                found_for(i, op->extent);
            }
        }

        IRVisitor::visit(op);
    }

public:
    int dimensions() const {
        for (int i = 0; i < 4; i++) {
            if (!block_extent[i].defined()) {
                return i;
            }
        }
        return 4;
    }

    Expr extent(int d) const {
        return block_extent[d];
    }
};

class NormalizeDimensionality : public IRMutator {
    using IRMutator::visit;

    const ExtractBlockSize &block_size;

    int depth;
    int max_depth;

    bool should_wrap;

    Stmt wrap(Stmt s) {
        if (depth != 0 || !should_wrap) {
            return s;
        }
        max_depth = 0;
        should_wrap = false;
        s = mutate(s);
        while (max_depth < block_size.dimensions()) {
            string name = thread_names[max_depth];
            s = For::make(name, 0, 1, For::Parallel, s);
            max_depth++;
        }
        should_wrap = true;
        return s;
    }

    void visit(const Pipeline *op) {
        Stmt produce = wrap(op->produce);
        Stmt update;
        if (op->update.defined()) {
            update = wrap(op->update);
        }
        Stmt consume = wrap(op->consume);

        if (produce.same_as(op->produce) &&
            update.same_as(op->update) &&
            consume.same_as(op->consume)) {
            stmt = op;
        } else {
            stmt = Pipeline::make(op->name, produce, update, consume);
        }
    }

    void visit(const Block *op) {
        Stmt first = wrap(op->first);

        Stmt rest;
        if (op->rest.defined()) {
            rest = wrap(op->rest);
        }

        if (first.same_as(op->first) &&
            rest.same_as(op->rest)) {
            stmt = op;
        } else {
            stmt = Block::make(first, rest);
        }
    }

    void visit(const For *op) {
        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            depth++;
            if (depth > max_depth) {
                max_depth = depth;
            }
            if (!is_zero(op->min)) {
                // Shift them all to be zero-based.
                Expr var = Variable::make(Int(32), op->name);
                Stmt body = substitute(op->name, var + op->min, op->body);
                body = mutate(body);
                stmt = For::make(op->name, 0, op->extent, For::Parallel, body);
            } else {
                IRMutator::visit(op);
            }
            depth--;
        } else {
            IRMutator::visit(op);
        }
    }

public:
    NormalizeDimensionality(const ExtractBlockSize &e) : block_size(e), depth(0), max_depth(0), should_wrap(true) {}
};

class ReplaceForWithIf : public IRMutator {
    using IRMutator::visit;

    const ExtractBlockSize &block_size;

    void visit(const For *op) {
        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            int dim;
            for (dim = 0; dim < 4; dim++) {
                if (ends_with(op->name, thread_names[dim])) {
                    break;
                }
            }

            internal_assert(dim >= 0 && dim < block_size.dimensions());

            Expr var = Variable::make(Int(32), "." + thread_names[dim]);
            internal_assert(is_zero(op->min));
            Stmt body = mutate(op->body);

            body = substitute(op->name, var, body);

            if (equal(op->extent, block_size.extent(dim))) {
                stmt = body;
            } else {
                Expr cond = var < op->extent;
                stmt = IfThenElse::make(cond, body, Stmt());
            }
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceForWithIf(const ExtractBlockSize &e) : block_size(e) {}
};

class ExtractSharedAllocations : public IRMutator {

    using IRMutator::visit;

    struct SharedAllocation {
        string name;
        Type type;
        Expr size;
    };
    vector<SharedAllocation> allocations;

    Scope<Interval> scope;

    set<string> shared;

    bool in_threads;

    void visit(const For *op) {
        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            bool old = in_threads;
            in_threads = true;
            IRMutator::visit(op);
            in_threads = old;
        } else {
            Interval min_bounds = bounds_of_expr_in_scope(op->min, scope);
            Interval extent_bounds = bounds_of_expr_in_scope(op->extent, scope);
            Interval bounds(min_bounds.min, min_bounds.max + extent_bounds.max - 1);
            scope.push(op->name, bounds);
            IRMutator::visit(op);
            scope.pop(op->name);
        }
    }

    void visit(const Allocate *op) {
        if (in_threads) {
            IRMutator::visit(op);
            return;
        }

        shared.insert(op->name);
        IRMutator::visit(op);
        shared.erase(op->name);
        op = stmt.as<Allocate>();
        internal_assert(op);

        SharedAllocation alloc;
        alloc.name = op->name;
        alloc.type = op->type;
        alloc.size = 1;
        for (size_t i = 0; i < op->extents.size(); i++) {
            alloc.size *= bounds_of_expr_in_scope(op->extents[i], scope).max;
        }
        allocations.push_back(alloc);
        stmt = op->body;

    }

    void visit(const Load *op) {
        if (shared.count(op->name)) {
            Expr base = Variable::make(Int(32), op->name + ".shared_offset");
            Expr index = mutate(op->index);
            expr = Load::make(op->type, shared_mem_name, base + index, op->image, op->param);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        if (shared.count(op->name)) {
            Expr base = Variable::make(Int(32), op->name + ".shared_offset");
            Expr index = mutate(op->index);
            Expr value = mutate(op->value);
            stmt = Store::make(shared_mem_name, value, base + index);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const LetStmt *op) {
        if (in_threads) {
            IRMutator::visit(op);
            return;
        }

        Expr value = mutate(op->value);
        Interval bounds = bounds_of_expr_in_scope(value, scope);
        scope.push(op->name, bounds);
        Stmt body = mutate(op->body);
        scope.pop(op->name);

        if (op->body.same_as(body) && op->value.same_as(value)) {
            stmt = op;
        } else {
            stmt = LetStmt::make(op->name, value, body);
        }
    }

public:
    Stmt rewrap(Stmt s) {
        // Sort the allocations by size in bytes of the primitive
        // type. Use reverse insertion sort.
        for (size_t i = 1; i < allocations.size(); i++) {
            for (size_t j = i; j > 0; j--) {
                if (allocations[j].type.bytes() > allocations[j - 1].type.bytes()) {
                    std::swap(allocations[j], allocations[j - 1]);
                }
            }
        }

        // Add a dummy allocation at the end to get the total size
        SharedAllocation sentinel;
        sentinel.name = "sentinel";
        sentinel.type = UInt(8);
        sentinel.size = 0;
        allocations.push_back(sentinel);

        Expr total_size = Variable::make(Int(32), allocations.back().name + ".shared_offset");
        s = Allocate::make(shared_mem_name, UInt(8), vec(total_size), s);

        // Define an offset for each allocation. The offsets are in
        // elements, not bytes, so that the stores and loads can use
        // them directly.
        for (int i = (int)(allocations.size()) - 1; i >= 0; i--) {
            Expr offset = 0;
            if (i > 0) {
                offset = Variable::make(Int(32), allocations[i-1].name + ".shared_offset");
                offset += allocations[i-1].size;
                int old_elem_size = allocations[i-1].type.bytes();
                int new_elem_size = allocations[i].type.bytes();
                internal_assert(old_elem_size >= new_elem_size);
                if (old_elem_size != new_elem_size) {
                    // We only have power-of-two sized types.
                    internal_assert(old_elem_size % new_elem_size == 0);
                    offset *= (old_elem_size / new_elem_size);
                }
            }

            s = LetStmt::make(allocations[i].name + ".shared_offset", offset, s);
        }

        return s;
    }

    ExtractSharedAllocations() : in_threads(false) {}
};

class FuseGPUThreadLoops : public IRMutator {
    using IRMutator::visit;

    void visit(const For *op) {
        if (ends_with(op->name, ".blockidx")) {

            Stmt body = op->body;

            ExtractBlockSize e;
            body.accept(&e);

            debug(3) << "Fusing thread block:\n" << body << "\n\n";

            NormalizeDimensionality n(e);
            body = n.mutate(body);

            debug(3) << "Normalized dimensionality:\n" << body << "\n\n";

            ExtractSharedAllocations h;
            body = h.mutate(body);

            debug(3) << "Pulled out shared allocations:\n" << body << "\n\n";

            InjectThreadBarriers i;
            body = i.mutate(body);

            debug(3) << "Injected synchronization:\n" << body << "\n\n";

            ReplaceForWithIf f(e);
            body = f.mutate(body);

            debug(3) << "Replaced for with if:\n" << body << "\n\n";

            // Rewrap the whole thing in the loop over threads
            for (int i = 0; i < e.dimensions(); i++) {
                body = For::make("." + thread_names[i], 0, e.extent(i), For::Parallel, body);
            }

            // There at least needs to be a loop over threadidx as a marker for codegen
            if (e.dimensions() == 0) {
                body = For::make(".threadidx", 0, 1, For::Parallel, body);
            }

            debug(3) << "Rewrapped in for loops:\n" << body << "\n\n";

            // Add back in the shared allocations
            body = h.rewrap(body);

            debug(3) << "Add back in shared allocations:\n" << body << "\n\n";

            if (body.same_as(op->body)) {
                stmt = op;
            } else {
                stmt = For::make(op->name, op->min, op->extent, op->for_type, body);
            }
        } else {
            IRMutator::visit(op);
        }

        // Shift the min to be zero
        if (CodeGen_GPU_Dev::is_gpu_var(op->name) && !is_zero(op->min)) {
            op = stmt.as<For>();
            internal_assert(op);
            Expr adjusted = Variable::make(Int(32), op->name) + op->min;
            Stmt body = substitute(op->name, adjusted, op->body);
            stmt = For::make(op->name, 0, op->extent, op->for_type, body);
        }
    }
};

Stmt fuse_gpu_thread_loops(Stmt s) {
    FuseGPUThreadLoops f;
    return f.mutate(s);
}

}
}

<commit_msg>Fix bug in dimensionality normalization for gpu loops<commit_after>#include "FuseGPUThreadLoops.h"
#include "CodeGen_GPU_Dev.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Bounds.h"
#include "Substitute.h"
#include "IREquality.h"
#include "Simplify.h"

namespace Halide {
namespace Internal {

using std::vector;
using std::string;
using std::set;

namespace {
string thread_names[] = {"threadidx", "threadidy", "threadidz", "threadidw"};
string shared_mem_name = "__shared__";
}

class InjectThreadBarriers : public IRMutator {
    bool in_threads;

    using IRMutator::visit;

    void visit(const For *op) {
        bool old_in_threads = in_threads;

        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            in_threads = true;
        }

        IRMutator::visit(op);

        in_threads = old_in_threads;
    }

    Stmt barrier() {
        return Evaluate::make(Call::make(Int(32), Call::gpu_thread_barrier, vector<Expr>(), Call::Intrinsic));
    }

    void visit(const Pipeline *op) {
        if (!in_threads) {
            Stmt produce = mutate(op->produce);
            produce = Block::make(produce, barrier());

            Stmt update;
            if (op->update.defined()) {
                update = mutate(op->update);
                update = Block::make(update, barrier());
            }

            Stmt consume = mutate(op->consume);

            stmt = Pipeline::make(op->name, produce, update, consume);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Block *op) {
        if (!in_threads && op->rest.defined()) {
            Stmt first = mutate(op->first);
            Stmt rest = mutate(op->rest);
            stmt = Block::make(Block::make(first, barrier()), rest);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    InjectThreadBarriers() : in_threads(false) {}
};

class ExtractBlockSize : public IRVisitor {
    Expr block_extent[4];

    using IRVisitor::visit;

    void found_for(int dim, Expr extent) {
        internal_assert(dim >= 0 && dim < 4);
        if (!block_extent[dim].defined()) {
            block_extent[dim] = extent;
        } else {
            block_extent[dim] = simplify(Max::make(extent, block_extent[dim]));
        }
    }

    void visit(const For *op) {
        for (int i = 0; i < 4; i++) {
            if (ends_with(op->name, thread_names[i])) {
                found_for(i, op->extent);
            }
        }

        IRVisitor::visit(op);
    }

public:
    int dimensions() const {
        for (int i = 0; i < 4; i++) {
            if (!block_extent[i].defined()) {
                return i;
            }
        }
        return 4;
    }

    Expr extent(int d) const {
        return block_extent[d];
    }
};

class NormalizeDimensionality : public IRMutator {
    using IRMutator::visit;

    const ExtractBlockSize &block_size;

    int depth;
    int max_depth;

    Stmt wrap(Stmt s) {
        if (depth != 0) {
            return mutate(s);
        }
        max_depth = 0;
        s = mutate(s);
        while (max_depth < block_size.dimensions()) {
            string name = thread_names[max_depth];
            s = For::make(name, 0, 1, For::Parallel, s);
            max_depth++;
        }
        return s;
    }

    void visit(const Pipeline *op) {
        Stmt produce = wrap(op->produce);
        Stmt update;
        if (op->update.defined()) {
            update = wrap(op->update);
        }
        Stmt consume = wrap(op->consume);

        if (produce.same_as(op->produce) &&
            update.same_as(op->update) &&
            consume.same_as(op->consume)) {
            stmt = op;
        } else {
            stmt = Pipeline::make(op->name, produce, update, consume);
        }
    }

    void visit(const Block *op) {
        Stmt first = wrap(op->first);

        Stmt rest;
        if (op->rest.defined()) {
            rest = wrap(op->rest);
        }

        if (first.same_as(op->first) &&
            rest.same_as(op->rest)) {
            stmt = op;
        } else {
            stmt = Block::make(first, rest);
        }
    }

    void visit(const For *op) {
        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            depth++;
            if (depth > max_depth) {
                max_depth = depth;
            }
            if (!is_zero(op->min)) {
                // Shift them all to be zero-based.
                Expr var = Variable::make(Int(32), op->name);
                Stmt body = substitute(op->name, var + op->min, op->body);
                body = mutate(body);
                stmt = For::make(op->name, 0, op->extent, For::Parallel, body);
            } else {
                IRMutator::visit(op);
            }
            depth--;
        } else {
            IRMutator::visit(op);
        }
    }

public:
    NormalizeDimensionality(const ExtractBlockSize &e) : block_size(e), depth(0), max_depth(0) {}
};

class ReplaceForWithIf : public IRMutator {
    using IRMutator::visit;

    const ExtractBlockSize &block_size;

    void visit(const For *op) {
        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            int dim;
            for (dim = 0; dim < 4; dim++) {
                if (ends_with(op->name, thread_names[dim])) {
                    break;
                }
            }

            internal_assert(dim >= 0 && dim < block_size.dimensions());

            Expr var = Variable::make(Int(32), "." + thread_names[dim]);
            internal_assert(is_zero(op->min));
            Stmt body = mutate(op->body);

            body = substitute(op->name, var, body);

            if (equal(op->extent, block_size.extent(dim))) {
                stmt = body;
            } else {
                Expr cond = var < op->extent;
                stmt = IfThenElse::make(cond, body, Stmt());
            }
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceForWithIf(const ExtractBlockSize &e) : block_size(e) {}
};

class ExtractSharedAllocations : public IRMutator {

    using IRMutator::visit;

    struct SharedAllocation {
        string name;
        Type type;
        Expr size;
    };
    vector<SharedAllocation> allocations;

    Scope<Interval> scope;

    set<string> shared;

    bool in_threads;

    void visit(const For *op) {
        if (CodeGen_GPU_Dev::is_gpu_thread_var(op->name)) {
            bool old = in_threads;
            in_threads = true;
            IRMutator::visit(op);
            in_threads = old;
        } else {
            Interval min_bounds = bounds_of_expr_in_scope(op->min, scope);
            Interval extent_bounds = bounds_of_expr_in_scope(op->extent, scope);
            Interval bounds(min_bounds.min, min_bounds.max + extent_bounds.max - 1);
            scope.push(op->name, bounds);
            IRMutator::visit(op);
            scope.pop(op->name);
        }
    }

    void visit(const Allocate *op) {
        if (in_threads) {
            IRMutator::visit(op);
            return;
        }

        shared.insert(op->name);
        IRMutator::visit(op);
        shared.erase(op->name);
        op = stmt.as<Allocate>();
        internal_assert(op);

        SharedAllocation alloc;
        alloc.name = op->name;
        alloc.type = op->type;
        alloc.size = 1;
        for (size_t i = 0; i < op->extents.size(); i++) {
            alloc.size *= bounds_of_expr_in_scope(op->extents[i], scope).max;
        }
        allocations.push_back(alloc);
        stmt = op->body;

    }

    void visit(const Load *op) {
        if (shared.count(op->name)) {
            Expr base = Variable::make(Int(32), op->name + ".shared_offset");
            Expr index = mutate(op->index);
            expr = Load::make(op->type, shared_mem_name, base + index, op->image, op->param);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        if (shared.count(op->name)) {
            Expr base = Variable::make(Int(32), op->name + ".shared_offset");
            Expr index = mutate(op->index);
            Expr value = mutate(op->value);
            stmt = Store::make(shared_mem_name, value, base + index);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const LetStmt *op) {
        if (in_threads) {
            IRMutator::visit(op);
            return;
        }

        Expr value = mutate(op->value);
        Interval bounds = bounds_of_expr_in_scope(value, scope);
        scope.push(op->name, bounds);
        Stmt body = mutate(op->body);
        scope.pop(op->name);

        if (op->body.same_as(body) && op->value.same_as(value)) {
            stmt = op;
        } else {
            stmt = LetStmt::make(op->name, value, body);
        }
    }

public:
    Stmt rewrap(Stmt s) {
        // Sort the allocations by size in bytes of the primitive
        // type. Use reverse insertion sort.
        for (size_t i = 1; i < allocations.size(); i++) {
            for (size_t j = i; j > 0; j--) {
                if (allocations[j].type.bytes() > allocations[j - 1].type.bytes()) {
                    std::swap(allocations[j], allocations[j - 1]);
                }
            }
        }

        // Add a dummy allocation at the end to get the total size
        SharedAllocation sentinel;
        sentinel.name = "sentinel";
        sentinel.type = UInt(8);
        sentinel.size = 0;
        allocations.push_back(sentinel);

        Expr total_size = Variable::make(Int(32), allocations.back().name + ".shared_offset");
        s = Allocate::make(shared_mem_name, UInt(8), vec(total_size), s);

        // Define an offset for each allocation. The offsets are in
        // elements, not bytes, so that the stores and loads can use
        // them directly.
        for (int i = (int)(allocations.size()) - 1; i >= 0; i--) {
            Expr offset = 0;
            if (i > 0) {
                offset = Variable::make(Int(32), allocations[i-1].name + ".shared_offset");
                offset += allocations[i-1].size;
                int old_elem_size = allocations[i-1].type.bytes();
                int new_elem_size = allocations[i].type.bytes();
                internal_assert(old_elem_size >= new_elem_size);
                if (old_elem_size != new_elem_size) {
                    // We only have power-of-two sized types.
                    internal_assert(old_elem_size % new_elem_size == 0);
                    offset *= (old_elem_size / new_elem_size);
                }
            }

            s = LetStmt::make(allocations[i].name + ".shared_offset", offset, s);
        }

        return s;
    }

    ExtractSharedAllocations() : in_threads(false) {}
};

class FuseGPUThreadLoops : public IRMutator {
    using IRMutator::visit;

    void visit(const For *op) {
        if (ends_with(op->name, ".blockidx")) {

            Stmt body = op->body;

            ExtractBlockSize e;
            body.accept(&e);

            debug(3) << "Fusing thread block:\n" << body << "\n\n";

            NormalizeDimensionality n(e);
            body = n.mutate(body);

            debug(3) << "Normalized dimensionality:\n" << body << "\n\n";

            ExtractSharedAllocations h;
            body = h.mutate(body);

            debug(3) << "Pulled out shared allocations:\n" << body << "\n\n";

            InjectThreadBarriers i;
            body = i.mutate(body);

            debug(3) << "Injected synchronization:\n" << body << "\n\n";

            ReplaceForWithIf f(e);
            body = f.mutate(body);

            debug(3) << "Replaced for with if:\n" << body << "\n\n";

            // Rewrap the whole thing in the loop over threads
            for (int i = 0; i < e.dimensions(); i++) {
                body = For::make("." + thread_names[i], 0, e.extent(i), For::Parallel, body);
            }

            // There at least needs to be a loop over threadidx as a marker for codegen
            if (e.dimensions() == 0) {
                body = For::make(".threadidx", 0, 1, For::Parallel, body);
            }

            debug(3) << "Rewrapped in for loops:\n" << body << "\n\n";

            // Add back in the shared allocations
            body = h.rewrap(body);

            debug(3) << "Add back in shared allocations:\n" << body << "\n\n";

            if (body.same_as(op->body)) {
                stmt = op;
            } else {
                stmt = For::make(op->name, op->min, op->extent, op->for_type, body);
            }
        } else {
            IRMutator::visit(op);
        }

        // Shift the min to be zero
        if (CodeGen_GPU_Dev::is_gpu_var(op->name) && !is_zero(op->min)) {
            op = stmt.as<For>();
            internal_assert(op);
            Expr adjusted = Variable::make(Int(32), op->name) + op->min;
            Stmt body = substitute(op->name, adjusted, op->body);
            stmt = For::make(op->name, 0, op->extent, op->for_type, body);
        }
    }
};

Stmt fuse_gpu_thread_loops(Stmt s) {
    FuseGPUThreadLoops f;
    return f.mutate(s);
}

}
}

<|endoftext|>


<commit_before>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"
#include "raw.h"

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *channel;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

// Connects to the server at a the given port.
void Connect(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Try each address until we successfully connect().
  // If socket() (or connect()) fails, close the socket and try the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int RequestSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int RequestLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int RequestLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int RequestJoin(char *channel) {
  struct request_join join;
  memset((char *) &join, 0, sizeof(join));
  join.req_type = REQ_JOIN;
  strncpy(join.req_channel, channel, CHANNEL_MAX);

  size_t message_size = sizeof(struct request_join);

  if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request join\n");
  }

  return 0;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);
  bool result = true;

  if (inputs[0] == "/exit") {
    RequestLogout();
    cooked_mode();
    result = false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join") {

  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch") {

  } else {
    std::cout << "\n*Unknown command" << std::endl;
  }

  return result;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  char *input;
  char tmp_buffer[SAY_MAX];
  memset(&tmp_buffer, 0, SAY_MAX);

  char stdin_buffer[SAY_MAX + 1];
  char *stdin_buffer_position = stdin_buffer;

//  struct timeval timeout;
  fd_set read_set;
//  int file_desc = 0;
  int result;
  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

//  char stdin_buffer[kBufferSize];
//  memset(&stdin_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  Connect(domain, port_str);

  RequestLogin(username);

  channel = (char *) "Common";
  RequestJoin(channel);

  // TODO handle response from send

  if (raw_mode() != 0){
    Error("client: error using raw mode");
  }

  std::cout << ">" << std::flush;

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        char c = (char) getchar();
        if (c == '\n') {
          *stdin_buffer_position++ = '\0';
          stdin_buffer_position = stdin_buffer;
          printf("\n");
          fflush(stdout);

          memset(&input, 0, SAY_MAX + 1);;
          strncpy(input, stdin_buffer, sizeof(stdin_buffer));
          memset(&stdin_buffer, 0, SAY_MAX + 1);

          if (input[0] == '/') {
            if (!ProcessInput(input)) {
              break;
            }
          } else {
            // Send chat messages
            RequestSay(input);
          }

        } else if (stdin_buffer_position != stdin_buffer + SAY_MAX) {
          *stdin_buffer_position++ = c;
          printf("%c", c);
//          std::cout << c << std::endl;
          fflush(stdout);
        }
      } // end of if STDIN_FILENO

      if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data
        int read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          // TODO capture user input, store, clean input, then print buffer, afterward replace input
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              struct text_say say;
              memcpy(&say, receive_buffer, sizeof(struct text_say));
              std::cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
              std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
              std::cout << ">" << stdin_buffer << std::flush;
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      } // end of if client_socket

    } // end of if result

  } // end of while

  return 0;
}<commit_msg>remove all that<commit_after>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"
#include "raw.h"

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *channel;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

// Connects to the server at a the given port.
void Connect(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Try each address until we successfully connect().
  // If socket() (or connect()) fails, close the socket and try the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int RequestSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int RequestLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int RequestLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int RequestJoin(char *channel) {
  struct request_join join;
  memset((char *) &join, 0, sizeof(join));
  join.req_type = REQ_JOIN;
  strncpy(join.req_channel, channel, CHANNEL_MAX);

  size_t message_size = sizeof(struct request_join);

  if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request join\n");
  }

  return 0;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);
  bool result = true;

  if (inputs[0] == "/exit") {
    RequestLogout();
    cooked_mode();
    result = false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join") {

  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch") {

  } else {
    std::cout << "\n*Unknown command" << std::endl;
  }

  return result;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  char *input;
  char tmp_buffer[SAY_MAX];
  memset(&tmp_buffer, 0, SAY_MAX);

  char stdin_buffer[SAY_MAX + 1];
  char *stdin_buffer_position = stdin_buffer;

//  struct timeval timeout;
  fd_set read_set;
//  int file_desc = 0;
  int result;
  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

//  char stdin_buffer[kBufferSize];
//  memset(&stdin_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  Connect(domain, port_str);

  RequestLogin(username);

  channel = (char *) "Common";
  RequestJoin(channel);

  // TODO handle response from send

  if (raw_mode() != 0){
    Error("client: error using raw mode");
  }

  std::cout << ">" << std::flush;

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        char c = (char) getchar();
        if (c == '\n') {
          *stdin_buffer_position++ = '\0';
          stdin_buffer_position = stdin_buffer;
          printf("\n");
          fflush(stdout);

//          memset(&input, 0, SAY_MAX + 1);;
//          strncpy(input, stdin_buffer, sizeof(stdin_buffer));
//          memset(&stdin_buffer, 0, SAY_MAX + 1);

          if (input[0] == '/') {
            if (!ProcessInput(input)) {
              break;
            }
          } else {
            // Send chat messages
            RequestSay(input);
          }

        } else if (stdin_buffer_position != stdin_buffer + SAY_MAX) {
          *stdin_buffer_position++ = c;
          printf("%c", c);
//          std::cout << c << std::endl;
          fflush(stdout);
        }
      } // end of if STDIN_FILENO

      if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data
        int read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          // TODO capture user input, store, clean input, then print buffer, afterward replace input
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              struct text_say say;
              memcpy(&say, receive_buffer, sizeof(struct text_say));
              std::cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
              std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
              std::cout << ">" << stdin_buffer << std::flush;
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      } // end of if client_socket

    } // end of if result

  } // end of while

  return 0;
}<|endoftext|>