<commit_before>/* Copyright 2016 Carnegie Mellon University
 *
 * 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 "scanner/evaluators/caffe/default/default_input_evaluator.h"
#include "scanner/util/memory.h"

namespace scanner {

DefaultInputEvaluator::DefaultInputEvaluator(DeviceType device_type,
                                             i32 device_id,
                                             const NetDescriptor& descriptor,
                                             i32 batch_size)
    : device_type_(device_type),
      device_id_(device_id),
      descriptor_(descriptor),
      batch_size_(batch_size)
#ifdef HAVE_CUDA
      ,
      num_cuda_streams_(32),
      streams_(num_cuda_streams_)
#endif

{
  if (descriptor_.input_width != -1) {
    net_input_width_ = descriptor_.input_width;
    net_input_height_ = descriptor_.input_height;
  } else {
    net_input_width_ = -1;
    net_input_height_ = -1;
  }

  if (device_type_ == DeviceType::CPU) {
    std::vector<float>& mean_colors = descriptor_.mean_colors;
    caffe::TransformationParameter param;
    param.set_force_color(true);
    param.set_scale(1.0 / 255.0);
    for (i32 i = 0; i < mean_colors.size(); i++) {
      param.add_mean_value(mean_colors[i]);
    }
    transformer_ =
        std::make_unique<caffe::DataTransformer<f32>>(param, caffe::TEST);
  }
}

void DefaultInputEvaluator::configure(const VideoMetadata& metadata) {
  metadata_ = metadata;

  i32 width = metadata.width();
  i32 height = metadata.height();
  if (net_input_width_ == -1) {
    net_input_width_ = width;
    net_input_height_ = height;
  }

  output_blob_.Reshape(batch_size_, 3, net_input_height_, net_input_width_);

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    cv::cuda::setDevice(device_id_);
    cudaSetDevice(device_id_);

    mean_mat_g_ = cv::cuda::GpuMat(
        net_input_height_, net_input_width_, CV_32FC3,
        cv::Scalar(descriptor_.mean_colors[0], descriptor_.mean_colors[1],
                   descriptor_.mean_colors[2]));

    frame_input_g_.clear();
    float_input_g_.clear();
    normalized_input_g_.clear();
    input_planes_g_.clear();
    planar_input_g_.clear();
    for (size_t i = 0; i < num_cuda_streams_; ++i) {
      frame_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      float_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      meanshifted_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      normalized_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      std::vector<cv::cuda::GpuMat> planes;
      for (i32 i = 0; i < 3; ++i) {
        planes.push_back(
            cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC1));
      }
      input_planes_g_.push_back(planes);
      planar_input_g_.push_back(
          cv::cuda::GpuMat(net_input_width_ * 3, net_input_height_, CV_32FC1));
    }
#else
    LOG(FATAL) << "Not built with Cuda support.";
#endif
  } else {
    resized_input_c_ = cv::Mat(net_input_height_, net_input_width_, CV_8UC3);
    for (i32 i = 0; i < batch_size_; ++i) {
      input_mats_c_.emplace_back(
          cv::Mat(net_input_height_, net_input_width_, CV_8UC3));
    }
  }
}

void DefaultInputEvaluator::evaluate(
    const std::vector<std::vector<u8*>>& input_buffers,
    const std::vector<std::vector<size_t>>& input_sizes,
    std::vector<std::vector<u8*>>& output_buffers,
    std::vector<std::vector<size_t>>& output_sizes) {
  auto eval_start = now();

  size_t frame_size = net_input_width_ * net_input_height_ * 3 * sizeof(float);
  i32 input_count = input_buffers[0].size();

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    streams_.resize(0);
    streams_.resize(num_cuda_streams_);

    for (i32 frame = 0; frame < input_count; frame += batch_size_) {
      i32 batch_count = std::min(input_count - frame, batch_size_);

      f32* net_input = nullptr;
      i32 net_input_size = frame_size * batch_count;
      cudaMalloc((void**)&net_input, net_input_size);

      for (i32 i = 0; i < batch_count; ++i) {
        int sid = i % num_cuda_streams_;
        cv::cuda::Stream& cv_stream = streams_[sid];

        u8* buffer = input_buffers[0][frame + i];
        frame_input_g_[sid] = cv::cuda::GpuMat(
            net_input_height_, net_input_width_, CV_8UC3, buffer);
        cv::cuda::cvtColor(frame_input_g_[sid], frame_input_g_[sid],
                           CV_RGB2BGR);
        frame_input_g_[sid].convertTo(float_input_g_[sid], CV_32FC3, cv_stream);
        cv::cuda::subtract(float_input_g_[sid], mean_mat_g_,
                           meanshifted_input_g_[sid], cv::noArray(), -1,
                           cv_stream);
        cv::cuda::divide(meanshifted_input_g_[sid], 255.0,
                         normalized_input_g_[sid]);
        // Changed from interleaved RGB to planar RGB
        cv::cuda::split(normalized_input_g_[sid], input_planes_g_[sid],
                        cv_stream);
        auto& plane1 = input_planes_g_[sid][0];
        auto& plane2 = input_planes_g_[sid][1];
        auto& plane3 = input_planes_g_[sid][2];
        auto& planar_input = planar_input_g_[sid];
        plane1.copyTo(planar_input(cv::Rect(
            0, net_input_width_ * 0, net_input_height_, net_input_width_)));
        plane2.copyTo(planar_input(cv::Rect(
            0, net_input_width_ * 1, net_input_height_, net_input_width_)));
        plane3.copyTo(planar_input(cv::Rect(
            0, net_input_width_ * 2, net_input_height_, net_input_width_)));
        assert(planar_input.cols == net_input_height_);
        cudaStream_t s = cv::cuda::StreamAccessor::getStream(cv_stream);
        CU_CHECK(cudaMemcpy2DAsync(
            net_input + i * (net_input_width_ * net_input_height_ * 3),
            net_input_height_ * sizeof(float), planar_input.data,
            planar_input.step, net_input_height_ * sizeof(float),
            net_input_width_ * 3, cudaMemcpyDeviceToDevice, s));
      }
      for (cv::cuda::Stream& s : streams_) {
        s.waitForCompletion();
      }

      output_buffers[0].push_back((u8*)net_input);
      output_sizes[0].push_back(net_input_size);
    }

    i32 num_batches = output_buffers[0].size();
    for (i32 i = 0; i < input_buffers[0].size() - num_batches; ++i) {
      void* buf;
      cudaMalloc(&buf, 1);
      output_buffers[0].push_back((u8*)buf);
      output_sizes[0].push_back(1);
    }
#else
    LOG(FATAL) << "Not built with CUDA support.";
#endif  // HAVE_CUDA
  } else {
    for (i32 frame = 0; frame < input_count; frame += batch_size_) {
      i32 batch_count = std::min(input_count - frame, batch_size_);

      i32 frame_width = metadata_.width();
      i32 frame_height = metadata_.height();

      for (i32 i = 0; i < batch_count; ++i) {
        u8* buffer = input_buffers[0][frame + i];
        cv::Mat input_mat(frame_height, frame_width, CV_8UC3, buffer);

        cv::resize(input_mat, resized_input_c_,
                   cv::Size(net_input_width_, net_input_height_), 0, 0,
                   cv::INTER_LINEAR);
        cv::cvtColor(resized_input_c_, input_mats_c_[i], CV_RGB2BGR);
      }

      std::vector<cv::Mat> input_mats_slice(
          input_mats_c_.begin(), input_mats_c_.begin() + batch_count);

      u8* net_input = new u8[frame_size * batch_count];
      output_blob_.set_cpu_data((f32*)net_input);
      output_blob_.Reshape(input_mats_slice.size(), output_blob_.shape(1),
                           output_blob_.shape(2), output_blob_.shape(3));
      transformer_->Transform(input_mats_slice, &output_blob_);

      output_buffers[0].push_back(net_input);
      output_sizes[0].push_back(frame_size * batch_count);
    }

    i32 num_batches = output_buffers[0].size();
    for (i32 i = 0; i < input_buffers[0].size() - num_batches; ++i) {
      output_buffers[0].push_back(new u8[1]);
      output_sizes[0].push_back(1);
    }
  }

  for (i32 i = 0; i < input_buffers[0].size(); ++i) {
    size_t size = input_sizes[0][i];
    u8* buffer = new_buffer(device_type_, device_id_, size);
    memcpy_buffer(buffer, device_type_, device_id_, input_buffers[0][i],
                  device_type_, device_id_, size);
    output_buffers[1].push_back(buffer);
    output_sizes[1].push_back(size);
  }

  if (profiler_) {
    profiler_->add_interval("caffe:transform_input", eval_start, now());
  }
}

DefaultInputEvaluatorFactory::DefaultInputEvaluatorFactory(
    DeviceType device_type, const NetDescriptor& descriptor, i32 batch_size)
    : device_type_(device_type),
      net_descriptor_(descriptor),
      batch_size_(batch_size) {}

EvaluatorCapabilities DefaultInputEvaluatorFactory::get_capabilities() {
  EvaluatorCapabilities caps;
  caps.device_type = device_type_;
  if (device_type_ == DeviceType::GPU) {
    caps.max_devices = 1;
  } else {
    caps.max_devices = EvaluatorCapabilities::UnlimitedDevices;
  }
  caps.warmup_size = 0;
  return caps;
}

std::vector<std::string> DefaultInputEvaluatorFactory::get_output_names() {
  return {"net_input", "frame"};
}

Evaluator* DefaultInputEvaluatorFactory::new_evaluator(
    const EvaluatorConfig& config) {
  return new DefaultInputEvaluator(device_type_, config.device_ids[0],
                                   net_descriptor_, batch_size_);
}
}
<commit_msg>make_unique only supported by C++14 compilers<commit_after>/* Copyright 2016 Carnegie Mellon University
 *
 * 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 "scanner/evaluators/caffe/default/default_input_evaluator.h"
#include "scanner/util/memory.h"

#ifdef HAVE_CUDA
#include <opencv2/cudaimgproc.hpp>
#endif

namespace scanner {

DefaultInputEvaluator::DefaultInputEvaluator(DeviceType device_type,
                                             i32 device_id,
                                             const NetDescriptor& descriptor,
                                             i32 batch_size)
    : device_type_(device_type),
      device_id_(device_id),
      descriptor_(descriptor),
      batch_size_(batch_size)
#ifdef HAVE_CUDA
      ,
      num_cuda_streams_(32),
      streams_(num_cuda_streams_)
#endif

{
  if (descriptor_.input_width != -1) {
    net_input_width_ = descriptor_.input_width;
    net_input_height_ = descriptor_.input_height;
  } else {
    net_input_width_ = -1;
    net_input_height_ = -1;
  }

  if (device_type_ == DeviceType::CPU) {
    std::vector<float>& mean_colors = descriptor_.mean_colors;
    caffe::TransformationParameter param;
    param.set_force_color(true);
    param.set_scale(1.0 / 255.0);
    for (i32 i = 0; i < mean_colors.size(); i++) {
      param.add_mean_value(mean_colors[i]);
    }
    transformer_.reset(new caffe::DataTransformer<f32>(param, caffe::TEST));
  }
}

void DefaultInputEvaluator::configure(const VideoMetadata& metadata) {
  metadata_ = metadata;

  i32 width = metadata.width();
  i32 height = metadata.height();
  if (net_input_width_ == -1) {
    net_input_width_ = width;
    net_input_height_ = height;
  }

  output_blob_.Reshape(batch_size_, 3, net_input_height_, net_input_width_);

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    cv::cuda::setDevice(device_id_);
    cudaSetDevice(device_id_);

    mean_mat_g_ = cv::cuda::GpuMat(
        net_input_height_, net_input_width_, CV_32FC3,

        cv::Scalar(descriptor_.mean_colors[0], descriptor_.mean_colors[1],
                   descriptor_.mean_colors[2]));

    frame_input_g_.clear();
    float_input_g_.clear();
    normalized_input_g_.clear();
    input_planes_g_.clear();
    planar_input_g_.clear();
    for (size_t i = 0; i < num_cuda_streams_; ++i) {
      frame_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      float_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      meanshifted_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      normalized_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      std::vector<cv::cuda::GpuMat> planes;
      for (i32 i = 0; i < 3; ++i) {
        planes.push_back(
            cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC1));
      }
      input_planes_g_.push_back(planes);
      planar_input_g_.push_back(
          cv::cuda::GpuMat(net_input_width_ * 3, net_input_height_, CV_32FC1));
    }
#else
    LOG(FATAL) << "Not built with Cuda support.";
#endif
  } else {
    resized_input_c_ = cv::Mat(net_input_height_, net_input_width_, CV_8UC3);
    for (i32 i = 0; i < batch_size_; ++i) {
      input_mats_c_.emplace_back(
          cv::Mat(net_input_height_, net_input_width_, CV_8UC3));
    }
  }
}

void DefaultInputEvaluator::evaluate(
    const std::vector<std::vector<u8*>>& input_buffers,
    const std::vector<std::vector<size_t>>& input_sizes,
    std::vector<std::vector<u8*>>& output_buffers,
    std::vector<std::vector<size_t>>& output_sizes) {
  auto eval_start = now();

  size_t frame_size = net_input_width_ * net_input_height_ * 3 * sizeof(float);
  i32 input_count = input_buffers[0].size();

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    streams_.resize(0);
    streams_.resize(num_cuda_streams_);

    for (i32 frame = 0; frame < input_count; frame += batch_size_) {
      i32 batch_count = std::min(input_count - frame, batch_size_);

      f32* net_input = nullptr;
      i32 net_input_size = frame_size * batch_count;
      cudaMalloc((void**)&net_input, net_input_size);

      for (i32 i = 0; i < batch_count; ++i) {
        int sid = i % num_cuda_streams_;
        cv::cuda::Stream& cv_stream = streams_[sid];

        u8* buffer = input_buffers[0][frame + i];
        frame_input_g_[sid] = cv::cuda::GpuMat(
            net_input_height_, net_input_width_, CV_8UC3, buffer);
        cv::cuda::cvtColor(frame_input_g_[sid], frame_input_g_[sid],
                           CV_RGB2BGR);
        frame_input_g_[sid].convertTo(float_input_g_[sid], CV_32FC3, cv_stream);
        cv::cuda::subtract(float_input_g_[sid], mean_mat_g_,
                           meanshifted_input_g_[sid], cv::noArray(), -1,
                           cv_stream);
        cv::cuda::divide(meanshifted_input_g_[sid], 255.0,
                         normalized_input_g_[sid]);
        // Changed from interleaved RGB to planar RGB
        cv::cuda::split(normalized_input_g_[sid], input_planes_g_[sid],
                        cv_stream);
        auto& plane1 = input_planes_g_[sid][0];
        auto& plane2 = input_planes_g_[sid][1];
        auto& plane3 = input_planes_g_[sid][2];
        auto& planar_input = planar_input_g_[sid];
        plane1.copyTo(planar_input(cv::Rect(
            0, net_input_width_ * 0, net_input_height_, net_input_width_)));
        plane2.copyTo(planar_input(cv::Rect(
            0, net_input_width_ * 1, net_input_height_, net_input_width_)));
        plane3.copyTo(planar_input(cv::Rect(
            0, net_input_width_ * 2, net_input_height_, net_input_width_)));
        assert(planar_input.cols == net_input_height_);
        cudaStream_t s = cv::cuda::StreamAccessor::getStream(cv_stream);
        CU_CHECK(cudaMemcpy2DAsync(
            net_input + i * (net_input_width_ * net_input_height_ * 3),
            net_input_height_ * sizeof(float), planar_input.data,
            planar_input.step, net_input_height_ * sizeof(float),
            net_input_width_ * 3, cudaMemcpyDeviceToDevice, s));
      }
      for (cv::cuda::Stream& s : streams_) {
        s.waitForCompletion();
      }

      output_buffers[0].push_back((u8*)net_input);
      output_sizes[0].push_back(net_input_size);
    }

    i32 num_batches = output_buffers[0].size();
    for (i32 i = 0; i < input_buffers[0].size() - num_batches; ++i) {
      void* buf;
      cudaMalloc(&buf, 1);
      output_buffers[0].push_back((u8*)buf);
      output_sizes[0].push_back(1);
    }
#else
    LOG(FATAL) << "Not built with CUDA support.";
#endif  // HAVE_CUDA
  } else {
    for (i32 frame = 0; frame < input_count; frame += batch_size_) {
      i32 batch_count = std::min(input_count - frame, batch_size_);

      i32 frame_width = metadata_.width();
      i32 frame_height = metadata_.height();

      for (i32 i = 0; i < batch_count; ++i) {
        u8* buffer = input_buffers[0][frame + i];
        cv::Mat input_mat(frame_height, frame_width, CV_8UC3, buffer);

        cv::resize(input_mat, resized_input_c_,
                   cv::Size(net_input_width_, net_input_height_), 0, 0,
                   cv::INTER_LINEAR);
        cv::cvtColor(resized_input_c_, input_mats_c_[i], CV_RGB2BGR);
      }

      std::vector<cv::Mat> input_mats_slice(
          input_mats_c_.begin(), input_mats_c_.begin() + batch_count);

      u8* net_input = new u8[frame_size * batch_count];
      output_blob_.set_cpu_data((f32*)net_input);
      output_blob_.Reshape(input_mats_slice.size(), output_blob_.shape(1),
                           output_blob_.shape(2), output_blob_.shape(3));
      transformer_->Transform(input_mats_slice, &output_blob_);

      output_buffers[0].push_back(net_input);
      output_sizes[0].push_back(frame_size * batch_count);
    }

    i32 num_batches = output_buffers[0].size();
    for (i32 i = 0; i < input_buffers[0].size() - num_batches; ++i) {
      output_buffers[0].push_back(new u8[1]);
      output_sizes[0].push_back(1);
    }
  }

  for (i32 i = 0; i < input_buffers[0].size(); ++i) {
    size_t size = input_sizes[0][i];
    u8* buffer = new_buffer(device_type_, device_id_, size);
    memcpy_buffer(buffer, device_type_, device_id_, input_buffers[0][i],
                  device_type_, device_id_, size);
    output_buffers[1].push_back(buffer);
    output_sizes[1].push_back(size);
  }

  if (profiler_) {
    profiler_->add_interval("caffe:transform_input", eval_start, now());
  }
}

DefaultInputEvaluatorFactory::DefaultInputEvaluatorFactory(
    DeviceType device_type, const NetDescriptor& descriptor, i32 batch_size)
    : device_type_(device_type),
      net_descriptor_(descriptor),
      batch_size_(batch_size) {}

EvaluatorCapabilities DefaultInputEvaluatorFactory::get_capabilities() {
  EvaluatorCapabilities caps;
  caps.device_type = device_type_;
  if (device_type_ == DeviceType::GPU) {
    caps.max_devices = 1;
  } else {
    caps.max_devices = EvaluatorCapabilities::UnlimitedDevices;
  }
  caps.warmup_size = 0;
  return caps;
}

std::vector<std::string> DefaultInputEvaluatorFactory::get_output_names() {
  return {"net_input", "frame"};
}

Evaluator* DefaultInputEvaluatorFactory::new_evaluator(
    const EvaluatorConfig& config) {
  return new DefaultInputEvaluator(device_type_, config.device_ids[0],
                                   net_descriptor_, batch_size_);
}
}
<|endoftext|>


<commit_before>//
// Copyright (C) 2006 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2006 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
//////////////////////////////////////////////////////////////////////////////

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <sipxunit/TestUtilities.h>

#include "mp/MpInputDeviceManager.h"
#include "mp/MprFromInputDevice.h"
#include "mp/MpOutputDeviceManager.h"
#include "mp/MprToOutputDevice.h"
#include "mp/MpFlowGraphBase.h"
#include "mp/MpMisc.h"
#include "os/OsTask.h"
#ifdef RTL_ENABLED
#  include <rtl_macro.h>
#else
#  define RTL_BLOCK(x)
#  define RTL_EVENT(x,y)
#endif

#include <os/OsFS.h>

#define TEST_TIME_MS                  10000  ///< Time to runs test (in milliseconds)
#define AUDIO_BUFS_NUM                500   ///< Number of buffers in buffer pool we'll use.
#define BUFFERS_TO_BUFFER_ON_INPUT    3     ///< Number of buffers to buffer in input manager.
#define BUFFERS_TO_BUFFER_ON_OUTPUT   0     ///< Number of buffers to buffer in output manager.

#define TEST_SAMPLES_PER_FRAME        80    ///< in samples
#define TEST_SAMPLES_PER_SECOND       8000  ///< in samples/sec (Hz)

#define BUFFER_ON_OUTPUT_MS           (BUFFERS_TO_BUFFER_ON_OUTPUT*TEST_SAMPLES_PER_FRAME*1000/TEST_SAMPLES_PER_SECOND)
                                            ///< Buffer size in output manager in milliseconds.

//#define USE_TEST_INPUT_DRIVER
//#define USE_TEST_OUTPUT_DRIVER

#ifdef USE_TEST_INPUT_DRIVER // USE_TEST_DRIVER [
#error Not supported platform!

#elif defined(WIN32) // USE_TEST_DRIVER ][ WIN32
#include <mp/MpInputDeviceDriverWnt.h>
#define INPUT_DRIVER MpInputDeviceDriverWnt
#define INPUT_DRIVER_CONSTRUCTOR_PARAMS(manager) "SoundMAX HD Audio", (manager)

#elif defined(__pingtel_on_posix__) // WIN32 ][ __pingtel_on_posix__
#include <mp/MpidOSS.h>
#define INPUT_DRIVER MpidOSS
#define INPUT_DRIVER_CONSTRUCTOR_PARAMS(manager) "/dev/dsp", (manager)

#else // __pingtel_on_possix__ ]
#error Unknown platform!
#endif

#ifdef USE_TEST_OUTPUT_DRIVER // USE_TEST_DRIVER [
#include <mp/MpodBufferRecorder.h>
#define OUTPUT_DRIVER MpodBufferRecorder
#define OUTPUT_DRIVER_CONSTRUCTOR_PARAMS "default", TEST_TIME_MS

#elif defined(WIN32) // USE_TEST_DRIVER ][ WIN32
#error No output driver for Windows exist!

#elif defined(__pingtel_on_posix__) // WIN32 ][ __pingtel_on_posix__
#include <mp/MpodOSS.h>
#define OUTPUT_DRIVER MpodOSS
#define OUTPUT_DRIVER_CONSTRUCTOR_PARAMS "/dev/dsp"

#else // __pingtel_on_possix__ ]
#error Unknown platform!
#endif

///  Unit test for MprSplitter
class MpInputOutputFrameworkTest : public CppUnit::TestCase
{
   CPPUNIT_TEST_SUITE(MpInputOutputFrameworkTest);
   CPPUNIT_TEST(testShortCircuit);
   CPPUNIT_TEST_SUITE_END();

protected:
   MpFlowGraphBase*  mpFlowGraph; ///< Flowgraph for our fromInputDevice and
                              ///< toOutputDevice resources.

public:

   void setUp()
   {
      // Setup media task
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           mpStartUp(TEST_SAMPLES_PER_SECOND, TEST_SAMPLES_PER_FRAME, 6*10, 0));

      mpFlowGraph = new MpFlowGraphBase( TEST_SAMPLES_PER_FRAME
                                       , TEST_SAMPLES_PER_SECOND);
      CPPUNIT_ASSERT(mpFlowGraph != NULL);
   }

   void tearDown()
   {
      // This should normally be done by haltFramework, but if we aborted due
      // to an assertion the flowgraph will need to be shutdown here
      if (mpFlowGraph && mpFlowGraph->isStarted())
      {
         osPrintf("WARNING: flowgraph found still running, shutting down\n");

         // ignore the result and keep going
         mpFlowGraph->stop();

         // Request processing of another frame so that the STOP_FLOWGRAPH
         // message gets handled
         mpFlowGraph->processNextFrame();
      }

      // Free flowgraph resources
      delete mpFlowGraph;
      mpFlowGraph = NULL;

      // Clear all Media Tasks data
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpShutdown());
   }

   void testShortCircuit()
   {
#ifdef RTL_ENABLED
      RTL_START(1000000);
#endif

      // Create input and output device managers
      MpInputDeviceManager inputDeviceManager(TEST_SAMPLES_PER_FRAME,
                                              TEST_SAMPLES_PER_SECOND,
                                              BUFFERS_TO_BUFFER_ON_INPUT,
                                              *MpMisc.RawAudioPool);
      MpOutputDeviceManager outputDeviceManager(TEST_SAMPLES_PER_FRAME,
                                                TEST_SAMPLES_PER_SECOND,
                                                BUFFER_ON_OUTPUT_MS);

      // Create source (input) device and add it to manager.
      INPUT_DRIVER sourceDevice(INPUT_DRIVER_CONSTRUCTOR_PARAMS(inputDeviceManager));
      MpInputDeviceHandle  sourceDeviceId = inputDeviceManager.addDevice(sourceDevice);
      CPPUNIT_ASSERT(sourceDeviceId > 0);
      CPPUNIT_ASSERT(!inputDeviceManager.isDeviceEnabled(sourceDeviceId));

      // Create sink (output) device and add it to manager.
      OUTPUT_DRIVER sinkDevice(OUTPUT_DRIVER_CONSTRUCTOR_PARAMS);
      MpOutputDeviceHandle  sinkDeviceId = outputDeviceManager.addDevice(&sinkDevice);
      CPPUNIT_ASSERT(sinkDeviceId > 0);
      CPPUNIT_ASSERT(!outputDeviceManager.isDeviceEnabled(sinkDeviceId));

      // Create source (input) and sink (output) resources.
      MprFromInputDevice pSource("MprFromInputDevice",
                                 TEST_SAMPLES_PER_FRAME,
                                 TEST_SAMPLES_PER_SECOND,
                                 &inputDeviceManager,
                                 sourceDeviceId);
      MprToOutputDevice pSink("MprToOutputDevice",
                              TEST_SAMPLES_PER_FRAME,
                              TEST_SAMPLES_PER_SECOND,
                              &outputDeviceManager,
                              sinkDeviceId);

      // Add source and sink resources to flowgraph and link them together.
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->addResource(pSource));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->addResource(pSink));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->addLink(pSource, 0, pSink, 0));

      // Enable devices
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           inputDeviceManager.enableDevice(sourceDeviceId));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           outputDeviceManager.enableDevice(sinkDeviceId));

      // Enable resources
      CPPUNIT_ASSERT(pSource.enable());
      CPPUNIT_ASSERT(pSink.enable());

      mpFlowGraph->start();

      // Run test!
      for (int i=0; i<TEST_TIME_MS*(TEST_SAMPLES_PER_SECOND/TEST_SAMPLES_PER_FRAME)/1000; i++)
      {
         RTL_BLOCK("test loop body");
         printf("==> i=%d\n",i);
         OsTask::delay(TEST_SAMPLES_PER_FRAME*1000/TEST_SAMPLES_PER_SECOND-2);
         RTL_EVENT("test loop body", 2);
         mpFlowGraph->processNextFrame();
      }

      // Disable resources
      CPPUNIT_ASSERT(pSource.disable());
      CPPUNIT_ASSERT(pSink.disable());
      mpFlowGraph->processNextFrame();

      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->stop());
      mpFlowGraph->processNextFrame();

#ifdef USE_TEST_OUTPUT_DRIVER // [
      OsFile::openAndWrite("capture.raw",
                           (const char*)sinkDevice.getBufferData(),
                           sinkDevice.getBufferLength()*sizeof(MpAudioSample));
#endif // USE_TEST_OUTPUT_DRIVER ]

      // Disable devices
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           inputDeviceManager.disableDevice(sourceDeviceId));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           outputDeviceManager.disableDevice(sinkDeviceId));

      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->removeResource(pSink));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->removeResource(pSource));
      mpFlowGraph->processNextFrame();

      OsTask::delay(10);

#ifdef RTL_ENABLED
      RTL_WRITE("testShortCircuit.rtl");
#endif

   }
};

CPPUNIT_TEST_SUITE_REGISTRATION(MpInputOutputFrameworkTest);
<commit_msg>MpInputOutputFrameworkTest updated to support new native flowgraph ticker.<commit_after>//
// Copyright (C) 2006 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2006 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
//////////////////////////////////////////////////////////////////////////////

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <sipxunit/TestUtilities.h>

#include "mp/MpInputDeviceManager.h"
#include "mp/MprFromInputDevice.h"
#include "mp/MpOutputDeviceManager.h"
#include "mp/MprToOutputDevice.h"
#include "mp/MpFlowGraphBase.h"
#include "mp/MpMisc.h"
#include "mp/MpMediaTask.h"
#include "os/OsTask.h"
#ifdef RTL_ENABLED
#  include <rtl_macro.h>
#else
#  define RTL_BLOCK(x)
#  define RTL_EVENT(x,y)
#endif

#include <os/OsFS.h>

#define TEST_TIME_MS                  10000  ///< Time to runs test (in milliseconds)
#define AUDIO_BUFS_NUM                500   ///< Number of buffers in buffer pool we'll use.
#define BUFFERS_TO_BUFFER_ON_INPUT    3     ///< Number of buffers to buffer in input manager.
#define BUFFERS_TO_BUFFER_ON_OUTPUT   0     ///< Number of buffers to buffer in output manager.

#define TEST_SAMPLES_PER_FRAME        80    ///< in samples
#define TEST_SAMPLES_PER_SECOND       8000  ///< in samples/sec (Hz)

#define BUFFER_ON_OUTPUT_MS           (BUFFERS_TO_BUFFER_ON_OUTPUT*TEST_SAMPLES_PER_FRAME*1000/TEST_SAMPLES_PER_SECOND)
                                            ///< Buffer size in output manager in milliseconds.

//#define USE_TEST_INPUT_DRIVER
//#define USE_TEST_OUTPUT_DRIVER

#ifdef USE_TEST_INPUT_DRIVER // USE_TEST_DRIVER [
#error Not supported platform!

#elif defined(WIN32) // USE_TEST_DRIVER ][ WIN32
#include <mp/MpInputDeviceDriverWnt.h>
#define INPUT_DRIVER MpInputDeviceDriverWnt
#define INPUT_DRIVER_CONSTRUCTOR_PARAMS(manager) "SoundMAX HD Audio", (manager)

#elif defined(__pingtel_on_posix__) // WIN32 ][ __pingtel_on_posix__
#include <mp/MpidOSS.h>
#define INPUT_DRIVER MpidOSS
#define INPUT_DRIVER_CONSTRUCTOR_PARAMS(manager) "/dev/dsp", (manager)

#else // __pingtel_on_possix__ ]
#error Unknown platform!
#endif

#ifdef USE_TEST_OUTPUT_DRIVER // USE_TEST_DRIVER [
#include <mp/MpodBufferRecorder.h>
#define OUTPUT_DRIVER MpodBufferRecorder
#define OUTPUT_DRIVER_CONSTRUCTOR_PARAMS "default", TEST_TIME_MS

#elif defined(WIN32) // USE_TEST_DRIVER ][ WIN32
#error No output driver for Windows exist!

#elif defined(__pingtel_on_posix__) // WIN32 ][ __pingtel_on_posix__
#include <mp/MpodOSS.h>
#define OUTPUT_DRIVER MpodOSS
#define OUTPUT_DRIVER_CONSTRUCTOR_PARAMS "/dev/dsp"

#else // __pingtel_on_possix__ ]
#error Unknown platform!
#endif

///  Unit test for MprSplitter
class MpInputOutputFrameworkTest : public CppUnit::TestCase
{
   CPPUNIT_TEST_SUITE(MpInputOutputFrameworkTest);
   CPPUNIT_TEST(testShortCircuit);
   CPPUNIT_TEST_SUITE_END();

protected:
   MpFlowGraphBase*  mpFlowGraph; ///< Flowgraph for our fromInputDevice and
                              ///< toOutputDevice resources.

public:

   void setUp()
   {
      // Setup media task
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           mpStartUp(TEST_SAMPLES_PER_SECOND, TEST_SAMPLES_PER_FRAME, 6*10, 0));

      mpFlowGraph = new MpFlowGraphBase( TEST_SAMPLES_PER_FRAME
                                       , TEST_SAMPLES_PER_SECOND);
      CPPUNIT_ASSERT(mpFlowGraph != NULL);

      // Call getMediaTask() which causes the task to get instantiated
      CPPUNIT_ASSERT(MpMediaTask::getMediaTask(10) != NULL);
   }

   void tearDown()
   {
      // This should normally be done by haltFramework, but if we aborted due
      // to an assertion the flowgraph will need to be shutdown here
      if (mpFlowGraph && mpFlowGraph->isStarted())
      {
         osPrintf("WARNING: flowgraph found still running, shutting down\n");

         // ignore the result and keep going
         mpFlowGraph->stop();

         // Request processing of another frame so that the STOP_FLOWGRAPH
         // message gets handled
         mpFlowGraph->processNextFrame();
      }

      // Free flowgraph resources
      delete mpFlowGraph;
      mpFlowGraph = NULL;

      // Clear all Media Tasks data
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpShutdown());
   }

   void testShortCircuit()
   {
      enableConsoleOutput(1);

#ifdef RTL_ENABLED
      RTL_START(1000000);
#endif

      // Get media processing task.
      MpMediaTask *pMediaTask = MpMediaTask::getMediaTask(10);

      // Create input and output device managers
      MpInputDeviceManager inputDeviceManager(TEST_SAMPLES_PER_FRAME,
                                              TEST_SAMPLES_PER_SECOND,
                                              BUFFERS_TO_BUFFER_ON_INPUT,
                                              *MpMisc.RawAudioPool);
      MpOutputDeviceManager outputDeviceManager(TEST_SAMPLES_PER_FRAME,
                                                TEST_SAMPLES_PER_SECOND,
                                                BUFFER_ON_OUTPUT_MS);

      // Create source (input) device and add it to manager.
      INPUT_DRIVER sourceDevice(INPUT_DRIVER_CONSTRUCTOR_PARAMS(inputDeviceManager));
      MpInputDeviceHandle  sourceDeviceId = inputDeviceManager.addDevice(sourceDevice);
      CPPUNIT_ASSERT(sourceDeviceId > 0);
      CPPUNIT_ASSERT(!inputDeviceManager.isDeviceEnabled(sourceDeviceId));

      // Create sink (output) device and add it to manager.
      OUTPUT_DRIVER sinkDevice(OUTPUT_DRIVER_CONSTRUCTOR_PARAMS);
      MpOutputDeviceHandle  sinkDeviceId = outputDeviceManager.addDevice(&sinkDevice);
      CPPUNIT_ASSERT(sinkDeviceId > 0);
      CPPUNIT_ASSERT(!outputDeviceManager.isDeviceEnabled(sinkDeviceId));

      // Create source (input) and sink (output) resources.
      MprFromInputDevice pSource("MprFromInputDevice",
                                 TEST_SAMPLES_PER_FRAME,
                                 TEST_SAMPLES_PER_SECOND,
                                 &inputDeviceManager,
                                 sourceDeviceId);
      MprToOutputDevice pSink("MprToOutputDevice",
                              TEST_SAMPLES_PER_FRAME,
                              TEST_SAMPLES_PER_SECOND,
                              &outputDeviceManager,
                              sinkDeviceId);

      // Add source and sink resources to flowgraph and link them together.
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->addResource(pSource));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->addResource(pSink));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->addLink(pSource, 0, pSink, 0));

      // Enable devices
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           inputDeviceManager.enableDevice(sourceDeviceId));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           outputDeviceManager.enableDevice(sinkDeviceId));

      // Set flowgraph ticker
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           outputDeviceManager.setFlowgraphTickerSource(sinkDeviceId));

      // Enable resources
      CPPUNIT_ASSERT(pSource.enable());
      CPPUNIT_ASSERT(pSink.enable());

      // Manage flowgraph with media task.
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, pMediaTask->manageFlowGraph(*mpFlowGraph));

      // Start flowgraph
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, pMediaTask->startFlowGraph(*mpFlowGraph));

      // Run test!
      OsTask::delay(TEST_TIME_MS);
/*      for (int i=0; i<TEST_TIME_MS*(TEST_SAMPLES_PER_SECOND/TEST_SAMPLES_PER_FRAME)/1000; i++)
      {
         RTL_BLOCK("test loop body");
         printf("==> i=%d\n",i);
         OsTask::delay(TEST_SAMPLES_PER_FRAME*1000/TEST_SAMPLES_PER_SECOND-2);
         RTL_EVENT("test loop body", 2);
         MpMediaTask::signalFrameStart();
      }
*/

      // Stop flowgraph
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, pMediaTask->stopFlowGraph(*mpFlowGraph));
      OsTask::delay(20);

      // Unmanage flowgraph with media task.
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, pMediaTask->unmanageFlowGraph(*mpFlowGraph));

      // Disable resources
      CPPUNIT_ASSERT(pSource.disable());
      CPPUNIT_ASSERT(pSink.disable());
      mpFlowGraph->processNextFrame();

      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->stop());
      mpFlowGraph->processNextFrame();

#ifdef USE_TEST_OUTPUT_DRIVER // [
      OsFile::openAndWrite("capture.raw",
                           (const char*)sinkDevice.getBufferData(),
                           sinkDevice.getBufferLength()*sizeof(MpAudioSample));
#endif // USE_TEST_OUTPUT_DRIVER ]

      // Disable devices
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           inputDeviceManager.disableDevice(sourceDeviceId));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS,
                           outputDeviceManager.disableDevice(sinkDeviceId));

      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->removeResource(pSink));
      CPPUNIT_ASSERT_EQUAL(OS_SUCCESS, mpFlowGraph->removeResource(pSource));
      mpFlowGraph->processNextFrame();

      OsTask::delay(10);

#ifdef RTL_ENABLED
      RTL_WRITE("testShortCircuit.rtl");
#endif

   }
};

CPPUNIT_TEST_SUITE_REGISTRATION(MpInputOutputFrameworkTest);
<|endoftext|>


<commit_before>#include "iostream"
#include "class_double_list.h"

int main(){
    List*head, *cur;
    int num;
    cout << "a=  ";
    cin >> num;
    head = createlist(num);
    cur = head;
    for (int i = 0; i < 9; i++) {
        cout << "a = ";
        cin >> num;
        cur = addToend(cur, num);
    }
    printlist(head);
    cout << endl;
    // Удаляем элемент со значением 3
    deleteElem(&head, 3);
    printlist(head);
    cout << endl;
    //Добавление элемента co значением 4 после элемента со значением 6
    addTomiddle(&head, 4, 6);
    printlist(head);
    cout << endl;
    // Сортировка элементов по возрастанию
    sort(&head);
    printlist(head);

    return 0;
}

<commit_msg>make new code<commit_after>#include "stdafx.h"
#include "double_linked_list.h"

int main()
{
	int num;
	double_linked_list list;
	for (int i = 1; i < 10; i++){
		cout << "a= ";
		cin >> num;
		list.append(num);
	}
	list.printlist();
	//Удаляем элемент со значением 4;
	list.deleteElem(4);
	list.printlist();
	//Добавление элемента со значением 9 после 5;
	list.insert(9, 5);
	list.printlist();
	//Сортировка списка по возрастанию
	list.sort();
	list.printlist();
	system("pause");
    return 0;
}
<|endoftext|>


<commit_before>/*
 * Vulkan
 *
 * Copyright (C) 2015 LunarG, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <map>
#include <unordered_map>
#include <map>
#include <vector>
#include "loader_platform.h"
#include "vk_dispatch_table_helper.h"
#include "vkLayer.h"
// The following is #included again to catch certain OS-specific functions
// being used:
#include "loader_platform.h"

#include "SPIRV/spirv.h"

static std::unordered_map<void *, VkLayerDispatchTable *> tableMap;

struct shader_source {
    std::vector<uint32_t> words;

    shader_source(VkShaderCreateInfo const *pCreateInfo) :
        words((uint32_t *)pCreateInfo->pCode, (uint32_t *)pCreateInfo->pCode + pCreateInfo->codeSize / sizeof(uint32_t)) {
    }
};

static std::unordered_map<void *, shader_source *> shader_map;

static VkLayerDispatchTable * initLayerTable(const VkBaseLayerObject *gpuw)
{
    VkLayerDispatchTable *pTable;

    assert(gpuw);
    std::unordered_map<void *, VkLayerDispatchTable *>::const_iterator it = tableMap.find((void *) gpuw->baseObject);
    if (it == tableMap.end())
    {
        pTable =  new VkLayerDispatchTable;
        tableMap[(void *) gpuw->baseObject] = pTable;
    } else
    {
        return it->second;
    }

    layer_initialize_dispatch_table(pTable, gpuw->pGPA, (VkPhysicalGpu) gpuw->nextObject);

    return pTable;
}

VK_LAYER_EXPORT VkResult VKAPI vkCreateDevice(VkPhysicalGpu gpu, const VkDeviceCreateInfo* pCreateInfo, VkDevice* pDevice)
{
    VkLayerDispatchTable* pTable = tableMap[gpu];
    VkResult result = pTable->CreateDevice(gpu, pCreateInfo, pDevice);
    // create a mapping for the device object into the dispatch table
    tableMap.emplace(*pDevice, pTable);
    return result;
}

VK_LAYER_EXPORT VkResult VKAPI vkEnumerateLayers(VkPhysicalGpu gpu, size_t maxLayerCount, size_t maxStringSize, size_t* pOutLayerCount, char* const* pOutLayers, void* pReserved)
{
    if (pOutLayerCount == NULL || pOutLayers == NULL || pOutLayers[0] == NULL || pOutLayers[1] == NULL || pReserved == NULL)
        return VK_ERROR_INVALID_POINTER;

    if (maxLayerCount < 1)
        return VK_ERROR_INITIALIZATION_FAILED;
    *pOutLayerCount = 1;
    strncpy((char *) pOutLayers[0], "ShaderChecker", maxStringSize);
    return VK_SUCCESS;
}

struct extProps {
    uint32_t version;
    const char * const name;
};
#define SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE 1
static const struct extProps shaderCheckerExts[SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE] = {
    // TODO what is the version?
    0x10, "ShaderChecker",
};

VK_LAYER_EXPORT VkResult VKAPI vkGetGlobalExtensionInfo(
                                               VkExtensionInfoType infoType,
                                               uint32_t extensionIndex,
                                               size_t*  pDataSize,
                                               void*    pData)
{
    VkResult result;

    /* This entrypoint is NOT going to init it's own dispatch table since loader calls here early */
    VkExtensionProperties *ext_props;
    uint32_t *count;

    if (pDataSize == NULL)
        return VK_ERROR_INVALID_POINTER;

    switch (infoType) {
        case VK_EXTENSION_INFO_TYPE_COUNT:
            *pDataSize = sizeof(uint32_t);
            if (pData == NULL)
                return VK_SUCCESS;
            count = (uint32_t *) pData;
            *count = SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE;
            break;
        case VK_EXTENSION_INFO_TYPE_PROPERTIES:
            *pDataSize = sizeof(VkExtensionProperties);
            if (pData == NULL)
                return VK_SUCCESS;
            if (extensionIndex >= SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE)
                return VK_ERROR_INVALID_VALUE;
            ext_props = (VkExtensionProperties *) pData;
            ext_props->version = shaderCheckerExts[extensionIndex].version;
            strncpy(ext_props->extName, shaderCheckerExts[extensionIndex].name,
                                        VK_MAX_EXTENSION_NAME);
            ext_props->extName[VK_MAX_EXTENSION_NAME - 1] = '\0';
            break;
        default:
            return VK_ERROR_INVALID_VALUE;
    };

    return VK_SUCCESS;
}

static int
value_or_default(std::unordered_map<unsigned, unsigned> const &map, unsigned id, int def)
{
    auto it = map.find(id);
    if (it == map.end())
        return def;
    else
        return it->second;
}

struct interface_var {
    uint32_t id;
    uint32_t type_id;
    /* TODO: collect the name, too? Isn't required to be present. */
};

static void
collect_interface_by_location(shader_source const *src, spv::StorageClass interface,
                              std::map<uint32_t, interface_var> &out,
                              std::map<uint32_t, interface_var> &builtins_out)
{
    unsigned int const *code = (unsigned int const *)&src->words[0];
    size_t size = src->words.size();

    if (code[0] != spv::MagicNumber) {
        printf("Invalid magic.\n");
        return;
    }

    std::unordered_map<unsigned, unsigned> var_locations;
    std::unordered_map<unsigned, unsigned> var_builtins;

    unsigned word = 5;
    while (word < size) {

        unsigned opcode = code[word] & 0x0ffffu;
        unsigned oplen = (code[word] & 0xffff0000u) >> 16;

        /* We consider two interface models: SSO rendezvous-by-location, and
         * builtins. Complain about anything that fits neither model.
         */
        if (opcode == spv::OpDecorate) {
            if (code[word+2] == spv::DecLocation) {
                var_locations[code[word+1]] = code[word+3];
            }

            if (code[word+2] == spv::DecBuiltIn) {
                var_builtins[code[word+1]] = code[word+3];
            }
        }

        /* TODO: handle grouped decorations */
        /* TODO: handle index=1 dual source outputs from FS -- two vars will
         * have the same location, and we DONT want to clobber. */

        if (opcode == spv::OpVariable && code[word+3] == interface) {
            int location = value_or_default(var_locations, code[word+2], -1);
            int builtin = value_or_default(var_builtins, code[word+2], -1);

            if (location == -1 && builtin == -1) {
                /* No location defined, and not bound to an API builtin.
                 * The spec says nothing about how this case works (or doesn't)
                 * for interface matching.
                 */
                printf("WARN: var %d (type %d) in %s interface has no Location or Builtin decoration\n",
                       code[word+2], code[word+1], interface == spv::StorageInput ? "input" : "output");
            }
            else if (location != -1) {
                /* A user-defined interface variable, with a location. */
                interface_var v;
                v.id = code[word+2];
                v.type_id = code[word+1];
                out[location] = v;
            }
            else {
                /* A builtin interface variable */
                interface_var v;
                v.id = code[word+2];
                v.type_id = code[word+1];
                builtins_out[builtin] = v;
            }
        }

        word += oplen;
    }
}

VK_LAYER_EXPORT VkResult VKAPI vkCreateShader(VkDevice device, const VkShaderCreateInfo *pCreateInfo,
                                                   VkShader *pShader)
{
    VkLayerDispatchTable* pTable = tableMap[(VkBaseLayerObject *)device];
    VkResult res = pTable->CreateShader(device, pCreateInfo, pShader);

    shader_map[(VkBaseLayerObject *) *pShader] = new shader_source(pCreateInfo);
    return res;
}

static void
validate_interface_between_stages(shader_source const *producer, char const *producer_name,
                                  shader_source const *consumer, char const *consumer_name)
{
    std::map<uint32_t, interface_var> outputs;
    std::map<uint32_t, interface_var> inputs;

    std::map<uint32_t, interface_var> builtin_outputs;
    std::map<uint32_t, interface_var> builtin_inputs;

    printf("Begin validate_interface_between_stages %s -> %s\n",
           producer_name, consumer_name);

    collect_interface_by_location(producer, spv::StorageOutput, outputs, builtin_outputs);
    collect_interface_by_location(consumer, spv::StorageInput, inputs, builtin_inputs);

    auto a_it = outputs.begin();
    auto b_it = inputs.begin();

    /* maps sorted by key (location); walk them together to find mismatches */
    while (a_it != outputs.end() || b_it != inputs.end()) {
        if (b_it == inputs.end() || a_it->first < b_it->first) {
            printf("  WARN: %s writes to output location %d which is not consumed by %s\n",
                   producer_name, a_it->first, consumer_name);
            a_it++;
        }
        else if (a_it == outputs.end() || a_it->first > b_it->first) {
            printf("  ERR: %s consumes input location %d which is not written by %s\n",
                   consumer_name, b_it->first, producer_name);
            b_it++;
        }
        else {
            printf("  OK: match on location %d\n",
                   a_it->first);
            /* TODO: typecheck */
            a_it++;
            b_it++;
        }
    }

    printf("End validate_interface_between_stages\n");
}

VK_LAYER_EXPORT VkResult VKAPI vkCreateGraphicsPipeline(VkDevice device,
                                                             const VkGraphicsPipelineCreateInfo *pCreateInfo,
                                                             VkPipeline *pPipeline)
{
    /* TODO: run cross-stage validation */
    /* - Validate vertex fetch -> VS interface */
    /* - Validate FS output -> CB */
    /* - Support GS, TCS, TES stages */

    /* We seem to allow pipeline stages to be specified out of order, so collect and identify them
     * before trying to do anything more: */

    shader_source const *vs_source = 0;
    shader_source const *fs_source = 0;
    VkPipelineCbStateCreateInfo const *cb = 0;
    VkPipelineVertexInputCreateInfo const *vi = 0;

    for (auto stage = pCreateInfo; stage; stage = (decltype(stage))stage->pNext) {
        if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO) {
            auto shader_stage = (VkPipelineShaderStageCreateInfo const *)stage;

            if (shader_stage->shader.stage == VK_SHADER_STAGE_VERTEX)
                vs_source = shader_map[(void *)(shader_stage->shader.shader)];
            else if (shader_stage->shader.stage == VK_SHADER_STAGE_FRAGMENT)
                fs_source = shader_map[(void *)(shader_stage->shader.shader)];
            else
                printf("Unknown shader stage %d\n", shader_stage->shader.stage);
        }
        else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO) {
            cb = (VkPipelineCbStateCreateInfo const *)stage;
        }
        else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO) {
            vi = (VkPipelineVertexInputCreateInfo const *)stage;
        }
    }

    printf("Pipeline: vi=%p vs=%p fs=%p cb=%p\n", vi, vs_source, fs_source, cb);

    if (vs_source && fs_source) {
        validate_interface_between_stages(vs_source, "vertex shader",
                                          fs_source, "fragment shader");
    }

    VkLayerDispatchTable *pTable = tableMap[(VkBaseLayerObject *)device];
    VkResult res = pTable->CreateGraphicsPipeline(device, pCreateInfo, pPipeline);
    return res;
}

VK_LAYER_EXPORT void * VKAPI vkGetProcAddr(VkPhysicalGpu gpu, const char* pName)
{
    if (gpu == NULL)
        return NULL;

    initLayerTable((const VkBaseLayerObject *) gpu);

#define ADD_HOOK(fn)    \
    if (!strncmp(#fn, pName, sizeof(#fn))) \
        return (void *) fn

    ADD_HOOK(vkGetProcAddr);
    ADD_HOOK(vkEnumerateLayers);
    ADD_HOOK(vkCreateDevice);
    ADD_HOOK(vkCreateShader);
    ADD_HOOK(vkCreateGraphicsPipeline);

    VkBaseLayerObject* gpuw = (VkBaseLayerObject *) gpu;
    if (gpuw->pGPA == NULL)
        return NULL;
    return gpuw->pGPA((VkPhysicalGpu) gpuw->nextObject, pName);
}
<commit_msg>shader_checker: validate vertex attribs against vs inputs<commit_after>/*
 * Vulkan
 *
 * Copyright (C) 2015 LunarG, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <map>
#include <unordered_map>
#include <map>
#include <vector>
#include "loader_platform.h"
#include "vk_dispatch_table_helper.h"
#include "vkLayer.h"
// The following is #included again to catch certain OS-specific functions
// being used:
#include "loader_platform.h"

#include "SPIRV/spirv.h"

static std::unordered_map<void *, VkLayerDispatchTable *> tableMap;

struct shader_source {
    std::vector<uint32_t> words;

    shader_source(VkShaderCreateInfo const *pCreateInfo) :
        words((uint32_t *)pCreateInfo->pCode, (uint32_t *)pCreateInfo->pCode + pCreateInfo->codeSize / sizeof(uint32_t)) {
    }
};

static std::unordered_map<void *, shader_source *> shader_map;

static VkLayerDispatchTable * initLayerTable(const VkBaseLayerObject *gpuw)
{
    VkLayerDispatchTable *pTable;

    assert(gpuw);
    std::unordered_map<void *, VkLayerDispatchTable *>::const_iterator it = tableMap.find((void *) gpuw->baseObject);
    if (it == tableMap.end())
    {
        pTable =  new VkLayerDispatchTable;
        tableMap[(void *) gpuw->baseObject] = pTable;
    } else
    {
        return it->second;
    }

    layer_initialize_dispatch_table(pTable, gpuw->pGPA, (VkPhysicalGpu) gpuw->nextObject);

    return pTable;
}

VK_LAYER_EXPORT VkResult VKAPI vkCreateDevice(VkPhysicalGpu gpu, const VkDeviceCreateInfo* pCreateInfo, VkDevice* pDevice)
{
    VkLayerDispatchTable* pTable = tableMap[gpu];
    VkResult result = pTable->CreateDevice(gpu, pCreateInfo, pDevice);
    // create a mapping for the device object into the dispatch table
    tableMap.emplace(*pDevice, pTable);
    return result;
}

VK_LAYER_EXPORT VkResult VKAPI vkEnumerateLayers(VkPhysicalGpu gpu, size_t maxLayerCount, size_t maxStringSize, size_t* pOutLayerCount, char* const* pOutLayers, void* pReserved)
{
    if (pOutLayerCount == NULL || pOutLayers == NULL || pOutLayers[0] == NULL || pOutLayers[1] == NULL || pReserved == NULL)
        return VK_ERROR_INVALID_POINTER;

    if (maxLayerCount < 1)
        return VK_ERROR_INITIALIZATION_FAILED;
    *pOutLayerCount = 1;
    strncpy((char *) pOutLayers[0], "ShaderChecker", maxStringSize);
    return VK_SUCCESS;
}

struct extProps {
    uint32_t version;
    const char * const name;
};
#define SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE 1
static const struct extProps shaderCheckerExts[SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE] = {
    // TODO what is the version?
    0x10, "ShaderChecker",
};

VK_LAYER_EXPORT VkResult VKAPI vkGetGlobalExtensionInfo(
                                               VkExtensionInfoType infoType,
                                               uint32_t extensionIndex,
                                               size_t*  pDataSize,
                                               void*    pData)
{
    VkResult result;

    /* This entrypoint is NOT going to init it's own dispatch table since loader calls here early */
    VkExtensionProperties *ext_props;
    uint32_t *count;

    if (pDataSize == NULL)
        return VK_ERROR_INVALID_POINTER;

    switch (infoType) {
        case VK_EXTENSION_INFO_TYPE_COUNT:
            *pDataSize = sizeof(uint32_t);
            if (pData == NULL)
                return VK_SUCCESS;
            count = (uint32_t *) pData;
            *count = SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE;
            break;
        case VK_EXTENSION_INFO_TYPE_PROPERTIES:
            *pDataSize = sizeof(VkExtensionProperties);
            if (pData == NULL)
                return VK_SUCCESS;
            if (extensionIndex >= SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE)
                return VK_ERROR_INVALID_VALUE;
            ext_props = (VkExtensionProperties *) pData;
            ext_props->version = shaderCheckerExts[extensionIndex].version;
            strncpy(ext_props->extName, shaderCheckerExts[extensionIndex].name,
                                        VK_MAX_EXTENSION_NAME);
            ext_props->extName[VK_MAX_EXTENSION_NAME - 1] = '\0';
            break;
        default:
            return VK_ERROR_INVALID_VALUE;
    };

    return VK_SUCCESS;
}

static int
value_or_default(std::unordered_map<unsigned, unsigned> const &map, unsigned id, int def)
{
    auto it = map.find(id);
    if (it == map.end())
        return def;
    else
        return it->second;
}

struct interface_var {
    uint32_t id;
    uint32_t type_id;
    /* TODO: collect the name, too? Isn't required to be present. */
};

static void
collect_interface_by_location(shader_source const *src, spv::StorageClass interface,
                              std::map<uint32_t, interface_var> &out,
                              std::map<uint32_t, interface_var> &builtins_out)
{
    unsigned int const *code = (unsigned int const *)&src->words[0];
    size_t size = src->words.size();

    if (code[0] != spv::MagicNumber) {
        printf("Invalid magic.\n");
        return;
    }

    std::unordered_map<unsigned, unsigned> var_locations;
    std::unordered_map<unsigned, unsigned> var_builtins;

    unsigned word = 5;
    while (word < size) {

        unsigned opcode = code[word] & 0x0ffffu;
        unsigned oplen = (code[word] & 0xffff0000u) >> 16;

        /* We consider two interface models: SSO rendezvous-by-location, and
         * builtins. Complain about anything that fits neither model.
         */
        if (opcode == spv::OpDecorate) {
            if (code[word+2] == spv::DecLocation) {
                var_locations[code[word+1]] = code[word+3];
            }

            if (code[word+2] == spv::DecBuiltIn) {
                var_builtins[code[word+1]] = code[word+3];
            }
        }

        /* TODO: handle grouped decorations */
        /* TODO: handle index=1 dual source outputs from FS -- two vars will
         * have the same location, and we DONT want to clobber. */

        if (opcode == spv::OpVariable && code[word+3] == interface) {
            int location = value_or_default(var_locations, code[word+2], -1);
            int builtin = value_or_default(var_builtins, code[word+2], -1);

            if (location == -1 && builtin == -1) {
                /* No location defined, and not bound to an API builtin.
                 * The spec says nothing about how this case works (or doesn't)
                 * for interface matching.
                 */
                printf("WARN: var %d (type %d) in %s interface has no Location or Builtin decoration\n",
                       code[word+2], code[word+1], interface == spv::StorageInput ? "input" : "output");
            }
            else if (location != -1) {
                /* A user-defined interface variable, with a location. */
                interface_var v;
                v.id = code[word+2];
                v.type_id = code[word+1];
                out[location] = v;
            }
            else {
                /* A builtin interface variable */
                interface_var v;
                v.id = code[word+2];
                v.type_id = code[word+1];
                builtins_out[builtin] = v;
            }
        }

        word += oplen;
    }
}

VK_LAYER_EXPORT VkResult VKAPI vkCreateShader(VkDevice device, const VkShaderCreateInfo *pCreateInfo,
                                                   VkShader *pShader)
{
    VkLayerDispatchTable* pTable = tableMap[(VkBaseLayerObject *)device];
    VkResult res = pTable->CreateShader(device, pCreateInfo, pShader);

    shader_map[(VkBaseLayerObject *) *pShader] = new shader_source(pCreateInfo);
    return res;
}

static void
validate_interface_between_stages(shader_source const *producer, char const *producer_name,
                                  shader_source const *consumer, char const *consumer_name)
{
    std::map<uint32_t, interface_var> outputs;
    std::map<uint32_t, interface_var> inputs;

    std::map<uint32_t, interface_var> builtin_outputs;
    std::map<uint32_t, interface_var> builtin_inputs;

    printf("Begin validate_interface_between_stages %s -> %s\n",
           producer_name, consumer_name);

    collect_interface_by_location(producer, spv::StorageOutput, outputs, builtin_outputs);
    collect_interface_by_location(consumer, spv::StorageInput, inputs, builtin_inputs);

    auto a_it = outputs.begin();
    auto b_it = inputs.begin();

    /* maps sorted by key (location); walk them together to find mismatches */
    while (a_it != outputs.end() || b_it != inputs.end()) {
        if (b_it == inputs.end() || a_it->first < b_it->first) {
            printf("  WARN: %s writes to output location %d which is not consumed by %s\n",
                   producer_name, a_it->first, consumer_name);
            a_it++;
        }
        else if (a_it == outputs.end() || a_it->first > b_it->first) {
            printf("  ERR: %s consumes input location %d which is not written by %s\n",
                   consumer_name, b_it->first, producer_name);
            b_it++;
        }
        else {
            printf("  OK: match on location %d\n",
                   a_it->first);
            /* TODO: typecheck */
            a_it++;
            b_it++;
        }
    }

    printf("End validate_interface_between_stages\n");
}

static void
validate_vi_against_vs_inputs(VkPipelineVertexInputCreateInfo const *vi, shader_source const *vs)
{
    std::map<uint32_t, interface_var> inputs;
    /* we collect builtin inputs, but they will never appear in the VI state --
     * the vs builtin inputs are generated in the pipeline, not sourced from buffers (VertexID, etc)
     */
    std::map<uint32_t, interface_var> builtin_inputs;

    printf("Begin validate_vi_against_vs_inputs\n");

    collect_interface_by_location(vs, spv::StorageInput, inputs, builtin_inputs);

    /* Build index by location */
    std::map<uint32_t, VkVertexInputAttributeDescription const *> attribs;
    for (int i = 0; i < vi->attributeCount; i++)
        attribs[vi->pVertexAttributeDescriptions[i].location] = &vi->pVertexAttributeDescriptions[i];

    auto it_a = attribs.begin();
    auto it_b = inputs.begin();

    while (it_a != attribs.end() || it_b != inputs.end()) {
        if (it_b == inputs.end() || it_a->first < it_b->first) {
            printf("  WARN: attribute at location %d not consumed by the vertex shader\n",
                   it_a->first);
            it_a++;
        }
        else if (it_a == attribs.end() || it_b->first < it_a->first) {
            printf("  ERR: vertex shader consumes input at location %d but not provided\n",
                   it_b->first);
            it_b++;
        }
        else {
            /* TODO: type check */
            printf("  OK: match on attribute location %d\n",
                   it_a->first);
            it_a++;
            it_b++;
        }
    }

    printf("End validate_vi_against_vs_inputs\n");
}

VK_LAYER_EXPORT VkResult VKAPI vkCreateGraphicsPipeline(VkDevice device,
                                                             const VkGraphicsPipelineCreateInfo *pCreateInfo,
                                                             VkPipeline *pPipeline)
{
    /* TODO: run cross-stage validation */
    /* - Validate FS output -> CB */
    /* - Support GS, TCS, TES stages */

    /* We seem to allow pipeline stages to be specified out of order, so collect and identify them
     * before trying to do anything more: */

    shader_source const *vs_source = 0;
    shader_source const *fs_source = 0;
    VkPipelineCbStateCreateInfo const *cb = 0;
    VkPipelineVertexInputCreateInfo const *vi = 0;

    for (auto stage = pCreateInfo; stage; stage = (decltype(stage))stage->pNext) {
        if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO) {
            auto shader_stage = (VkPipelineShaderStageCreateInfo const *)stage;

            if (shader_stage->shader.stage == VK_SHADER_STAGE_VERTEX)
                vs_source = shader_map[(void *)(shader_stage->shader.shader)];
            else if (shader_stage->shader.stage == VK_SHADER_STAGE_FRAGMENT)
                fs_source = shader_map[(void *)(shader_stage->shader.shader)];
            else
                printf("Unknown shader stage %d\n", shader_stage->shader.stage);
        }
        else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO) {
            cb = (VkPipelineCbStateCreateInfo const *)stage;
        }
        else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO) {
            vi = (VkPipelineVertexInputCreateInfo const *)stage;
        }
    }

    printf("Pipeline: vi=%p vs=%p fs=%p cb=%p\n", vi, vs_source, fs_source, cb);

    if (vi && vs_source) {
        validate_vi_against_vs_inputs(vi, vs_source);
    }

    if (vs_source && fs_source) {
        validate_interface_between_stages(vs_source, "vertex shader",
                                          fs_source, "fragment shader");
    }

    VkLayerDispatchTable *pTable = tableMap[(VkBaseLayerObject *)device];
    VkResult res = pTable->CreateGraphicsPipeline(device, pCreateInfo, pPipeline);
    return res;
}

VK_LAYER_EXPORT void * VKAPI vkGetProcAddr(VkPhysicalGpu gpu, const char* pName)
{
    if (gpu == NULL)
        return NULL;

    initLayerTable((const VkBaseLayerObject *) gpu);

#define ADD_HOOK(fn)    \
    if (!strncmp(#fn, pName, sizeof(#fn))) \
        return (void *) fn

    ADD_HOOK(vkGetProcAddr);
    ADD_HOOK(vkEnumerateLayers);
    ADD_HOOK(vkCreateDevice);
    ADD_HOOK(vkCreateShader);
    ADD_HOOK(vkCreateGraphicsPipeline);

    VkBaseLayerObject* gpuw = (VkBaseLayerObject *) gpu;
    if (gpuw->pGPA == NULL)
        return NULL;
    return gpuw->pGPA((VkPhysicalGpu) gpuw->nextObject, pName);
}
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2020 Scientific Computing and Imaging Institute,
   University of Utah.

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

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

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

///
///    @file    InterfaceWithTetGen.cc
///    @author  Martin Cole
///    @date    Wed Mar 22 07:56:22 2006
///

#include <Modules/Legacy/Fields/InterfaceWithTetGenImpl.h>

#include <Core/Thread/Mutex.h>
#include <Core/Logging/LoggerInterface.h>
#include <Core/Datatypes/Legacy/Field/Field.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/Legacy/Field/FieldInformation.h>
#include <Dataflow/Network/Module.h>

#pragma GCC diagnostic ignored "-Wunused-parameter"
#define TETLIBRARY   // Required definition for use of tetgen library
#include <tetgen.h>

#include <sstream>

#include <sci_debug.h>

using namespace SCIRun;
using namespace Dataflow::Networks;
using namespace Modules::Fields;
using namespace Core::Thread;
using namespace Core::Geometry;

namespace SCIRun {
namespace Modules {
namespace Fields {
namespace detail {

/// @class InterfaceWithTetGen
/// @brief This module interfaces with TetGen.

class InterfaceWithTetGenImplImpl //: public AlgorithmBase
{
  public:
    explicit InterfaceWithTetGenImplImpl(Module* mod);

    FieldHandle runImpl(const std::deque<FieldHandle>& surfaces,
      FieldHandle points, FieldHandle region_attribs, const InterfaceWithTetGenInput& input) const;

  private:
    // translate the ui variables into the string with options
    // that TetGen uses
    Module* module_;
    static Mutex TetGenMutex;
};

Mutex InterfaceWithTetGenImplImpl::TetGenMutex("Protect TetGen from running in parallel");
}}}}

detail::InterfaceWithTetGenImplImpl::InterfaceWithTetGenImplImpl(Module* module) : module_(module)
{}

std::string InterfaceWithTetGenInput::fillCommandOptions(bool addPoints) const
{
  // Collect the options in this output string stream
  std::ostringstream oss;
  if (addPoints)
  {
    oss << "i";
  }

  if (piecewiseFlag_)
  {
    oss << "p";
  }

  if (suppressSplitFlag_)
  {
    if (setSplitFlag_)
    {
      oss << "YY";
    }
    else
    {
      oss << "Y";
    }
  }

  // Always use zero indexing that is the only thing SCIRun knows....
  oss << "z";

  if (qualityFlag_)
  {
    oss << "q";
    if (setRatioFlag_)
    {
      oss << minRadius_;
    }
  }
  if (volConstraintFlag_)
  {
    oss << "a";
    if (setMaxVolConstraintFlag_)
    {
      oss << maxVolConstraint_;
    }
  }
  if (detectIntersectionsFlag_)
  {
    oss << "d";
  }
  if (assignFlag_)
  {
    if (setNonzeroAttributeFlag_)
    {
      oss << "AA";
    }
    else
    {
      oss << "A";
    }
  }

  // Add in whatever the user wants to add additionally.
  oss << moreSwitches_;
  return oss.str();
}

FieldHandle detail::InterfaceWithTetGenImplImpl::runImpl(const std::deque<FieldHandle>& surfaces,
  FieldHandle points, FieldHandle region_attribs, const InterfaceWithTetGenInput& input) const
{
#if 0
  if (inputs_changed_ || piecewiseFlag_.changed() ||
      assignFlag_.changed() || setNonzeroAttributeFlag_.changed() ||
      suppressSplitFlag_.changed() || setSplitFlag_.changed() ||
      qualityFlag_.changed() || setRatioFlag_.changed() ||
      volConstraintFlag_.changed() || setMaxVolConstraintFlag_.changed() ||
      minRadius_.changed() || maxVolConstraint_.changed() ||
      detectIntersectionsFlag_.changed() || moreSwitches_.changed() ||
      !oport_cached("TetVol"))
  #endif
  //{
    // Tell SCIRun we are actually doing some work
//    update_state(Executing);

  //TODO DAN: check for proper memory management with these classes.
    tetgenio in, addin, out;

    bool add_points = false;
    if (points)
    {
      VMesh* mesh = points->vmesh();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();

      addin.numberofpoints = num_nodes;
      addin.pointlist = new REAL[num_nodes*3];
      for(VMesh::Node::index_type idx=0; idx<num_nodes; ++idx)
      {
        Point p;
        mesh->get_center(p, idx);
        addin.pointlist[idx * 3] = p.x();
        addin.pointlist[idx * 3 + 1] = p.y();
        addin.pointlist[idx * 3 + 2] = p.z();
      }
      add_points = true;
      module_->remark("Added extra interior points from Points port.");
    }

    if (region_attribs)
    {
      VMesh* mesh = region_attribs->vmesh();
      VField* field = region_attribs->vfield();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();

      in.regionlist = new REAL[num_nodes*5];
      in.numberofregions = num_nodes;
      for(VMesh::Node::index_type idx=0; idx<num_nodes; ++idx)
      {
        Point p; double val;
        mesh->get_center(p, idx);
        in.regionlist[idx * 5] = p.x();
        in.regionlist[idx * 5 + 1] = p.y();
        in.regionlist[idx * 5 + 2] = p.z();
        in.regionlist[idx * 5 + 3] = idx;
        field->get_value(val, idx);
        in.regionlist[idx * 5 + 4] = val;
      }
    }

    // indices start from 0.
    in.firstnumber = 0;
    in.mesh_dim = 3;

    int marker = -10;

    VMesh::size_type tot_num_nodes = 0;
    VMesh::size_type tot_num_elems = 0;

    for (size_t j=0; j< surfaces.size(); j++)
    {
      VMesh*  mesh = surfaces[j]->vmesh();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();
      VMesh::Elem::size_type num_elems = mesh->num_elems();

      tot_num_nodes += num_nodes;
      tot_num_elems += num_elems;
    }

    in.pointlist = new REAL[(tot_num_nodes) * 3];
    in.numberofpoints = tot_num_nodes;

    in.facetlist = new tetgenio::facet[tot_num_elems];
    in.facetmarkerlist = new int[tot_num_elems];
    in.numberoffacets = tot_num_elems;

    VMesh::index_type idx = 0;
    VMesh::index_type fidx = 0;
    VMesh::index_type off;

    for (size_t j=0; j< surfaces.size(); j++)
    {
      VMesh*  mesh = surfaces[j]->vmesh();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();
      VMesh::Elem::size_type num_elems = mesh->num_elems();

      off = idx;
      for(VMesh::Node::index_type nidx=0; nidx<num_nodes; ++nidx)
      {
        Point p;
        mesh->get_center(p, nidx);
        in.pointlist[idx * 3] = p.x();
        in.pointlist[idx * 3 + 1] = p.y();
        in.pointlist[idx * 3 + 2] = p.z();
        ++idx;
      }

      unsigned int vert_per_face = mesh->num_nodes_per_elem();

      // iterate over faces.
      VMesh::Node::array_type nodes;

      for (VMesh::Elem::index_type eidx = 0; eidx < num_elems; ++eidx)
      {
        if (vert_per_face > 0)
        {
          tetgenio::facet *f = &in.facetlist[fidx];
          f->numberofpolygons = 1;
          f->polygonlist = new tetgenio::polygon[1];
          f->numberofholes = 0;
          f->holelist = nullptr;
          tetgenio::polygon *p = &f->polygonlist[0];
          p->numberofvertices = vert_per_face;

          p->vertexlist = new int[p->numberofvertices];
          mesh->get_nodes(nodes, eidx);
          for (size_t i = 0; i < nodes.size(); i++)
          {
            p->vertexlist[i] = VMesh::index_type(nodes[i]) + off;
          }
        }
        else
        {
          tetgenio::facet *f = &in.facetlist[fidx];
          f->numberofpolygons = 0;
          f->polygonlist = nullptr;
          f->numberofholes = 0;
          f->holelist = nullptr;
        }
        in.facetmarkerlist[fidx] = marker;
        ++fidx;
      }

      marker *= 2;
    }

    module_->getUpdaterFunc()(.2);
    // Save files for later debugging.
    // std::string filename = std::string(sci_getenv("SCIRUN_TMP_DIR")) + "/tgIN";
    // in.save_nodes((char*)filename.c_str());
    // in.save_poly((char*)filename.c_str());

    std::string cmmd_ln = input.fillCommandOptions(add_points);
  #if DEBUG
    std::cerr << "\nTetgen command line: " << cmmd_ln << std::endl;
  #endif
    tetgenio *addtgio = nullptr;
    if (add_points)
    {
       addtgio = &addin;
    }
    // Create the new mesh.

    // Make sure only one thread accesses TetGen
    // It is not thread safe :)
    {
      Guard g(TetGenMutex.get());
      try
      {
        tetrahedralize(const_cast<char*>(cmmd_ln.c_str()), &in, &out, addtgio);
      }
      catch(std::exception& e)
      {
        module_->error(std::string("TetGen failed to generate a mesh: ") + e.what());
        return nullptr;
      }
      catch (...)
      {
        module_->error("TetGen failed to generate a mesh");
        return nullptr;
      }
    }

    module_->getUpdaterFunc()(.9);
    FieldInformation fi(TETVOLMESH_E,CONSTANTDATA_E,DOUBLE_E);
    FieldHandle tetvol_out = CreateField(fi);
    // Convert to a SCIRun TetVol.

    VMesh* mesh = tetvol_out->vmesh();
    VField* field = tetvol_out->vfield();
    for (int i = 0; i < out.numberofpoints; i++)
    {
      Point p(out.pointlist[i*3], out.pointlist[i*3+1], out.pointlist[i*3+2]);
      mesh->add_point(p);
    }

    VMesh::Node::size_type numnodes = mesh->num_nodes();
    VMesh::Node::array_type nodes(4);
    for (int i = 0; i < out.numberoftetrahedra; i++)
    {
      nodes[0] = out.tetrahedronlist[i*4];
      nodes[1] = out.tetrahedronlist[i*4+1];
      nodes[2] = out.tetrahedronlist[i*4+2];
      nodes[3] = out.tetrahedronlist[i*4+3];

      if (nodes[0] >= numnodes ||nodes[1] >= numnodes ||
          nodes[2] >= numnodes ||nodes[3] >= numnodes )
      {
        module_->error("TetGen failed to produce a valid tetrahedralization");
        return nullptr;
      }
      mesh->add_elem(nodes);
    }

    field->resize_values();

    int atts =  out.numberoftetrahedronattributes;

    for (int i = 0; i < out.numberoftetrahedra; i++)
    {
      for (int j = 0; j < atts; j++)
      {
        double val = out.tetrahedronattributelist[i * atts + j];
        field->set_value(val, VMesh::Elem::index_type(i));
      }
    }

    module_->getUpdaterFunc()(1.0);
    return tetvol_out;
}

InterfaceWithTetGenInput::InterfaceWithTetGenInput() :
piecewiseFlag_(true),
assignFlag_(true),
setNonzeroAttributeFlag_(false),
suppressSplitFlag_(true),
setSplitFlag_(false),
qualityFlag_(true),
setRatioFlag_(false),
volConstraintFlag_(false),
setMaxVolConstraintFlag_(false),
// see TetGen documentation for "-q" switch: default is 2.0
minRadius_(2.0),
maxVolConstraint_(0.1),
detectIntersectionsFlag_(false),
moreSwitches_("")
{
}

InterfaceWithTetGenImpl::InterfaceWithTetGenImpl(Module* module, const InterfaceWithTetGenInput& input) :
  impl_(new detail::InterfaceWithTetGenImplImpl(module)),
  inputFlags_(input)
{}

FieldHandle InterfaceWithTetGenImpl::runImpl(const std::deque<FieldHandle>& surfaces, FieldHandle points, FieldHandle region_attribs) const
{
  return impl_->runImpl(surfaces, points, region_attribs, inputFlags_);
}
<commit_msg>Tetgen build fix<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2020 Scientific Computing and Imaging Institute,
   University of Utah.

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

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

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

///
///    @file    InterfaceWithTetGen.cc
///    @author  Martin Cole
///    @date    Wed Mar 22 07:56:22 2006
///

#include <Modules/Legacy/Fields/InterfaceWithTetGenImpl.h>

#include <Core/Thread/Mutex.h>
#include <Core/Logging/LoggerInterface.h>
#include <Core/Datatypes/Legacy/Field/Field.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/Legacy/Field/FieldInformation.h>
#include <Dataflow/Network/Module.h>

#pragma GCC diagnostic ignored "-Wunused-parameter"
#define TETLIBRARY   // Required definition for use of tetgen library
#include <tetgen.h>

#include <sstream>

#include <sci_debug.h>

using namespace SCIRun;
using namespace Dataflow::Networks;
using namespace Modules::Fields;
using namespace Core::Thread;
using namespace Core::Geometry;

namespace SCIRun {
namespace Modules {
namespace Fields {
namespace detail {

/// @class InterfaceWithTetGen
/// @brief This module interfaces with TetGen.

class InterfaceWithTetGenImplImpl //: public AlgorithmBase
{
  public:
    explicit InterfaceWithTetGenImplImpl(Module* mod);

    FieldHandle runImpl(const std::deque<FieldHandle>& surfaces,
      FieldHandle points, FieldHandle region_attribs, const InterfaceWithTetGenInput& input) const;

  private:
    // translate the ui variables into the string with options
    // that TetGen uses
    Module* module_;
    static Mutex TetGenMutex;
};

Mutex InterfaceWithTetGenImplImpl::TetGenMutex("Protect TetGen from running in parallel");
}}}}

detail::InterfaceWithTetGenImplImpl::InterfaceWithTetGenImplImpl(Module* module) : module_(module)
{}

std::string InterfaceWithTetGenInput::fillCommandOptions(bool addPoints) const
{
  // Collect the options in this output string stream
  std::ostringstream oss;
  if (addPoints)
  {
    oss << "i";
  }

  if (piecewiseFlag_)
  {
    oss << "p";
  }

  if (suppressSplitFlag_)
  {
    if (setSplitFlag_)
    {
      oss << "YY";
    }
    else
    {
      oss << "Y";
    }
  }

  // Always use zero indexing that is the only thing SCIRun knows....
  oss << "z";

  if (qualityFlag_)
  {
    oss << "q";
    if (setRatioFlag_)
    {
      oss << minRadius_;
    }
  }
  if (volConstraintFlag_)
  {
    oss << "a";
    if (setMaxVolConstraintFlag_)
    {
      oss << maxVolConstraint_;
    }
  }
  if (detectIntersectionsFlag_)
  {
    oss << "d";
  }
  if (assignFlag_)
  {
    if (setNonzeroAttributeFlag_)
    {
      oss << "AA";
    }
    else
    {
      oss << "A";
    }
  }

  // Add in whatever the user wants to add additionally.
  oss << moreSwitches_;
  return oss.str();
}

FieldHandle detail::InterfaceWithTetGenImplImpl::runImpl(const std::deque<FieldHandle>& surfaces,
  FieldHandle points, FieldHandle region_attribs, const InterfaceWithTetGenInput& input) const
{
#if 0
  if (inputs_changed_ || piecewiseFlag_.changed() ||
      assignFlag_.changed() || setNonzeroAttributeFlag_.changed() ||
      suppressSplitFlag_.changed() || setSplitFlag_.changed() ||
      qualityFlag_.changed() || setRatioFlag_.changed() ||
      volConstraintFlag_.changed() || setMaxVolConstraintFlag_.changed() ||
      minRadius_.changed() || maxVolConstraint_.changed() ||
      detectIntersectionsFlag_.changed() || moreSwitches_.changed() ||
      !oport_cached("TetVol"))
  #endif
  //{
    // Tell SCIRun we are actually doing some work
//    update_state(Executing);

  //TODO DAN: check for proper memory management with these classes.
    tetgenio in, addin, out;

    bool add_points = false;
    if (points)
    {
      VMesh* mesh = points->vmesh();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();

      addin.numberofpoints = num_nodes;
      addin.pointlist = new REAL[num_nodes*3];
      for(VMesh::Node::index_type idx=0; idx<num_nodes; ++idx)
      {
        Point p;
        mesh->get_center(p, idx);
        addin.pointlist[idx * 3] = p.x();
        addin.pointlist[idx * 3 + 1] = p.y();
        addin.pointlist[idx * 3 + 2] = p.z();
      }
      add_points = true;
      module_->remark("Added extra interior points from Points port.");
    }

    if (region_attribs)
    {
      VMesh* mesh = region_attribs->vmesh();
      VField* field = region_attribs->vfield();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();

      in.regionlist = new REAL[num_nodes*5];
      in.numberofregions = num_nodes;
      for(VMesh::Node::index_type idx=0; idx<num_nodes; ++idx)
      {
        Point p; double val;
        mesh->get_center(p, idx);
        in.regionlist[idx * 5] = p.x();
        in.regionlist[idx * 5 + 1] = p.y();
        in.regionlist[idx * 5 + 2] = p.z();
        in.regionlist[idx * 5 + 3] = idx;
        field->get_value(val, idx);
        in.regionlist[idx * 5 + 4] = val;
      }
    }

    // indices start from 0.
    in.firstnumber = 0;
    in.mesh_dim = 3;

    int marker = -10;

    VMesh::size_type tot_num_nodes = 0;
    VMesh::size_type tot_num_elems = 0;

    for (size_t j=0; j< surfaces.size(); j++)
    {
      VMesh*  mesh = surfaces[j]->vmesh();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();
      VMesh::Elem::size_type num_elems = mesh->num_elems();

      tot_num_nodes += num_nodes;
      tot_num_elems += num_elems;
    }

    in.pointlist = new REAL[(tot_num_nodes) * 3];
    in.numberofpoints = tot_num_nodes;

    in.facetlist = new tetgenio::facet[tot_num_elems];
    in.facetmarkerlist = new int[tot_num_elems];
    in.numberoffacets = tot_num_elems;

    VMesh::index_type idx = 0;
    VMesh::index_type fidx = 0;
    VMesh::index_type off;

    for (size_t j=0; j< surfaces.size(); j++)
    {
      VMesh*  mesh = surfaces[j]->vmesh();
      VMesh::Node::size_type num_nodes = mesh->num_nodes();
      VMesh::Elem::size_type num_elems = mesh->num_elems();

      off = idx;
      for(VMesh::Node::index_type nidx=0; nidx<num_nodes; ++nidx)
      {
        Point p;
        mesh->get_center(p, nidx);
        in.pointlist[idx * 3] = p.x();
        in.pointlist[idx * 3 + 1] = p.y();
        in.pointlist[idx * 3 + 2] = p.z();
        ++idx;
      }

      unsigned int vert_per_face = mesh->num_nodes_per_elem();

      // iterate over faces.
      VMesh::Node::array_type nodes;

      for (VMesh::Elem::index_type eidx = 0; eidx < num_elems; ++eidx)
      {
        if (vert_per_face > 0)
        {
          tetgenio::facet *f = &in.facetlist[fidx];
          f->numberofpolygons = 1;
          f->polygonlist = new tetgenio::polygon[1];
          f->numberofholes = 0;
          f->holelist = nullptr;
          tetgenio::polygon *p = &f->polygonlist[0];
          p->numberofvertices = vert_per_face;

          p->vertexlist = new int[p->numberofvertices];
          mesh->get_nodes(nodes, eidx);
          for (size_t i = 0; i < nodes.size(); i++)
          {
            p->vertexlist[i] = VMesh::index_type(nodes[i]) + off;
          }
        }
        else
        {
          tetgenio::facet *f = &in.facetlist[fidx];
          f->numberofpolygons = 0;
          f->polygonlist = nullptr;
          f->numberofholes = 0;
          f->holelist = nullptr;
        }
        in.facetmarkerlist[fidx] = marker;
        ++fidx;
      }

      marker *= 2;
    }

    module_->getUpdaterFunc()(.2);
    // Save files for later debugging.
    // std::string filename = std::string(sci_getenv("SCIRUN_TMP_DIR")) + "/tgIN";
    // in.save_nodes((char*)filename.c_str());
    // in.save_poly((char*)filename.c_str());

    std::string cmmd_ln = input.fillCommandOptions(add_points);
  #if DEBUG
    std::cerr << "\nTetgen command line: " << cmmd_ln << std::endl;
  #endif
    tetgenio *addtgio = nullptr;
    if (add_points)
    {
       addtgio = &addin;
    }
    // Create the new mesh.

    // Make sure only one thread accesses TetGen
    // It is not thread safe :)
    {
      Guard g(TetGenMutex.get());
      try
      {
        tetrahedralize(const_cast<char*>(cmmd_ln.c_str()), &in, &out, addtgio);
      }
      catch(std::exception& e)
      {
        module_->error(std::string("TetGen failed to generate a mesh: ") + e.what());
        return nullptr;
      }
      catch (...)
      {
        module_->error("TetGen failed to generate a mesh");
        return nullptr;
      }
    }

    module_->getUpdaterFunc()(.9);
    FieldInformation fi(mesh_info_type::TETVOLMESH_E, databasis_info_type::CONSTANTDATA_E, data_info_type::DOUBLE_E);
    FieldHandle tetvol_out = CreateField(fi);
    // Convert to a SCIRun TetVol.

    VMesh* mesh = tetvol_out->vmesh();
    VField* field = tetvol_out->vfield();
    for (int i = 0; i < out.numberofpoints; i++)
    {
      Point p(out.pointlist[i*3], out.pointlist[i*3+1], out.pointlist[i*3+2]);
      mesh->add_point(p);
    }

    VMesh::Node::size_type numnodes = mesh->num_nodes();
    VMesh::Node::array_type nodes(4);
    for (int i = 0; i < out.numberoftetrahedra; i++)
    {
      nodes[0] = out.tetrahedronlist[i*4];
      nodes[1] = out.tetrahedronlist[i*4+1];
      nodes[2] = out.tetrahedronlist[i*4+2];
      nodes[3] = out.tetrahedronlist[i*4+3];

      if (nodes[0] >= numnodes ||nodes[1] >= numnodes ||
          nodes[2] >= numnodes ||nodes[3] >= numnodes )
      {
        module_->error("TetGen failed to produce a valid tetrahedralization");
        return nullptr;
      }
      mesh->add_elem(nodes);
    }

    field->resize_values();

    int atts =  out.numberoftetrahedronattributes;

    for (int i = 0; i < out.numberoftetrahedra; i++)
    {
      for (int j = 0; j < atts; j++)
      {
        double val = out.tetrahedronattributelist[i * atts + j];
        field->set_value(val, VMesh::Elem::index_type(i));
      }
    }

    module_->getUpdaterFunc()(1.0);
    return tetvol_out;
}

InterfaceWithTetGenInput::InterfaceWithTetGenInput() :
piecewiseFlag_(true),
assignFlag_(true),
setNonzeroAttributeFlag_(false),
suppressSplitFlag_(true),
setSplitFlag_(false),
qualityFlag_(true),
setRatioFlag_(false),
volConstraintFlag_(false),
setMaxVolConstraintFlag_(false),
// see TetGen documentation for "-q" switch: default is 2.0
minRadius_(2.0),
maxVolConstraint_(0.1),
detectIntersectionsFlag_(false),
moreSwitches_("")
{
}

InterfaceWithTetGenImpl::InterfaceWithTetGenImpl(Module* module, const InterfaceWithTetGenInput& input) :
  impl_(new detail::InterfaceWithTetGenImplImpl(module)),
  inputFlags_(input)
{}

FieldHandle InterfaceWithTetGenImpl::runImpl(const std::deque<FieldHandle>& surfaces, FieldHandle points, FieldHandle region_attribs) const
{
  return impl_->runImpl(surfaces, points, region_attribs, inputFlags_);
}
<|endoftext|>