plugins_manager.cpp 16.7 KB
Newer Older
1 2 3
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions

   Copyright (C) 2014, 2015 CNRS
4
   Copyright (C) 2013, 2014, 2015, 2016 Inria
5 6 7 8 9 10 11

   This file is part of ALTA.

   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0.  If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/.  */

12
#include "plugins_manager.h"
13 14
#include "rational_function.h"
#include "vertical_segment.h"
15

pacanows's avatar
pacanows committed
16
#ifdef _WIN32
17
    #include <windows.h>
18 19 20
#else
    #include <dlfcn.h>
#endif
21
#include <cstdlib>
22
#include <stdio.h>
23
#include <list>
24

25 26
using namespace alta;

27 28 29
//#define DEBUG


30 31
//! Add dynamic library extension (.so or .dll) to a dynamic object as well as
//! prepending 'lib' depending on the plateform.
32
static std::string library_name(const std::string name)
33
{
34
  std::string filename;
35

36
  const int n = name.length();
37
#if defined( _WIN32)
38 39 40 41
  if(name.substr(n-4, n) != std::string("dll")) {
    filename.append(name);
    filename.append(".dll");
  }
42
#elif defined(__APPLE__)
43 44 45 46 47
  if(name.substr(n-5, n) != std::string("dylib")) {
    filename.append("lib");
    filename.append(name);
    filename.append(".dylib");
  }
48
#else
49 50 51 52 53
  if(name.substr(n-2, n) != std::string("so")) {
    filename.append("lib");
    filename.append(name);
    filename.append(".so");
  }
54
#endif
55 56 57
  else {
    filename.append(name);
  }
58

59
  return filename;
60 61
}

62 63 64
// Return the plugin search path based on the 'ALTA_PLUGIN_PATH' environment
// variable.  Empty entries in that variable's value are taken to designate
// the plugin installation directory.
65
static std::list<std::string> plugin_search_path()
66
{
67
  std::list<std::string> dirs;
68 69
  std::string obj_str ;
  const char *env_str = std::getenv("ALTA_PLUGIN_PATH");
70

71 72 73
  if(env_str == NULL) {
    obj_str = "";
  } else {
74
    obj_str = env_str;
75
  }
76

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
  if (obj_str.empty())
      // Always add at least the system's plugin directory.
      dirs.push_back(ALTA_PLUGIN_DIRECTORY);
  else {
      for (auto start = 0, next = 0;
           start < obj_str.size();
           start = next + 1)
      {
          next = obj_str.find(':', start);
          if (next == std::string::npos)
              next = obj_str.size();

          // An empty entry denotes the system directory.
          auto entry = next == start
              ? ALTA_PLUGIN_DIRECTORY
              : obj_str.substr(start, next - start);

          dirs.push_back(entry);

          if (next == obj_str.size() - 1)
              // This is a trailing empty entry.
              dirs.push_back(ALTA_PLUGIN_DIRECTORY);
      }
  }
101 102

  return dirs;
103 104
}

105 106 107
//! \brief Open a dynamic library file (.so or .dll) and extract the associated
//! provide function. The template argument is used to cast the library to a
//! specific type.
108 109
template<typename T>
static T open_library(const std::string& filename, const char* function)
110
{
111
  auto directories = plugin_search_path();
112

113 114
  for (auto&& directory: directories)
  {
115
    auto libname = directory + "/" + library_name(filename);
116

pacanows's avatar
pacanows committed
117
#ifdef _WIN32
118
    HINSTANCE handle = LoadLibraryA(libname.c_str());
119 120 121 122 123 124
    if(handle != NULL)
    {
        T res = (T)GetProcAddress(handle, function);

        if(res == NULL)
        {
125
#ifdef DEBUG_CORE
126
        std::cerr << "<<ERROR>> unable to load the symbol \"" << function << "\" from " << filename << std::endl;
127
#endif
128
        continue;
129 130 131 132 133 134 135 136
        }
#ifdef DEBUG_CORE
        std::cout << "<<DEBUG>> will provide a " << function << " for library \"" << filename << "\"" << std::endl;
#endif
        return res;
    }
    else
    {
137
#ifdef DEBUG_CORE
138
        std::cerr << "<<ERROR>> unable to load the dynamic library file \"" << libname << "\"" << std::endl;
139
        std::cerr << "          cause: \"" << GetLastError() << "\"" << std::endl;
140
#endif
141
     continue;
142
    }
143
#else
144
    void* handle = dlopen(libname.c_str(), RTLD_GLOBAL | RTLD_LAZY);
145

146 147 148 149
   if(handle != NULL)
   {
     void (*res)();
     *(void **)(&res) = dlsym(handle, function);
150 151 152

        if(dlerror() != NULL)
        {
153
#ifdef DEBUG_CORE
154
            std::cerr << "<<ERROR>> unable to load the symbol \"" << function << "\" from " << libname << std::endl;
155
#endif
156
            continue;
157 158
        }
#ifdef DEBUG_CORE
159
        std::cout << "<<DEBUG>> will provide a " << function << " for library \"" << libname << "\"" << std::endl;
160 161 162 163 164
#endif
        return (T)res;
    }
    else
    {
165
#ifdef DEBUG_CORE
166
        std::cerr << "<<ERROR>> unable to load the dynamic library file \"" << libname << "\"" << std::endl;
167
      std::cerr << "          cause: \"" << dlerror() << "\"" << std::endl;
168
#endif
169
        continue;
170 171
    }
#endif
172
  }
173

174 175
  std::cerr << "<<ERROR>> unable to load the symbol \"" << function << "\" from " << filename << std::endl;
  return NULL;
176 177
}

178
//! \brief load a function from the ALTA input file.
179
function* plugins_manager::load_function(const std::string& filename)
180
{
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
  std::ifstream file;
  file.open(filename.c_str()) ;
  if(!file.is_open())
  {
    std::cerr << "<<ERROR>> unable to open file \"" << filename << "\"" << std::endl ;
    return NULL;
  }

  // Set the precision of the input
  file.precision(10);

  // Parameters of the function object
  params::input param_in   = params::UNKNOWN_INPUT;
  params::output param_out = params::UNKNOWN_OUTPUT;
  arguments args;

  // Test for the first line of the file. Should be a ALTA FUNC HEADER
  std::string line ;
  std::getline(file, line) ;
  if(line != "#ALTA FUNC HEADER")
  {
    std::cerr << "<<ERROR>> this is not a function file" << std::endl;
203 204 205

    //RP: Returning NULL immediately now
    return NULL;
206
  }
207

208 209 210
  // Parse the header for the function command line and the dimension
  // of the function
  arguments header = arguments::parse_header(file);
211

212
  std::pair<int, int> dim = header.get_pair<int>("DIM");
213

214 215 216
  param_in = params::parse_input(header.get_string("PARAM_IN", "UNKNOWN_INPUT"));
  param_out = params::parse_output(header.get_string("PARAM_OUT", "UNKNOWN_OUTPUT"));
  args = arguments::create_arguments(header["CMD"]);
217

218 219
  parameters params(dim.first, dim.second, param_in, param_out);

220 221
  // Create the function from the command line
  function* f = get_function(args);
222 223 224 225 226 227
  f->setParametrization(params);

  // FIXME: Since the above is not quite the same as calling 'setDimY' (which
  // is virtual), also call it from here.  TODO: Remove it ASAP.
  f->setDimY(params.dimY());
  f->setDimX(params.dimX());
228

229
  // Load the function part from the file object
230 231 232 233
  if( f->load(file) )
  {
    return f;
  }
234

235
  std::cout << "<<ERROR>> COULD NOT LOAD THE BRDF from File " << filename << std::endl;
236
   return NULL;
237 238
}

239
//! Get an instance of the function selected based on the name <em>n</em>.
240
//! Return NULL if none exists.
241
function* plugins_manager::get_function(const arguments& args)
242
{
243 244 245 246 247 248 249
  #ifdef DEBUG
  std::cout << __FILE__ << " " << __LINE__ << std::endl;
  std::cout << " INitial args = " << args << std::endl;
  #endif

  if(!args.is_defined("func"))
  {
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
250 251 252 253
    #ifdef DEBUG
    std::cout << "<<DEBUG>> no function plugin specified, returning a rational function" << std::endl;
    #endif
    return new rational_function();
254 255 256 257 258 259 260 261 262 263
  }

  // The function to be returned.
  function* func = NULL;

  if(args.is_vec("func")) //Checking if  the argument prodived with --func is of the form --func [ arg1, arg2, arg3]
  {
    #ifdef DEBUG
    std::cout << __FILE__ << " " << __LINE__ << " ARGUMENTS of --func are forming a VECTOR " << std::endl;
    #endif 
264

265
    std::vector<std::string> args_vec = args.get_vec("func");
Laurent Belcour's avatar
Laurent Belcour committed
266

267 268
    // Treating the case []
    if(args_vec.size() == 0)
269
    {
270 271
        return NULL;
    }
272

273
   //! create a *compound* class to store multiple ! functions in it.
274
    compound_function* compound = new compound_function();
275

276 277
   //! For each args_vec element, create a function object and add ! it to the
   //compound one.
278 279 280 281
    for(unsigned int i=0; i<args_vec.size(); ++i)
    {
      std::string n("--func ");
      n.append(args_vec[i]);
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
282 283

      #ifdef DEBUG
284 285 286
      std::cout << __FILE__ << " " << __LINE__ << std::endl
                << " at i=" << i << " n=" << n << std::endl;
      std::cout << "<<DEBUG>> load function with args: " << n << std::endl;
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
287 288
      #endif
      
289
      arguments temp_args = arguments::create_arguments(n);
290

291 292
      //Recursive call
      function* f = get_function(temp_args);
293 294
      nonlinear_function *nl_f = dynamic_cast<nonlinear_function*>(f);
      if(nl_f == NULL)
295 296 297 298 299
      {
          std::cerr << "<<ERROR>> only non-linear functions are compatible with a compound" << std::endl;
      }
      else
      {
300
          compound->push_back(ptr<nonlinear_function>(nl_f), temp_args);
301 302
      }
    }
303

304 305
      //! return the compound class
      func = compound;
306 307 308
      #ifdef DEBUG
      std::cout << __FILE__ << " " << __LINE__ << " WE HAVE A COMPOUND " << std::endl;
      #endif
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
309 310 311 312 313 314 315 316 317 318 319 320
  }
  else //Here we check if the argument provided with --func is file describing a function
  {
    const std::string filename = args["func"];
    FunctionPrototype myFunction = open_library<FunctionPrototype>(filename, "provide_function");
    if(myFunction != NULL)
    {
      #ifdef DEBUG
      std::cout << "<<DEBUG>> using function provider in file \"" << filename << "\"" << std::endl;
      #endif
      
      func =  myFunction();
321
    }
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
322
    else
323
    {
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
324 325
      std::cerr << "<<ERROR>> no function provider found in file \"" << filename << "\"" << std::endl;
      return new rational_function() ;
326
    }
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
327
  }
328

PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
329 330 331
  // Treat the case of the Fresnel
  if(args.is_defined("fresnel"))
  {
332 333 334 335 336
      #ifdef DEBUG
      std::cout << __FILE__ << " " << __LINE__ << std::endl;
      std::cout << " INSIDE fresnel args = " << args << std::endl;
      #endif

PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
337 338 339 340 341 342 343 344 345 346
      // Cast into a non linear function, only those are permitted to use
      // a Fresnel term.
      nonlinear_function* nl_func = dynamic_cast<nonlinear_function*>(func);
      if(nl_func == NULL)
      {
       std::cerr << "<<ERROR>> only non-linear function are permitted to use Fresnel" << std::endl;
       return func;
      }

      std::cout << "<<DEBUG>> multiplying by a Fresnel term" << std::endl;
347

PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
348 349 350 351 352 353 354 355 356 357 358
      std::string n("--func ");
      if(args.is_vec("fresnel"))
      {
       std::string temp = args["fresnel"];
       n.append(temp.substr(1, temp.size()-2));
      }
      else
      {
       std::string fname = args["fresnel"];
       if(fname.empty()) // Nothing to do except print error, no plugin defined
       {
359 360 361
         std::cerr << "<<ERROR>> Fresnel plugin not defined" << std::endl;
         std::cerr << "<<ERROR>> using --fresnel alone is not permitted" << std::endl;
         return func;
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
362 363 364
       }
       else // Case where the fresnel parameters is only the plugin filename
       {
365
         n.append(fname);
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
366 367 368
       }
      }

369 370 371 372 373
      #ifdef DEBUG
      std::cout << __FILE__ << " " << __LINE__ << std::endl;
      std::cout << " n = " << n << std::endl;
      #endif
       
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
374 375 376
      nonlinear_function* func_fres = dynamic_cast<nonlinear_function*>(get_function(arguments::create_arguments(n)));
      if(func_fres != NULL)
      {
377 378 379 380 381 382 383 384 385 386 387 388
        bool const fresnel_is_fixed = (args.is_defined("fixed-fresnel")) ? (true) : (false);
        bool const lobe_is_fixed    = (args.is_defined("fixed-lobe")) ? (true) : (false);
       
        if( fresnel_is_fixed )
        {
          std::cout << "<<DEBUG>> The Fresnel term is fixed" << std::endl;
        }
        if( lobe_is_fixed )
        {
          std::cout << "<<DEBUG>> The lobe is fixed" << std::endl;
        }

389 390 391
        return new product_function(ptr<nonlinear_function>(nl_func),
                                    ptr<nonlinear_function>(func_fres),
                                    lobe_is_fixed, fresnel_is_fixed);
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
392 393 394
      }
      else
      {
395 396
        std::cerr << "<<ERROR>> the right part of the product is not a nonlinear function. Will use only the left part." << std::endl;
        return func;
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
397
      }
398
   }
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
399 400

   
401
/*
402 403 404 405 406 407 408
   // Correction of the data by 1/cosine(theta_L)
   if(args.is_defined("data-correct-cosine"))
   {
     nonlinear_function* cosine = new cosine_function();
     func = new product_function(cosine, dynamic_cast<nonlinear_function*>(func));
   }
   // End of correction
409
*/
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
410 411


412
    return func;
413
}
414 415 416 417 418 419 420 421 422 423 424 425


ptr<function> plugins_manager::get_function(const std::string& n)
{
    if(n.empty())
    {
#ifdef DEBUG
        std::cout << "<<DEBUG>> no function plugin specified, returning nothing" << std::endl;
#endif
        return NULL;
    }

426
   FunctionPrototype myFunc = open_library<FunctionPrototype>(n, "provide_function");
427 428 429 430 431
    if(myFunc != NULL)
    {
#ifdef DEBUG
        std::cout << "<<DEBUG>> using function provider in file \"" << n << "\"" << std::endl;
#endif
432
        return ptr<function>(myFunc());
433 434 435 436 437 438 439 440 441
    }
    else
    {
        std::cerr << "<<ERROR>> no function provider found in file \"" << n << "\"" << std::endl;
        return NULL;
    }
}


442
ptr<data> plugins_manager::get_data(const std::string& n, const arguments& args)
443
{
444
    if(n.empty() || n == "vertical_segment")
445
    {
446
#ifdef DEBUG
447
        std::cout << "<<DEBUG>> no data plugin specified, returning a vertical_segment loader" << std::endl;
448
#endif
449
        return ptr<data>(new vertical_segment());
450 451
    }

452
   DataPrototype myData = open_library<DataPrototype>(n, "provide_data");
453 454
    if(myData != NULL)
    {
455
#ifdef DEBUG
456
        std::cout << "<<DEBUG>> using data provider in file \"" << n << "\"" << std::endl;
457
#endif
458
        return ptr<data>(myData(args));
459 460 461 462
    }
    else
    {
        std::cerr << "<<ERROR>> no data provider found in file \"" << n << "\"" << std::endl;
463
        return ptr<data>(new vertical_segment()) ;
464
    }
465
}
466
ptr<fitter> plugins_manager::get_fitter(const std::string& n)
467
{
Laurent Belcour's avatar
Laurent Belcour committed
468 469 470 471 472
    if(n.empty())
    {
#ifdef DEBUG
        std::cout << "<<DEBUG>> no fitter plugin specified, returning null" << std::endl;
#endif
473
        return NULL;
Laurent Belcour's avatar
Laurent Belcour committed
474 475
    }

476
    FitterPrototype myFitter = open_library<FitterPrototype>(n, "provide_fitter");
Laurent Belcour's avatar
Laurent Belcour committed
477 478 479
    if(myFitter != NULL)
    {
#ifdef DEBUG
480
        std::cout << "<<DEBUG>> using fitter provider in file \"" << n << "\"" << std::endl;
Laurent Belcour's avatar
Laurent Belcour committed
481
#endif
482
        return ptr<fitter>(myFitter());
Laurent Belcour's avatar
Laurent Belcour committed
483 484 485
    }
    else
    {
486
        std::cerr << "<<ERROR>> no fitter provider found in file \"" << n << "\"" << std::endl;
487
        return NULL ;
Laurent Belcour's avatar
Laurent Belcour committed
488
    }
489
}
490

491 492 493
void plugins_manager::check_compatibility( ptr<data>& d, 
                                           const ptr<function>& f,
                                           const arguments& args)
Laurent Belcour's avatar
Laurent Belcour committed
494
{
495
  if(d->parametrization().input_parametrization() == params::UNKNOWN_INPUT &&
496
    f->parametrization().input_parametrization() == params::UNKNOWN_INPUT)
497 498 499 500 501 502 503 504 505 506
  {
    std::cout << "<<WARNING>> both function and data objects have no parametrization" << std::endl;
  }
  else
  {
    if(d->parametrization().input_parametrization() == params::UNKNOWN_INPUT)
    {
      std::cout << "<<WARNING>> unknown parametrization for data" << std::endl;
    }

507
    if(f->parametrization().input_parametrization() == params::UNKNOWN_INPUT)
508 509
    {
      std::cout << "<<DEBUG>> function will take the parametrization of the data" << std::endl;
510 511 512 513 514
      parameters params(f->parametrization().dimX(),
                        f->parametrization().dimY(),
                        d->parametrization().input_parametrization(),
                        f->parametrization().output_parametrization());
      f->setParametrization(params);
515
    }
516
    else if(d->parametrization().input_parametrization() != f->parametrization().input_parametrization() && args.is_defined("change-param"))
517 518
    {
      std::cout << "<<INFO>> has to change the parametrization of the input data " << params::get_name(d->parametrization().input_parametrization()) << std::endl;
519 520
      std::cout << "<<INFO>> to " << params::get_name(f->parametrization().input_parametrization()) << std::endl;
      ptr<data_params> dd = ptr<data_params>(new data_params(d, f->parametrization().input_parametrization()));
521 522 523 524 525 526 527 528
      d = dynamic_pointer_cast<data>(dd) ;
    }
    else
    {
      std::cout << "<<DEBUG>> no change was made to the parametrization" << std::endl;
    }
  }

529
  if(f->parametrization().dimY() != d->parametrization().dimY())
530 531 532 533 534 535 536 537 538 539 540 541
  {
    f->setDimY(d->parametrization().dimY());
  }

  /*
  // Check is the data has to be clusterized
  if(args.is_defined("cluster-dim"))
  {
  clustering* cluster = new clustering(d, args);
  d = cluster;
  }
  */
Laurent Belcour's avatar
Laurent Belcour committed
542
}
543

544
// \todo implement the Darwin (MACOS) version.
545 546
// \todo : Why not move these functions to common.h ?
#ifdef _WIN32
547
#include <windows.h>
548
size_t plugins_manager::get_system_memory()
549
{
550 551 552 553
  MEMORYSTATUSEX status;
  status.dwLength = sizeof(status);
  GlobalMemoryStatusEx(&status);
  return status.ullTotalPhys;
554
}
555 556 557 558 559
#elif defined(__APPLE__)
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
560 561
size_t plugins_manager::get_system_memory()
{
562 563 564 565
  int mib[2];
  mib[0] = CTL_HW;
  mib[1] = HW_MEMSIZE;    
  int64_t size = 0;   
566
  size_t len = sizeof( size );
567 568 569 570
  if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
    return (size_t)size;
  
  return 0L;
571
}
572 573
#else
#include <unistd.h>
574
size_t plugins_manager::get_system_memory()
575
{
576 577 578
  long pages = sysconf(_SC_PHYS_PAGES);
  long page_size = sysconf(_SC_PAGE_SIZE);
  return pages * page_size;
579 580
}
#endif