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:
committed by
Copybara-Service
parent
33fd2fe40e
commit
f2461f9ce6
@@ -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:
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
|
||||
namespace mujoco::plugin::actuator {
|
||||
|
||||
mjPLUGIN_LIB_INIT { Pid::RegisterPlugin(); }
|
||||
mjPLUGIN_LIB_INIT(actuator) { Pid::RegisterPlugin(); }
|
||||
|
||||
} // namespace mujoco::plugin::actuator
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace mujoco::plugin::elasticity {
|
||||
|
||||
mjPLUGIN_LIB_INIT {
|
||||
mjPLUGIN_LIB_INIT(elasticity) {
|
||||
Cable::RegisterPlugin();
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace mujoco::plugin::sensor {
|
||||
|
||||
mjPLUGIN_LIB_INIT {
|
||||
mjPLUGIN_LIB_INIT(sensor) {
|
||||
TouchGrid::RegisterPlugin();
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user