Share

Deferred Loading of Plug-ins

Plug-in DLL Defer Loading

A defer loaded plug-in DLL will not be loaded into memory until required by 3ds Max for the first time. Deferring a plug-in is a useful technique for improving the start-up time and reducing the memory footprint of 3ds Max. This is why 3ds Max uses automatic defer loading system. If a plug-in DLL is deferred, it should not impact the user experience. For example:

  • The set of user interface elements (menus, hotkeys, buttons, list of available modifiers, list of file types available to import/export, etc), the set of MAXScript commands, etc, should be the same as if the plug-in would be loaded.
  • Support for loading scenes, material libraries or other file based assets should be the same as if the plug-in would be loaded.

Certain plug-in types that are cached (proxied out) by 3ds Max need all their data to be constant to prevent run time issues when defer loaded. 3ds Max will still defer load the plug-in if those values are not constant, but the user may experience unexpected behavior in certain 3ds Max features. One example is the input type of a modifier plugin. If the type of an input object used by a modifier changes at run-time, then the user may not be able to apply the deferred modifier on certain object types. For the correct behavior to re-establish, the modifier plug-in needs to be loaded.

If it is not possible for a plug-in to be defer loaded without impacting the user experience, then it is the responsibility of a plug-in developer to opt out of the automatic defer loading system. This can be done by returning FALSE from the DLL exported function CanAutoDefer() as following:

// The plug-in opts out from 3ds Max\'s defer loading mechanism, 
// i.e. it\'s always loaded when 3ds Max starts up.
__declspec( dllexport ) ULONG CanAutoDefer()
{
   return FALSE;
}

You will also need to add this line to the *.DEF file. Example:

 CanAutoDefer @5

Loading of a plugin however will not be defered, even if CanAutoDefer() function returns true, if the plug-in:

  • exposes structures, functions, properties, etc to Maxscript using pre-Function Publishing technology, such as the def_struct_primitive, def_visible_primitive, def_property, etc macros. See the documentation of the file define_abstract_functions.h
  • register with 3ds Max app-data readers - see ObjectDataReaderCallback

Although 3ds Max implements some heuristics to detect if the plug-in can be safely deferred, plug-in developers are ultimately responsible for detecting violations of this requirement and explicitly disallow 3ds Max from defer loading the plug-in.

The 3dsmax.ini Flags

Developers can turn on or off specific plug-in defer loading features using certain flags defined in the file 3dsmax.ini. These features are:

  • The ability to toggle between the legacy opt-in mechanism and the new opt-out mechanism.
  • The ability to defer load plug-ins that expose action tables (action items - an action table contains action items, action items always live in an action table).
  • The ability to defer load plug-in that expose core interfaces. See Core Interfaces.
  • The ability to defer load plug-ins that listen to startup the NOTIFY_SYSTEM_STARTUP notification. See the RegisterNotification() global function.

These flags are introduced in the example below.

[PluginSettings]
; Controls whether plug-ins DLL that do not implement ULONG CanAutoDefer are defer loaded (1) or not (0)
CanAutoDeferDefaultValue=1 
; Controls whether plug-in DLLs that expose Action Table (action items) can be defer loaded (1) or not (0)
EnableActionItemProxySystem=1
; Controls whether plug-in DLLs that function publish "core" interfaces can be defer loaded (1) or not (0)
EnableCoreInterfaceProxySystem=1
; Controls whether plug-in DLLs that rely on startup notifications can be defer loaded (1) or not (0)
EnableStartupNotificationReplaySystem=1

The Plug-In Manager feature of 3ds Max shows the user whether a plug-in DLL has been deferred or loaded.

Common Problems and Solutions

Plug-in Registers Callbacks and Objects with 3ds Max

Besides action tables and core interfaces, a plug-in may need to register with 3ds Max other callbacks and objects that affect the user interface such as, but not limited to:

  • Action item override names: See IActionItemOverrideManager::ActivateActionItemOverride().
  • Geometry Checkers: See IGeometryCheckerManager::RegisterGeometryChecker().
  • Drag and drop handlers: See class DragAndDropHandler.
  • Menus other than those based on action items: See IMenuManager::RegisterMenu().
  • Viewport buttons: See IViewportButtonManager::RegisterButton().
  • Trackbar filters: See ITrackBarFilterManager:: RegisterFilter().
  • Object converters and "Collapse" types: See RegisterObjectConverter(), RegisterStaticEditTri() , RegisterEditTriObjDesc() and RegisterEditPolyObjDesc().

At this time 3ds Max's plug-in loading mechanism does not detect these cases and as a result, it will defer load such a plug-in DLL unless it explicitly disallows deferring.

Load Order Dependencies between Plug-ins

The deferred plug-in DLLs are loaded when a certain user action requires them to be present. Because the sequence of user actions is not always predictable, the order in which deferred plug-in DLLs are loaded by 3ds Max will not be deterministic. In general, Plug-in DLL A has a load order dependency on plug-in DLL B if B needs to be loaded in 3ds Max's address space before A. If plug-in DLL A calls a function exported from plug-in DLL B, a load order dependency will exist between the two plug-in DLLs.

If the plug-in DLL B is defer loaded:

  • Plug-in DLL A may fail to be loaded by the operating system's loader. In thiscase error 126 (The specified module could not be found) is displayed by the operating system. This is the case if plug-in DLL A links against plug-in DLL B.
  • A run-time error may occur if plug-in DLL A tries to acquire the address of a function exported by plug-in DLL B (with a call to WINAPI GetProcAddress()) and call that function.

The solution to this problem is to eliminate the load order dependency between the two plug-in DLLs by:

  • Moving the functionality that the plug-in DLL A (and possibly plug-in DLL B) relies on into a new DLL C that is not a plug-in and that any plug-in DLLs can either link against or load at runtime.
    • The DLL containing the common functionality should not be delay loaded if it exposes core interfaces to MAXScript. The delay loading mechanism may result in the core interface not being properly registered and 3ds Max may crash when client code requests such an interface.
  • Exposing the functionality plug-in DLL A relies on as a core interface (an object that derives from FPStaticInterface and is created with the FP_CORE flag). Plug-in DLLs that expose core interfaces are guaranteed to be loaded on demand, when client code acquires the core interface they expose.

User Defined Plug-in Architectures

One example of a plug-in architecture that does not work well with plug-in DLL defer loading is when a 3ds Max plug-in loads a DLL at run-time that extend the plug-in's functionality and the plug-in exposes this functionality in the 3ds Max user interface.

When the plug-in DLL is deferred, extensions in the form of DLLs installed since the last session of 3ds Max may not show up in the user interface, until some user action triggers the loading of the plug-in DLL that depends on these extensions.

Was this information helpful?