plugins_manager.cpp 16.1 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 27
//#define DEBUG


28 29
//! Add dynamic library extension (.so or .dll) to a dynamic object as well as
//! prepending 'lib' depending on the plateform.
30
static std::string library_name(const std::string name)
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
{
	std::string filename;

	const int n = name.length();
#if defined( _WIN32)
	if(name.substr(n-4, n) != std::string("dll")) {
		filename.append(name);
		filename.append(".dll");
	}
#elif defined(__APPLE__)
	if(name.substr(n-5, n) != std::string("dylib")) {
		filename.append("lib");
		filename.append(name);
		filename.append(".dylib");
	}
#else
	if(name.substr(n-2, n) != std::string("so")) {
		filename.append("lib");
		filename.append(name);
		filename.append(".so");
	}
#endif
	else {
		filename.append(name);
	}

	return filename;
}

60 61 62
// 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.
63
static std::list<std::string> plugin_search_path()
64
{
65
  std::list<std::string> dirs;
66
	std::string obj_str ;
67
	const char *env_str = std::getenv("ALTA_PLUGIN_PATH");
68 69

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

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
  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);
      }
  }
99 100

  return dirs;
101 102
}

103 104 105
//! \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.
106 107
template<typename T>
static T open_library(const std::string& filename, const char* function)
108
{
109
  auto basename = plugin_search_path();
110 111 112 113

	std::list<std::string>::iterator iter;
	for(iter = basename.begin(); iter != basename.end(); ++iter)
	{
114
	 std::string libname = *iter;
115
	 libname += "/" + library_name(filename);
116

117

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

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

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

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

179
//! \brief load a function from the ALTA input file.
180
function* plugins_manager::load_function(const std::string& filename)
181 182 183 184 185 186
{
	std::ifstream file;
	file.open(filename.c_str()) ;
	if(!file.is_open())
	{
		std::cerr << "<<ERROR>> unable to open file \"" << filename << "\"" << std::endl ;
187
		return NULL;
188 189
	}

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

193 194
	// Parameters of the function object
	int nX, nY;
195
	params::input param_in   = params::UNKNOWN_INPUT;
196
	params::output param_out = params::UNKNOWN_OUTPUT;
197 198 199 200 201 202 203 204
	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;
205 206 207

    //RP: Returning NULL immediately now
    return NULL;
208 209 210 211
	}

	// Parse the header for the function command line and the dimension
	// of the function
212
	arguments header = arguments::parse_header(file);
213

214
	std::pair<int, int> dim = header.get_pair<int>("DIM");
215 216
	nX = dim.first;
	nY = dim.second;
217

218 219
	param_in = params::parse_input(header.get_string("PARAM_IN", "UNKNOWN_INPUT"));
	param_out = params::parse_output(header.get_string("PARAM_OUT", "UNKNOWN_OUTPUT"));
220
	args = arguments::create_arguments(header["CMD"]);
221 222 223 224 225

	// Create the function from the command line
	function* f = get_function(args);
	f->setDimX(nX);
	f->setDimY(nY);
226 227 228 229
  if(f->input_parametrization() == params::UNKNOWN_INPUT)
  {
    f->setParametrization(param_in);
  }
230
	f->setParametrization(param_out);
231 232

	// Load the function part from the file object
233 234 235 236
  if( f->load(file) )
  {
    return f;
  }
237

238 239
  std::cout << "<<ERROR>> COULD NOT LOAD THE BRDF from File " << filename << std::endl;
	 return NULL;
240 241
}

242
//! Get an instance of the function selected based on the name <em>n</em>.
243
//! Return NULL if none exists.
244
function* plugins_manager::get_function(const arguments& args)
245
{
246 247 248 249 250 251 252
  #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
253 254 255 256
    #ifdef DEBUG
    std::cout << "<<DEBUG>> no function plugin specified, returning a rational function" << std::endl;
    #endif
    return new rational_function();
257 258 259 260 261 262 263 264 265 266
  }

  // 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 
267

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

270 271
    // Treating the case []
    if(args_vec.size() == 0)
272
    {
273 274
        return NULL;
    }
275

276
	 //! create a *compound* class to store multiple ! functions in it.
277
    compound_function* compound = new compound_function();
278

279 280
	 //! For each args_vec element, create a function object and add ! it to the
	 //compound one.
281 282 283 284
    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
285 286

      #ifdef DEBUG
287 288 289
      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
290 291
      #endif
      
292
      arguments temp_args = arguments::create_arguments(n);
293

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

307
		  //! return the compound class 
308
		  func = compound;
309 310 311
      #ifdef DEBUG
      std::cout << __FILE__ << " " << __LINE__ << " WE HAVE A COMPOUND " << std::endl;
      #endif
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
312 313 314 315 316 317 318 319 320 321 322 323
  }
  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();
324
    }
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
325
    else
326
    {
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
327 328
      std::cerr << "<<ERROR>> no function provider found in file \"" << filename << "\"" << std::endl;
      return new rational_function() ;
329
    }
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
330
  }
331

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

PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
340 341 342 343 344 345 346 347 348 349
      // 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;
350

PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
      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
       {
      	 std::cerr << "<<ERROR>> Fresnel plugin not defined" << std::endl;
      	 std::cerr << "<<ERROR>> using --fresnel alone is not permitted" << std::endl;
      	 return func;
       }
       else // Case where the fresnel parameters is only the plugin filename
       {
      	 n.append(fname);
       }
      }

372 373 374 375 376
      #ifdef DEBUG
      std::cout << __FILE__ << " " << __LINE__ << std::endl;
      std::cout << " n = " << n << std::endl;
      #endif
       
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
377 378 379
      nonlinear_function* func_fres = dynamic_cast<nonlinear_function*>(get_function(arguments::create_arguments(n)));
      if(func_fres != NULL)
      {
380 381 382 383 384 385 386 387 388 389 390 391
        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;
        }

392 393 394
        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
395 396 397
      }
      else
      {
398 399
        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
400
      }
401
	 }
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
402 403

   
404
/*
405 406 407 408 409 410 411
	 // 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
412
*/
PACANOWSKI Romain's avatar
PACANOWSKI Romain committed
413 414


415
    return func;
416
}
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434


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;
    }

	 FunctionPrototype myFunc = open_library<FunctionPrototype>(n, "provide_function");
    if(myFunc != NULL)
    {
#ifdef DEBUG
        std::cout << "<<DEBUG>> using function provider in file \"" << n << "\"" << std::endl;
#endif
435
        return ptr<function>(myFunc());
436 437 438 439 440 441 442 443 444
    }
    else
    {
        std::cerr << "<<ERROR>> no function provider found in file \"" << n << "\"" << std::endl;
        return NULL;
    }
}


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

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

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

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

		if(f->input_parametrization() == params::UNKNOWN_INPUT)
		{
			std::cout << "<<DEBUG>> function will take the parametrization of the data" << std::endl;
			f->setParametrization(d->input_parametrization());
		}
515
		else if(d->input_parametrization() != f->input_parametrization() && args.is_defined("change-param"))
516
		{
517
			std::cout << "<<INFO>> has to change the parametrization of the input data " << params::get_name(d->input_parametrization()) << std::endl;
518
      std::cout << "<<INFO>> to " << params::get_name(f->input_parametrization()) << std::endl;
519
			ptr<data_params> dd = ptr<data_params>(new data_params(d, f->input_parametrization()));
520
			d = dynamic_pointer_cast<data>(dd) ;
521 522 523 524 525
		}
		else
		{
			std::cout << "<<DEBUG>> no change was made to the parametrization" << std::endl;
		}
Laurent Belcour's avatar
Laurent Belcour committed
526 527 528 529
	}

	if(f->dimY() != d->dimY())
	{
530
    f->setDimY(d->dimY());
Laurent Belcour's avatar
Laurent Belcour committed
531 532 533 534 535 536 537 538 539 540 541
	}

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

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