Material Definition Language API nvidia_logo_transpbg.gif Up
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Example for Starting and Shutting Down the MDL API
[Up] [Next]

This example accesses the main MDL API interface, queries the version interface, prints some version information and then starts and shuts down the MDL API. See the Getting Started Section for a description of how the example programs can be compiled.

New Topics

  • Naming conventions.
  • Main include files.
  • Main API access point.
  • Interfaces and handles.
  • Starting and shutting down the MDL API.

Detailed Description

Naming conventions


The MDL API is written in C++. It uses the mi and mi::neuraylib namespaces for its own identifiers, or the MI_NEURAYLIB_ prefix for macros.

Multiple words are concatenated with _ to form identifiers. Function names are all lower case, while types and classes start with one initial upper-case letter.

Main include files


The include file mi/mdl_sdk.h provides all the functionality of the MDL API.

Main API access point


The mi_factory() function is the only public access point to all algorithms and data structures in the API (see Library Design for an explanation why). To obtain a pointer to the main interface mi::neuraylib::INeuray, call this function with the mi::neuraylib::INeuray::IID() as its iid parameter. If no compatible interface can be found in the library, a NULL pointer is returned. In this case, you can use mi::neuraylib::IVersion::IID() to request a mi::neuraylib::IVersion interface from the mi_factory() for diagnostics. The mi::neuraylib::INeuray interface can only be accessed once per process.

Before you are able to call the mi_factory() function, you need to load the MDL API DSO and to locate the factory function. The convenience function load_and_get_ineuray() abstracts these platform-dependent steps.

Note that it is not required to use load_and_get_ineuray(). In particular in larger setups you might want to write your own code to load the MDL API DSO and to locate the factory function. In such cases, you call mi_factory() directly. For simplicity, the examples will use the convenience function load_and_get_ineuray() instead of mi_factory().

Interfaces and handles


Except for trivial classes, such as the math vector class, all classes in the MDL API are implemented using interfaces. See Library Design for an explanation. Interfaces are created and destroyed by the MDL API. They implement reference counting for life-time control and cheap copying operations. Interface names start with an I prefix.

Whenever you do not need an interface any longer, you have to release it by calling its release() method. Omitting such calls leads to memory leaks. To simplify your life we provide a simple handle class mi::base::Handle. This handle class maintains a pointer semantic while supporting reference counting for interface pointers. For example, the -> operator acts on the underlying interface pointer, which means that you can use a handle to a particular interface pointer in a way very similar to the interface pointer itself. The destructor calls release() on the interface pointer, copy constructor and assignment operator take care of retaining and releasing the interface pointer as necessary.

Note that the handle class has two different constructors to deal with ownership of the interface pointer. See the mi::base::Handle documentation for details.

Note that it is also possible to use other handle class implementations, e.g., std::tr1::shared_ptr<T> (or boost::shared_ptr<T>). In case you prefer to use such handle classes, you have to ensure that their destructor calls the release() method of the interface pointer. This can be achieved by passing an appropriate argument as second parameter, e.g.,

std::tr1::shared_ptr<T> p ( load_and_get_ineuray(), std::mem_fun (&T::release));

Starting and shutting down the MDL API


The mi::neuraylib::INeuray interface is used to start and shut down the MDL API. The API can only be used after it has been started (and before it has been shut down). Startup does not happen during the mi_factory() call because you might want to configure the behavior of the API, which has to happen before startup.

The status of the API can be queried using the mi::neuraylib::INeuray::get_status() method.

Finally, you have to shut down the MDL API. At this point, you should have released all interface pointers except the pointer to the main mi::neuraylib::INeuray interface. If you are using the handle class, make sure that all handles have gone out of scope.

Example Source

Source Code Location: examples/mdl_sdk/shared/example_shared.h

/******************************************************************************
* Copyright 2018 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_shared.h
//
// Code shared by all examples
#ifndef EXAMPLE_SHARED_H
#define EXAMPLE_SHARED_H
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <mi/mdl_sdk.h>
#ifdef MI_PLATFORM_WINDOWS
#include <direct.h>
#include <Shlobj.h>
#include <Knownfolders.h>
#include <mi/base/miwindows.h>
#else
#include <dlfcn.h>
#include <unistd.h>
#include <dirent.h>
#endif
#ifdef MI_PLATFORM_MACOSX
#include <mach-o/dyld.h> // _NSGetExecutablePath
#endif
#ifndef MDL_SAMPLES_ROOT
#define MDL_SAMPLES_ROOT "."
#endif
// Pointer to the DSO handle. Cached here for unload().
void* g_dso_handle = 0;
// Returns the value of the given environment variable.
//
// \param env_var environment variable name
// \return the value of the environment variable or an empty string
// if that variable does not exist or does not have a value.
std::string get_environment(const char* env_var)
{
std::string value;
#ifdef MI_PLATFORM_WINDOWS
char* buf = nullptr;
size_t sz = 0;
if (_dupenv_s(&buf, &sz, env_var) == 0 && buf != nullptr) {
value = buf;
free(buf);
}
#else
const char* v = getenv(env_var);
if (v)
value = v;
#endif
return value;
}
// Sets the value of the given environment variable.
//
// \param env_var environment variable name
// \param value the new value to set.
bool set_environment(const char* env_var, const char* value)
{
#ifdef MI_PLATFORM_WINDOWS
return 0 == _putenv_s(env_var, value);
#else
return 0 == setenv(env_var, value, 1);
#endif
}
// Checks if the given directory exists.
//
// \param directory path to check
// \return true, of the path points to a directory, false if not
bool dir_exists(const char* path)
{
#ifdef MI_PLATFORM_WINDOWS
DWORD attrib = GetFileAttributesA(path);
return (attrib != INVALID_FILE_ATTRIBUTES) && (attrib & FILE_ATTRIBUTE_DIRECTORY);
#else
DIR* dir = opendir(path);
if (dir == nullptr)
return false;
closedir(dir);
return true;
#endif
}
// Returns a string pointing to the directory relative to which the SDK examples
// expect their resources, e. g. materials or textures.
std::string get_samples_root()
{
std::string samples_root = get_environment("MDL_SAMPLES_ROOT");
if (samples_root.empty()) {
samples_root = MDL_SAMPLES_ROOT;
}
if (dir_exists(samples_root.c_str()))
return samples_root;
return ".";
}
// Returns a string pointing to the MDL search root for the SDK examples
std::string get_samples_mdl_root()
{
return get_samples_root() + "/mdl";
}
// Ensures that the console with the log messages does not close immediately. On Windows, the user
// is asked to press enter. On other platforms, nothing is done as the examples are most likely
// started from the console anyway.
void keep_console_open() {
#ifdef MI_PLATFORM_WINDOWS
if( IsDebuggerPresent()) {
fprintf( stderr, "Press enter to continue . . . \n");
fgetc( stdin);
}
#endif // MI_PLATFORM_WINDOWS
}
// Helper macro. Checks whether the expression is true and if not prints a message and exits.
#define check_success( expr) \
do { \
if( !(expr)) { \
fprintf( stderr, "Error in file %s, line %u: \"%s\".\n", __FILE__, __LINE__, #expr); \
keep_console_open(); \
exit( EXIT_FAILURE); \
} \
} while( false)
// Helper function similar to check_success(), but specifically for the result of
// #mi::neuraylib::INeuray::start().
void check_start_success( mi::Sint32 result)
{
if( result == 0)
return;
fprintf( stderr, "mi::neuraylib::INeuray::start() failed with return code %d.\n", result);
keep_console_open();
exit( EXIT_FAILURE);
}
// Configures the MDL SDK by setting the default MDL search path and loading the
// freeimage plugin.
//
// \param neuray pointer to the main MDL SDK interface
void configure(mi::neuraylib::INeuray* neuray)
{
// Set the module and texture search path.
const std::string mdl_root = get_samples_mdl_root();
check_success(mdl_compiler->add_module_path(mdl_root.c_str()) == 0);
// Load the FreeImage plugin.
check_success(mdl_compiler->load_plugin_library("nv_freeimage" MI_BASE_DLL_FILE_EXT) == 0);
}
// Returns a string-representation of the given message severity
const char* message_severity_to_string(mi::base::Message_severity severity)
{
switch (severity) {
return "error";
return "warning";
return "info";
return "verbose";
return "debug";
default:
break;
}
return "";
}
// Returns a string-representation of the given message category
const char* message_kind_to_string(mi::neuraylib::IMessage::Kind message_kind)
{
switch (message_kind) {
return "MDL SDK";
return "Importer/Exporter";
return "Compiler Backend";
return "Compiler Core";
return "Compiler Archive Tool";
return "Compiler DAG generator";
default:
break;
}
return "";
}
// Prints the messages of the given context.
// Returns true, if the context does not contain any error messages, false otherwise.
bool print_messages(mi::neuraylib::IMdl_execution_context* context)
{
for (mi::Size i = 0; i < context->get_messages_count(); ++i) {
fprintf(stderr, "%s %s: %s\n",
message_kind_to_string(message->get_kind()),
message_severity_to_string(message->get_severity()),
message->get_string());
}
return context->get_error_messages_count() == 0;
}
// printf() format specifier for arguments of type LPTSTR (Windows only).
#ifdef MI_PLATFORM_WINDOWS
#ifdef UNICODE
#define FMT_LPTSTR "%ls"
#else // UNICODE
#define FMT_LPTSTR "%s"
#endif // UNICODE
#endif // MI_PLATFORM_WINDOWS
// Loads the MDL SDK and calls the main factory function.
//
// This convenience function loads the MDL SDK DSO, locates and calls the #mi_factory()
// function. It returns an instance of the main #mi::neuraylib::INeuray interface.
// The function may be called only once.
//
// \param filename The file name of the DSO. It is feasible to pass \c nullptr, which uses a
// built-in default value.
// \return A pointer to an instance of the main #mi::neuraylib::INeuray interface
mi::neuraylib::INeuray* load_and_get_ineuray( const char* filename = 0)
{
if( !filename)
filename = "libmdl_sdk" MI_BASE_DLL_FILE_EXT;
#ifdef MI_PLATFORM_WINDOWS
void* handle = LoadLibraryA((LPSTR) filename);
if( !handle) {
LPTSTR buffer = 0;
LPTSTR message = TEXT("unknown failure");
DWORD error_code = GetLastError();
if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, 0, error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buffer, 0, 0))
message = buffer;
fprintf( stderr, "Failed to load library (%u): " FMT_LPTSTR, error_code, message);
if( buffer)
LocalFree( buffer);
return 0;
}
void* symbol = GetProcAddress((HMODULE) handle, "mi_factory");
if( !symbol) {
LPTSTR buffer = 0;
LPTSTR message = TEXT("unknown failure");
DWORD error_code = GetLastError();
if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, 0, error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buffer, 0, 0))
message = buffer;
fprintf( stderr, "GetProcAddress error (%u): " FMT_LPTSTR, error_code, message);
if( buffer)
LocalFree( buffer);
return 0;
}
#else // MI_PLATFORM_WINDOWS
void* handle = dlopen( filename, RTLD_LAZY);
if( !handle) {
fprintf( stderr, "%s\n", dlerror());
return 0;
}
void* symbol = dlsym( handle, "mi_factory");
if( !symbol) {
fprintf( stderr, "%s\n", dlerror());
return 0;
}
#endif // MI_PLATFORM_WINDOWS
g_dso_handle = handle;
mi::neuraylib::INeuray* neuray = mi::neuraylib::mi_factory<mi::neuraylib::INeuray>( symbol);
if( !neuray)
{
mi::neuraylib::mi_factory<mi::neuraylib::IVersion>( symbol));
if( !version)
fprintf( stderr, "Error: Incompatible library.\n");
else
fprintf( stderr, "Error: Library version %s does not match header version %s.\n",
version->get_product_version(), MI_NEURAYLIB_PRODUCT_VERSION_STRING);
return 0;
}
return neuray;
}
// Unloads the MDL SDK.
bool unload()
{
#ifdef MI_PLATFORM_WINDOWS
int result = FreeLibrary( (HMODULE)g_dso_handle);
if( result == 0) {
LPTSTR buffer = 0;
LPTSTR message = TEXT("unknown failure");
DWORD error_code = GetLastError();
if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, 0, error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buffer, 0, 0))
message = buffer;
fprintf( stderr, "Failed to unload library (%u): " FMT_LPTSTR, error_code, message);
if( buffer)
LocalFree( buffer);
return false;
}
return true;
#else
int result = dlclose( g_dso_handle);
if( result != 0) {
printf( "%s\n", dlerror());
return false;
}
return true;
#endif
}
// Sleep the indicated number of seconds.
void sleep_seconds( mi::Float32 seconds)
{
#ifdef MI_PLATFORM_WINDOWS
Sleep( static_cast<DWORD>( seconds * 1000));
#else
usleep( static_cast<useconds_t>( seconds * 1000000));
#endif
}
// Map snprintf to _snprintf on Windows.
#ifdef MI_PLATFORM_WINDOWS
#define snprintf _snprintf
#endif
std::string get_working_directory()
{
char current_path[FILENAME_MAX];
#ifdef MI_PLATFORM_WINDOWS
_getcwd(current_path, FILENAME_MAX);
#else
getcwd(current_path, FILENAME_MAX); // TODO
#endif
return current_path;
}
// Returns the folder path of the current executable.
std::string get_executable_folder()
{
#ifdef MI_PLATFORM_WINDOWS
char path[MAX_PATH];
if (!GetModuleFileNameA(nullptr, path, MAX_PATH))
return "";
const char sep = '\\';
#else // MI_PLATFORM_WINDOWS
char path[4096];
#ifdef MI_PLATFORM_MACOSX
uint32_t buflen(sizeof(path));
if (_NSGetExecutablePath(path, &buflen) != 0)
return "";
#else // MI_PLATFORM_MACOSX
char proc_path[64];
#ifdef __FreeBSD__
snprintf(proc_path, sizeof(proc_path), "/proc/%d/file", getpid());
#else
snprintf(proc_path, sizeof(proc_path), "/proc/%d/exe", getpid());
#endif
ssize_t written = readlink(proc_path, path, sizeof(path));
if (written < 0 || size_t(written) >= sizeof(path))
return "";
path[written] = 0; // add terminating null
#endif // MI_PLATFORM_MACOSX
const char sep = '/';
#endif // MI_PLATFORM_WINDOWS
char *last_sep = strrchr(path, sep);
if (last_sep == nullptr) return "";
return std::string(path, last_sep + 1);
}
namespace
{
#ifdef MI_PLATFORM_WINDOWS
//-----------------------------------------------------------------------------
// helper function to create standard mdl path inside the known folder. WINDOWS only
//
std::string get_known_folder(const KNOWNFOLDERID& id, const std::string& postfix)
{
// Fetch the 'knownFolder' path.
HRESULT hr = -1;
wchar_t* knownFolderPath = nullptr;
std::string result;
#if(_WIN32_WINNT >= 0x0600)
hr = SHGetKnownFolderPath(id, 0, nullptr, &knownFolderPath);
#endif
if (SUCCEEDED(hr))
{
// convert from wstring to string and append the postfix
std::wstring s(knownFolderPath);
int len;
int slength = (int) s.length();
len = WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, 0, 0, 0, 0);
result = std::string(len, '\0');
WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, &result[0], len, 0, 0);
result.append(postfix);
CoTaskMemFree(static_cast<void*>(knownFolderPath));
}
return result;
}
#endif // MI_PLATFORM_WINDOWS
std::vector<std::string> string_split(const std::string& input, char sep)
{
size_t offset(0);
size_t pos(0);
while (pos != std::string::npos)
{
pos = input.find(sep, offset);
if (pos == std::string::npos)
{
chunks.push_back(input.substr(offset));
break;
}
chunks.push_back(input.substr(offset, pos - offset));
offset = pos + 1;
}
return chunks;
}
}
std::vector<std::string> get_mdl_admin_space_search_paths()
{
std::string paths = get_environment("MDL_SYSTEM_PATH");
if (!paths.empty())
{
std::vector<std::string> result = string_split(paths, ';');
return result;
}
// default paths on the different platforms
#if defined(MI_PLATFORM_WINDOWS)
result.emplace_back(get_known_folder(FOLDERID_ProgramData, "/NVIDIA Corporation/mdl"));
#elif defined(MI_PLATFORM_UNIX)
result.emplace_back("/opt/nvidia/mdl");
#elif defined(MI_PLATFORM_MACOSX)
result.emplace_back("/Library/Application Support/NVIDIA Corporation/mdl");
#endif
return result;
}
std::vector<std::string> get_mdl_user_space_search_paths()
{
std::string paths = get_environment("MDL_USER_PATH");
if (!paths.empty())
{
std::vector<std::string> result = string_split(paths, ';');
return result;
}
// default paths on the different platforms
#if defined(MI_PLATFORM_WINDOWS)
result.emplace_back(get_known_folder(FOLDERID_Documents, "/mdl"));
#else
const std::string home = getenv("HOME");
result.emplace_back(home + "/Documents/mdl");
#endif
return result;
}
#endif // MI_EXAMPLE_SHARED_H

Source Code Location: examples/mdl_sdk/start_shutdown/example_start_shutdown.cpp

/******************************************************************************
* Copyright 2018 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_start_shutdown.cpp
//
// Obtain an INeuray interface, start the MDL SDK and shut it down.
#include <mi/mdl_sdk.h>
// Include code shared by all examples.
#include "example_shared.h"
// The main function initializes the MDL SDK, starts it, and shuts it down after waiting for
// user input.
int main( int /*argc*/, char* /*argv*/[])
{
// Get the INeuray interface in a suitable smart pointer.
mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
if( !neuray.is_valid_interface()) {
fprintf( stderr, "Error: The MDL SDK library failed to load and to provide "
"the mi::neuraylib::INeuray interface.\n");
keep_console_open();
return EXIT_FAILURE;
}
// Print library version information.
fprintf( stderr, "MDL SDK header version = %s\n",
fprintf( stderr, "MDL SDK library product name = %s\n", version->get_product_name());
fprintf( stderr, "MDL SDK library product version = %s\n", version->get_product_version());
fprintf( stderr, "MDL SDK library build number = %s\n", version->get_build_number());
fprintf( stderr, "MDL SDK library build date = %s\n", version->get_build_date());
fprintf( stderr, "MDL SDK library build platform = %s\n", version->get_build_platform());
fprintf( stderr, "MDL SDK library version string = \"%s\"\n", version->get_string());
mi::base::Uuid neuray_id_libraray = version->get_neuray_iid();
fprintf( stderr, "MDL SDK header interface ID = <%2x, %2x, %2x, %2x>\n",
neuray_id_interface.m_id1,
neuray_id_interface.m_id2,
neuray_id_interface.m_id3,
neuray_id_interface.m_id4);
fprintf( stderr, "MDL SDK library interface ID = <%2x, %2x, %2x, %2x>\n\n",
neuray_id_libraray.m_id1,
neuray_id_libraray.m_id2,
neuray_id_libraray.m_id3,
neuray_id_libraray.m_id4);
version = 0;
// configuration settings go here, none in this example
// After all configurations, the MDL SDK is started. A return code of 0 implies success. The
// start can be blocking or non-blocking. Here the blocking mode is used so that you know that
// the MDL SDK is up and running after the function call. You can use a non-blocking call to do
// other tasks in parallel and check with
//
// neuray->get_status() == mi::neuraylib::INeuray::STARTED
//
// if startup is completed.
mi::Sint32 result = neuray->start( true);
check_start_success( result);
// scene graph manipulations and rendering calls go here, none in this example.
// Shutting down in blocking mode. Again, a return code of 0 indicates success.
check_success( neuray->shutdown( true) == 0);
neuray = 0;
// Unload the MDL SDK
check_success( unload());
keep_console_open();
return EXIT_SUCCESS;
}
[Up] [Next]