MDL SDK API nvidia_logo_transpbg.gif Up
Example for MDL Modules
[Previous] [Up] [Next]

This example loads an MDL module and scan for selected information, such as its import, resource dependencies and more.

Using the command line, we can specify which details of a module to extract, such as its imports, type and constant definitions, material and function declarations, as well as resource dependencies. Additionally, the tool can optionally perform a recursive scan of the imported modules.

New Topics

  • Configuration of the MDL SDK
  • Loading of MDL modules
  • Enumerating the contents of MDL modules
  • Inspecting the parameters of material and function definitions

Detailed Description

Configuration of the MDL SDK


In order to allow the the MDL SDK to locate MDL modules and its resources in the file system it is necessary to configure the MDL module search path. This is done by calling mi::neuraylib::IMdl_configuration::add_mdl_path().

Our example module ::nvidia::sdk_examples::tutorials makes use of textures. The support for image formats in the the MDL SDK is provided by image plugins, which have to be loaded explicitly via mi::neuraylib::IPlugin_configuration::load_plugin_library().

In this and most other examples the configuration is done within the utility function configure(). Here, we set the module search path to the MDL directory shipped with the examples. We also load the OpenImageIO plugin which comes with the the MDL SDK and is capable of handling .png files.

Loading of MDL modules


MDL modules are loaded with the method mi::neuraylib::IMdl_impexp_api::load_module(). Note that this method expects the module name, not the name of the file containing the module. To find the corresponding file it is necessary to configure the module search path as described above.

Enumerating the contents of MDL modules


The mi::neuraylib::IModule interface offers various methods related to metadata, like the name of the module, or the name of the file it was loaded from. Furthermore, it offers methods to enumerate the material and function definitions contained in the module. It also allows to enumerate all modules that have been (directly) imported by that module. Imported modules will be automatically loaded (if not already loaded before).

Inspecting the parameters of material and function definitions


MDL materials and functions (as found in a .mdl file) are represented in the MDL SDK API by the interface mi::neuraylib::IFunction_definition. Similar to the module interface this interface offers various methods related to metadata, like the name of the definition, or the name of the containing module. Important methods are those that provide details about the parameters, like their names, types, and the values of defaults (if present).

In this example, you can first retrieve a list of function and material definitions using the filter option "-f definitions". Afterward, you can select specific functions or materials to extract detailed information, such as parameters with their default values, temporaries, and the body. By using the "-d substring" filter option, you can narrow down the selection to only those function and material definitions that contain the specified substring.

The textual representation of the default values, temporaries and body is generated by the method mi::neuraylib::IExpression_factory::dump().

Enumerating resources required by a module


The example also demonstrates the iteration of required resources. After reading the number of resources, we can iterate them and get the database name, the mdl file path, and the resource type. Based on the latter, it is possible to fetch the resolved resource path that points to the file on the current system. In case the resource could not be resolved, the database name will be nullptr. However, the MDL file path will still point to the missing resource.

Example Source

Source Code Location: examples/mdl_sdk/modules/example_modules.cpp

/******************************************************************************
* Copyright 2025 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/mdl_sdk/modules/example_modules.cpp
//
// Loads an MDL module and scan for its imports, resource dependencies and more...
#include <iostream>
#include <string>
#include <unordered_set>
#include "example_shared.h"
// Dump options. The type of information that can be selected to be dump from a module
enum Dump_options
{
DUMP_NOTHING = 0x00,
DUMP_IMPORTS = 0x01,
DUMP_TYPES = 0x02,
DUMP_CONSTANTS = 0x04,
DUMP_DEFINITIONS = 0x08,
DUMP_RESOURCES = 0x10,
DUMP_ALL = 0xFF
};
class Options
{
public:
static void print_usage(std::ostream& s);
bool parse(int argc, char* argv[]);
std::string m_qualified_module_name = "::nvidia::sdk_examples::tutorials";
bool m_recursive = false; // scan modules recursively.
bool m_skip_standard = false; // skip standard modules.
int m_dump_options = DUMP_IMPORTS | DUMP_RESOURCES;
// Dump function/material definitions containing the m_def_filter substring keyword.
std::string m_def_filter = "";
bool m_nostdpath = false;
std::vector<std::string> m_mdl_paths;
};
// This helper class contains some methods to scan and dump selected information from
// an MDL module and its imports (recursively). It keeps track of modules already visited
// in order to avoid repeated outputs.
class MDL_module_scanner
{
public:
// MDL_module_scanner constructor.
MDL_module_scanner(mi::neuraylib::INeuray* neuray, const Options& options);
// The entry point for scanning an MDL module.
void scan_module(const std::string& qualified_module_name);
private:
// Dump the parameters of a material or function definition.
void dump_definition(
mi::Size depth,
std::ostream& s);
// Dumps selective information of an MDL module and optionally its imports (recursively).
void dump_module(
const mi::neuraylib::IModule* module,
mi::Size level);
Options m_options;
// List of modules already visited.
std::unordered_set<std::string> m_module_list;
};
// MDL_module_scanner constructor.
MDL_module_scanner::MDL_module_scanner(mi::neuraylib::INeuray* neuray, const Options& options)
: m_neuray(neuray)
, m_options(options)
{
m_mdl_factory = m_neuray->get_api_component<mi::neuraylib::IMdl_factory>();
}
// The entry point for scanning an MDL module.
void MDL_module_scanner::scan_module(const std::string& qualified_module_name)
{
// Access the database and scope.
database(m_neuray->get_api_component<mi::neuraylib::IDatabase>());
scope(database->get_global_scope());
// Create execution context.
mdl_impexp_api(m_neuray->get_api_component<mi::neuraylib::IMdl_impexp_api>());
context(m_mdl_factory->create_execution_context());
// Create a transaction.
m_transaction = scope->create_transaction();
{
// Load the module.
check_success(mdl_impexp_api->load_module(
m_transaction.get(), qualified_module_name.c_str(), context.get()) >= 0);
print_messages(context.get());
// Get module database name.
m_mdl_factory->get_db_module_name(qualified_module_name.c_str()));
m_transaction->access<mi::neuraylib::IModule>(module_db_name->get_c_str()));
dump_module(module.get(), 0);
}
// All transactions need to get committed.
m_transaction->commit();
}
// Dump the parameters of a material or function definition.
void MDL_module_scanner::dump_definition(
mi::Size level,
std::ostream& s)
{
const std::string shift(level * 4 + 4, ' ');
m_mdl_factory->create_type_factory(m_transaction.get()));
m_mdl_factory->create_expression_factory(m_transaction.get()));
const mi::Size count = definition->get_parameter_count();
for (mi::Size index = 0; index < count; index++)
{
mi::base::Handle<const mi::neuraylib::IType> type(types->get_type(index));
mi::base::Handle<const mi::IString> type_text(type_factory->dump(type.get(), level + 1));
const std::string name = definition->get_parameter_name(index);
s << shift << "parameter " << type_text->get_c_str() << " " << name;
defaults->get_expression(name.c_str()));
if (default_.is_valid_interface())
{
expression_factory->dump(default_.get(), 0, level + 1));
s << ", default = " << default_text->get_c_str() << std::endl;
}
else
{
s << " (no default)" << std::endl;
}
}
const mi::Size temporary_count = definition->get_temporary_count();
for (mi::Size i = 0; i < temporary_count; ++i)
{
std::stringstream name;
name << i;
expression_factory->dump(temporary.get(), name.str().c_str(), 1));
s << shift << "temporary " << result->get_c_str() << std::endl;
}
mi::base::Handle<const mi::IString> result(expression_factory->dump(body.get(), 0, 1));
if (result)
s << shift << "body " << result->get_c_str() << std::endl;
else
s << shift << "body not available for this function" << std::endl;
s << std::endl;
}
// Dump selective information of an MDL module and optionally its imports (recursively).
void MDL_module_scanner::dump_module(
const mi::neuraylib::IModule* module,
mi::Size level)
{
const std::string separator(120 - level * 4, '-');
const std::string shift(level * 4, ' ');
// Get module display name.
// Note that IModule::get_mdl_name() only returns the module MDL name which could be encoded.
// Then IMdl_factory::decode_name() has to be used for display purposes.
decoded_name(m_mdl_factory->decode_name(module->get_mdl_name()));
const std::string module_name = decoded_name->get_c_str();
// Print the module name and the file name it was loaded from.
std::cout << shift << separator << std::endl;
if (module->is_standard_module())
{
std::cout << shift << "Found module: " << module_name << " (Standard Module)" << std::endl;
}
else
{
std::cout << shift << "Found module: " << module_name << std::endl;
const char* filename = module->get_filename();
if (filename)
std::cout << shift << "Loaded file : " << filename << std::endl;
}
std::cout << std::endl;
// Dump imported modules.
const mi::Size module_count = module->get_import_count();
if (module_count > 0 && m_options.m_dump_options & DUMP_IMPORTS)
{
std::cout << shift << "+Imports included in " << module_name << ":" << std::endl;
for (mi::Size i = 0; i < module_count; i++)
{
const std::string import_name(module->get_import(i));
m_transaction->access<mi::neuraylib::IModule>((import_name.c_str())));
decoded_name(m_mdl_factory->decode_name(imported_module->get_mdl_name()));
const std::string imported_name = decoded_name->get_c_str();
std::cout << shift << " " << imported_name << std::endl;
}
std::cout << std::endl;
}
// Dump exported types.
if (m_options.m_dump_options & DUMP_TYPES)
{
m_mdl_factory->create_type_factory(m_transaction.get()));
const mi::Size num_types = types->get_size();
if (num_types > 0)
{
std::cout << shift << "+Types defined in " << module_name << ":" << std::endl;
for (mi::Size i = 0; i < num_types; ++i)
{
mi::base::Handle<const mi::IString> result(type_factory->dump(type.get(), level + 1));
std::cout << shift << " " << result->get_c_str() << std::endl;
}
std::cout << std::endl;
}
}
// Dump exported constants.
if (m_options.m_dump_options & DUMP_CONSTANTS)
{
m_mdl_factory->create_value_factory(m_transaction.get()));
const mi::Size num_constants = constants->get_size();
if (num_constants > 0)
{
std::cout << shift << "+Constants defined in " << module_name << ":" << std::endl;
for (mi::Size i = 0; i < num_constants; ++i)
{
const char* name = constants->get_name(i);
mi::base::Handle<const mi::neuraylib::IValue> constant(constants->get_value(i));
mi::base::Handle<const mi::IString> result(value_factory->dump(constant.get(), 0, level + 1));
std::cout << shift << " " << name << " = " << result->get_c_str() << std::endl;
}
std::cout << std::endl;
}
}
// Dump function definitions of the module.
const mi::Size function_count = module->get_function_count();
if (function_count > 0 && m_options.m_dump_options & DUMP_DEFINITIONS)
{
std::cout << shift << "+Functions defined in " << module_name << ":" << std::endl;
for (mi::Size i = 0; i < function_count; i++)
{
const std::string function_name(module->get_function(i));
const bool apply_filter = !m_options.m_def_filter.empty() &&
(function_name.find(m_options.m_def_filter) != std::string::npos);
if (m_options.m_def_filter.empty() || apply_filter)
{
std::cout << shift << " " << function_name << std::endl;
m_transaction->access<mi::neuraylib::IFunction_definition>(function_name.c_str()));
if (apply_filter)
dump_definition(function_definition.get(), level + 1, std::cout);
// Dump thumbnail filename is available.
const char* thumbnail = function_definition->get_thumbnail();
if (thumbnail)
std::cout << shift << " Thumbnail: " << thumbnail << std::endl;
}
}
std::cout << std::endl;
}
// Dump material definitions of the module.
const mi::Size material_count = module->get_material_count();
if (material_count > 0 && m_options.m_dump_options & DUMP_DEFINITIONS)
{
std::cout << shift << "+Materials defined in " << module_name << ":" << std::endl;
for (mi::Size i = 0; i < material_count; i++)
{
const std::string material_name(module->get_material(i));
const bool apply_filter = !m_options.m_def_filter.empty() &&
(material_name.find(m_options.m_def_filter) != std::string::npos);
if (m_options.m_def_filter.empty() || apply_filter)
{
std::cout << shift << " " << material_name << std::endl;
m_transaction->access<mi::neuraylib::IFunction_definition>(material_name.c_str()));
if (apply_filter)
dump_definition(material_definition.get(), level + 1, std::cout);
// Dump thumbnail filename is available.
const char* thumbnail = material_definition->get_thumbnail();
if (thumbnail)
std::cout << shift << " Thumbnail: " << thumbnail << std::endl;
}
}
std::cout << std::endl;
}
// Dump the resources referenced by this module.
const mi::Size resources_count = module->get_resources_count();
if (resources_count > 0 && m_options.m_dump_options & DUMP_RESOURCES)
{
std::cout << shift << "+Resources used in " << module_name << ":" << std::endl;
for (mi::Size r = 0; r < resources_count; ++r)
{
module->get_resource(r));
const char* db_name = resource->get_value();
const char* mdl_file_path = resource->get_file_path();
if (db_name == nullptr)
{
// resource is either not used and therefore has not been loaded or
// could not be found.
std::cout << shift << " db_name: none" << std::endl;
std::cout << shift << " mdl_file_path: " << mdl_file_path << std::endl
<< std::endl;
continue;
}
std::cout << shift << " db_name: " << db_name << std::endl;
std::cout << shift << " mdl_file_path: " << mdl_file_path << std::endl;
resource->get_type());
switch (type->get_kind())
{
{
m_transaction->access<mi::neuraylib::ITexture>(db_name));
m_transaction->access<mi::neuraylib::IImage>(texture->get_image()));
for (mi::Size f = 0, fn = image->get_length(); f < fn; ++f)
for (mi::Size t = 0, tn = image->get_frame_length(f); t < tn; ++t)
{
const char* resolved_file_path = image->get_filename(f, t);
if (resolved_file_path)
{
std::cout << shift << " resolved_file_path[" << f << "," << t << "]: "
<< resolved_file_path << std::endl;
}
}
break;
}
{
m_transaction->access<mi::neuraylib::ILightprofile>(db_name));
const char* resolved_file_path = light_profile->get_filename();
if (resolved_file_path)
std::cout << shift << " resolved_file_path: " << resolved_file_path << std::endl;
break;
}
{
m_transaction->access<mi::neuraylib::IBsdf_measurement>(db_name));
const char* resolved_file_path = mbsdf->get_filename();
if(resolved_file_path)
std::cout << shift << " resolved_file_path: " << resolved_file_path << std::endl;
break;
}
default:
exit_failure(std::string("Unexpected Resource type: " +
std::to_string(type->get_kind())).c_str());
break;
}
std::cout << std::endl;
}
}
// Dump imported modules recursively.
if (m_options.m_recursive)
{
const mi::Size module_count = module->get_import_count();
for (mi::Size i = 0; i < module_count; i++)
{
const std::string db_name(module->get_import(i));
// Skip modules already visited.
auto it = std::find(m_module_list.begin(), m_module_list.end(), db_name);
if (it != m_module_list.end())
continue;
// Update module list.
m_module_list.insert(db_name);
// Access and dump imported module.
{
m_transaction->access<mi::neuraylib::IModule>(db_name.c_str()));
if (m_options.m_skip_standard && imported_module->is_standard_module())
continue;
dump_module(imported_module.get(), level + 1);
}
}
}
std::cout << std::endl;
}
// Print command line usage.
void Options::print_usage(std::ostream& s)
{
s << R"(
code_gen [options] <qualified_module_name>
options:
-h|--help Print this usage message and exit.
-p|--mdl_path <path> Add the given path to the MDL search path.
-n|--nostdpath Prevent adding the MDL system and user search
path(s) to the MDL search path.
-f|--filter Select type of information to be dumped.
Format: -f "type1|type2|...|typeN".
Types: 'imports', 'types', 'constants', definitions', 'resources',
'all'.
Default: "imports|resources".
-r|--recursive Scan imported modules recursively.
-s|--skip_standard Skip standard modules.
-d|--filter_definitions If 'definitions' have been included with the '-f' option, this extra
filter will select only function and material definitions with names
that contain the "substring", and for the previous, it will dump
extended information like parameters with its defaults, temporaries
and the body.
Format: -d "substring")";
s << std::endl << std::endl;
}
// Parse command line options.
bool Options::parse(int argc, char* argv[])
{
for (int i = 1; i < argc; ++i)
{
const std::string arg = argv[i];
if (arg[0] == '-')
{
if (arg == "-h" || arg == "--help")
{
return false;
}
else if (arg == "-n" || arg == "--nostdpath")
{
m_nostdpath = true;
}
else if (arg == "-p" || arg == "--mdl_path")
{
if (i == argc - 1)
{
std::cerr << "error: Argument for -p|--mdl_path missing." << std::endl;
return false;
}
m_mdl_paths.push_back(argv[++i]);
}
else if (arg == "-f" || arg == "--filter")
{
if (i == argc - 1)
{
std::cerr << "error: Argument for -f|--filter missing." << std::endl;
return false;
}
std::istringstream ss(argv[++i]);
std::string type;
m_dump_options = DUMP_NOTHING;
while (std::getline(ss, type, '|'))
{
if (type == "imports")
{
m_dump_options |= DUMP_IMPORTS;
}
else if (type == "types")
{
m_dump_options |= DUMP_TYPES;
}
else if (type == "constants")
{
m_dump_options |= DUMP_CONSTANTS;
}
else if (type == "definitions")
{
m_dump_options |= DUMP_DEFINITIONS;
}
else if (type == "resources")
{
m_dump_options |= DUMP_RESOURCES;
}
else if (type == "all")
{
m_dump_options |= DUMP_ALL;
}
else
{
std::cerr << "error: Unknown filter type: " << type << "." << std::endl;
return false;
}
}
}
else if (arg == "-r" || arg == "--recursive")
{
m_recursive = true;
}
else if (arg == "-s" || arg == "--skip_standard")
{
m_skip_standard = true;
}
else if (arg == "-d" || arg == "--filter_definitions")
{
if (i == argc - 1)
{
std::cerr << "error: Argument for -d|--filter_definitions missing." << std::endl;
return false;
}
m_def_filter = argv[++i];
}
else
{
std::cerr << "error: Unknown option \"" << arg << "\"." << std::endl;
return false;
}
}
else
{
if (i == argc - 1)
m_qualified_module_name = arg;
}
}
return true;
}
// Main function.
int MAIN_UTF8(int argc, char* argv[])
{
// Parse command line options.
Options options;
if (!options.parse(argc, argv))
{
options.print_usage(std::cout);
exit_failure("Failed to parse command line arguments.");
}
// Access the MDL SDK.
mi::base::Handle<mi::neuraylib::INeuray> neuray(mi::examples::mdl::load_and_get_ineuray());
if (!neuray.is_valid_interface())
exit_failure("Failed to load the SDK.");
// Configure the MDL SDK.
mi::examples::mdl::Configure_options configure_options;
// Apply the search path setup described on the command line.
configure_options.additional_mdl_paths = options.m_mdl_paths;
if (options.m_nostdpath)
{
configure_options.add_admin_space_search_paths = false;
configure_options.add_user_space_search_paths = false;
configure_options.add_example_search_path = false;
}
if (!mi::examples::mdl::configure(neuray.get(), configure_options))
exit_failure("Failed to initialize the SDK.");
// Start the MDL SDK.
mi::Sint32 ret = neuray->start();
if (ret != 0)
exit_failure("Failed to initialize the SDK. Result code: %d", ret);
// Load an MDL module to scan and dump selected information.
{
MDL_module_scanner module_scanner(neuray.get(), options);
module_scanner.scan_module(options.m_qualified_module_name);
}
// Shut down the MDL SDK.
if (neuray->shutdown() != 0)
exit_failure("Failed to shutdown the SDK.");
// Unload the MDL SDK.
neuray = nullptr;
if (!mi::examples::mdl::unload())
exit_failure("Failed to unload the SDK.");
exit_success();
}
// Convert command line arguments to UTF8 on Windows.
COMMANDLINE_TO_UTF8
A scene element that stores measured BSDF data.
Definition: ibsdf_measurement.h:39
This interface is used to interact with the distributed database.
Definition: idatabase.h:363
This interface represents a function definition.
Definition: ifunction_definition.h:44
virtual const IExpression * get_temporary(Size index) const =0
Returns the expression of a temporary.
virtual Size get_parameter_count() const =0
Returns the number of parameters.
virtual const IExpression * get_body() const =0
Returns the expression that represents the body of the function (if possible).
virtual Size get_temporary_count() const =0
Returns the number of temporaries used by this function.
virtual const IType_list * get_parameter_types() const =0
Returns the types of all parameters.
virtual const char * get_parameter_name(Size index) const =0
Returns the name of the parameter at index.
virtual const IExpression_list * get_defaults() const =0
Returns the defaults of all parameters.
This interface represents a pixel image file.
Definition: iimage.h:66
This interface represents light profiles.
Definition: ilightprofile.h:73
Factory for various MDL interfaces and functions.
Definition: imdl_factory.h:53
API component for MDL related import and export operations.
Definition: imdl_impexp_api.h:43
This interface represents an MDL module.
Definition: imodule.h:634
virtual const IValue_resource * get_resource(Size index) const =0
Returns a resource defined in the module.
virtual Size get_function_count() const =0
Returns the number of function definitions exported by the module.
virtual const char * get_function(Size index) const =0
Returns the DB name of the function definition at index.
virtual bool is_standard_module() const =0
Indicates whether this module is a standard module.
virtual const IValue_list * get_constants() const =0
Returns the constants exported by this module.
virtual const char * get_filename() const =0
Returns the name of the MDL source file from which this module was created.
virtual Size get_import_count() const =0
Returns the number of modules imported by the module.
virtual const char * get_material(Size index) const =0
Returns the DB name of the material definition at index.
virtual const char * get_import(Size index) const =0
Returns the DB name of the imported module at index.
virtual const char * get_mdl_name() const =0
Returns the MDL name of the module.
virtual Size get_material_count() const =0
Returns the number of material definitions exported by the module.
virtual const IType_list * get_types() const =0
Returns the types exported by this module.
virtual Size get_resources_count() const =0
Returns the number of resources defined in the module.
This is an object representing the MDL SDK library.
Definition: ineuray.h:44
Textures add image processing options to images.
Definition: itexture.h:68
@ TK_TEXTURE
A texture type. See mi::neuraylib::IType_texture.
Definition: itype.h:183
@ TK_BSDF_MEASUREMENT
The bsdf_measurement type. See mi::neuraylib::IType_bsdf_measurement.
Definition: itype.h:187
@ TK_LIGHT_PROFILE
The light_profile type. See mi::neuraylib::IType_light_profile.
Definition: itype.h:185
int Sint32
32-bit signed integer.
Definition: types.h:46
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
[Previous] [Up] [Next]