Modify mj_PLUGIN_LIB_INIT to support multiple plugins in the same compilation unit.

Previously on MSVC we used DllMain to register plugins, you cannot have multiple definitions of DllMain in a single unit so you would get errors if you tried to register two plugins. This modifies the implementation to insert a function pointer into the C runtime initialization instead.

PiperOrigin-RevId: 896612678
Change-Id: I07732147b955d741c836acff6da986db0e9b9eff
This commit is contained in:
Sam Haves
2026-04-08 11:34:33 -07:00
committed by Copybara-Service
parent 33fd2fe40e
commit f2461f9ce6
14 changed files with 60 additions and 53 deletions
+6 -10
View File
@@ -691,17 +691,13 @@ mjPLUGIN_LIB_INIT
.. code-block:: C
#define mjPLUGIN_LIB_INIT \
static void _mjplugin_dllmain(void); \
mjEXTERNC int __stdcall mjDLLMAIN(void* hinst, unsigned long reason, void* reserved) { \
if (reason == 1) { \
_mjplugin_dllmain(); \
} \
return 1; \
} \
static void _mjplugin_dllmain(void)
#define mjPLUGIN_LIB_INIT(n) \
static void _mj_init_##n(void) __attribute__((constructor)); \
static void _mj_init_##n(void)
Register a plugin as a dynamic library. See :ref:`plugin registration<exRegistration>` for more details.
Register a plugin before `main()` is called. This macro takes a unique identifier `n` as an argument that is used to avoid
name collisions between different plugin initialization functions. See :ref:`plugin registration<exRegistration>` for
more details.
.. _tyXMacro:
+4
View File
@@ -44,6 +44,10 @@ General
- The ``vertcollide`` field in :ref:`mjsFlex` has been removed. It is no longer required since
:doc:`MuJoCo Warp <mjwarp/index>` supports native flex collisions.
- :ref:`mjPLUGIN_LIB_INIT` macro now requires a name argument to avoid initialization function name collisions.
When building with MSVC, we now use the C runtime initialization section to initialize plugins instead of
``DllMain``. See :ref:`plugin registration<exRegistration>` for more details.
- The :ref:`mjtWarning` enum value ``mjWARN_VGEOMFULL`` is removed. Exhaustion of visual geoms is now handled
internally by the :ref:`mjvScene`.
+5 -6
View File
@@ -230,12 +230,11 @@ troubleshoot issues with a model) can be statically linked into the application.
:ref:`mjpPlugin` struct in the ``main`` function, then passing it to :ref:`mjp_registerPlugin` to be registered with
MuJoCo.
Generally, reusable plugins are expected to be packaged as dynamic libraries. A dynamic library containing one or more
MuJoCo plugins should make sure that all plugins are registered when the library is loaded. In GCC-compatible compilers,
this can be achieved by calling :ref:`mjp_registerPlugin` in a function that is declared with
``__attribute__((constructor))``, while in MSVC this can be done in a DLL entry point (canonically known as
``DllMain``). MuJoCo provides a convenience macro :ref:`mjPLUGIN_LIB_INIT` that expands to either of these
constructs depending on the compiler used.
Generally, reusable plugins are expected to be packaged as libraries and should be registered when the library is
loaded. In GCC-compatible compilers, this can be achieved by calling :ref:`mjp_registerPlugin` in a function that is
declared with ``__attribute__((constructor))``, while in MSVC this can be done by injecting code into the C runtime
initialization. MuJoCo provides a convenience macro :ref:`mjPLUGIN_LIB_INIT` that expands to either of these constructs
depending on the compiler used.
Users of plugins that are delivered as dynamic libraries as described above can load the library using the function
:ref:`mj_loadPluginLibrary`. This is the preferred way to load dynamic libraries containing MuJoCo plugins (rather than,
+34 -27
View File
@@ -182,39 +182,46 @@ struct mjSDF_ {
};
typedef struct mjSDF_ mjSDF;
//------------------------------------ Initialization ----------------------------------------------
#if defined(__has_attribute)
#if __has_attribute(constructor)
#define mjPLUGIN_LIB_INIT __attribute__((constructor)) static void _mjplugin_init(void)
#endif // __has_attribute(constructor)
#elif defined(_MSC_VER)
#ifndef mjDLLMAIN
#define mjDLLMAIN DllMain
#define mjPLUGIN_LIB_INIT(n) \
static void _mj_init_##n(void) __attribute__((constructor)); \
static void _mj_init_##n(void)
#endif
#if !defined(mjEXTERNC)
#if defined(__cplusplus)
#define mjEXTERNC extern "C"
#elif defined(_MSC_VER)
// on x86, symbols are decorated with a leading underscore
#ifdef _M_IX86
#define LINKER_NAME "__mj_ptr_"
#else
#define mjEXTERNC
#endif // defined(__cplusplus)
#endif // !defined(mjEXTERNC)
#define LINKER_NAME "_mj_ptr_"
#endif
// NOLINTBEGIN(runtime/int)
#define mjPLUGIN_LIB_INIT \
static void _mjplugin_dllmain(void); \
mjEXTERNC int __stdcall mjDLLMAIN(void* hinst, unsigned long reason, void* reserved) { \
if (reason == 1) { \
_mjplugin_dllmain(); \
} \
return 1; \
} \
static void _mjplugin_dllmain(void)
// NOLINTEND(runtime/int)
#pragma section(".CRT$XCU", read)
#endif // defined(_MSC_VER)
#if !defined(mjEXTERNC)
#if defined(__cplusplus)
#define mjEXTERNC extern "C"
#else
#define mjEXTERNC
#endif // defined(__cplusplus)
#endif // !defined(mjEXTERNC)
#define mjPLUGIN_LIB_INIT(n) \
static void __cdecl _mj_init_##n(void); \
/* use mjEXTERNC to prevent C++ name mangling */ \
/* allocate the function pointer to the .CRT$XCU section of the executable */ \
/* functions in this section are executed on startup before calling main() */ \
mjEXTERNC __declspec(allocate(".CRT$XCU")) \
void (__cdecl * _mj_ptr_##n)(void) = _mj_init_##n; \
/* Force the linker to include the pointer symbol */ \
__pragma(comment(linker, "/include:" LINKER_NAME #n)) \
static void __cdecl _mj_init_##n(void)
#else
#error "Unknown compiler: Plugin registration not supported."
#endif
// function pointer type for mj_loadAllPluginLibraries callback
typedef void (*mjfPluginLibraryLoadCallback)(const char* filename, int first, int count);
+1 -1
View File
@@ -17,6 +17,6 @@
namespace mujoco::plugin::actuator {
mjPLUGIN_LIB_INIT { Pid::RegisterPlugin(); }
mjPLUGIN_LIB_INIT(actuator) { Pid::RegisterPlugin(); }
} // namespace mujoco::plugin::actuator
+1 -1
View File
@@ -17,7 +17,7 @@
namespace mujoco::plugin::elasticity {
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(elasticity) {
Cable::RegisterPlugin();
}
+1 -1
View File
@@ -114,7 +114,7 @@ int CanDecode(const mjResource* resource) {
} // namespace
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(obj_decoder) {
mjpDecoder decoder;
mjp_defaultDecoder(&decoder);
decoder.content_type = "model/obj";
+2 -1
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <mujoco/mjplugin.h>
#include "bolt.h"
#include "bowl.h"
#include "gear.h"
@@ -20,7 +21,7 @@
namespace mujoco::plugin::sdf {
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(sdf) {
Bolt::RegisterPlugin();
Bowl::RegisterPlugin();
Gear::RegisterPlugin();
+1 -1
View File
@@ -17,7 +17,7 @@
namespace mujoco::plugin::sensor {
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(sensor) {
TouchGrid::RegisterPlugin();
}
+1 -1
View File
@@ -132,7 +132,7 @@ int CanDecode(const mjResource* resource) {
} // namespace
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(stl_decoder) {
mjpDecoder decoder;
mjp_defaultDecoder(&decoder);
decoder.content_type = "model/stl";
+1 -1
View File
@@ -2459,7 +2459,7 @@ int CanDecode(const mjResource* resource) {
} // namespace
// clang-format off
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(usd_decoder) {
mjpDecoder decoder;
mjp_defaultDecoder(&decoder);
decoder.content_type = "model/usd";
+1 -1
View File
@@ -192,7 +192,7 @@ static mjSpec* ParseZipBuffer(const void* buffer, int nbuffer, const char* name,
return mj_parseXML(root.c_str(), vfs, error, error_sz);
}
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(mjz_decoder) {
mjpDecoder decoder;
decoder.content_type = "application/zip";
decoder.extension = ".mjz|.zip";
+1 -1
View File
@@ -238,7 +238,7 @@ void Renderer::UpdateFps() {
} // namespace mujoco::platform
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(renderer) {
mujoco::platform::GuiPlugin plugin;
plugin.name = "Filament";
plugin.update = [](mujoco::platform::GuiPlugin* self) {
@@ -175,7 +175,7 @@ class ObjectLauncher {
} // namespace mujoco::studio
mjPLUGIN_LIB_INIT {
mjPLUGIN_LIB_INIT(object_launcher) {
using mujoco::studio::ObjectLauncher;
static ObjectLauncher plugin;