Raise an informative error when running on macOS under Rosetta 2.
Current users only get a cryptic "Illegal instruction" crash. Context: https://github.com/deepmind/mujoco/issues/611 PiperOrigin-RevId: 491705647 Change-Id: I1285897a36e858300ee3d125957e90754be9a7f7
This commit is contained in:
committed by
Copybara-Service
parent
5f7d9f4f02
commit
df25d7d602
@@ -70,6 +70,10 @@ General
|
||||
- Sensors of type :ref:`user<sensor-user>` no longer require :at:`objtype` and :at:`objname`. If unspecified, the
|
||||
objtype will be :ref:`mjOBJ_UNKNOWN<mjtObj>`. ``user`` sensors :at:`datatype` default is now :at-val:`"real"`.
|
||||
- Add support for capsules in URDF import.
|
||||
- On macOS, issue an informative error message when run under `Rosetta 2 <https://support.apple.com/en-gb/HT211861>`_
|
||||
translation on an Apple Silicon machine. Pre-built MuJoCo binaries make use of
|
||||
`AVX <https://en.wikipedia.org/wiki/Advanced_Vector_Extensions>`_ instructions on x86-64 machines, which is not
|
||||
supported by Rosetta 2. (Before this version, users only get a cryptic "Illegal instruction" message.)
|
||||
|
||||
Simulate
|
||||
^^^^^^^^
|
||||
|
||||
@@ -23,6 +23,18 @@ import subprocess
|
||||
_SYSTEM = platform.system()
|
||||
if _SYSTEM == 'Windows':
|
||||
ctypes.WinDLL(os.path.join(os.path.dirname(__file__), 'mujoco.dll'))
|
||||
elif _SYSTEM == 'Darwin':
|
||||
proc_translated = subprocess.run(
|
||||
['sysctl', '-n', 'sysctl.proc_translated'], capture_output=True).stdout
|
||||
try:
|
||||
is_rosetta = bool(int(proc_translated))
|
||||
except ValueError:
|
||||
is_rosetta = False
|
||||
if is_rosetta and platform.machine() == 'x86_64':
|
||||
raise ImportError(
|
||||
'You are running an x86_64 build of Python on an Apple Silicon '
|
||||
'machine. This is not supported by MuJoCo. Please install and run a '
|
||||
'native, arm64 build of Python.')
|
||||
|
||||
from mujoco._callbacks import *
|
||||
from mujoco._constants import *
|
||||
|
||||
@@ -122,7 +122,7 @@ target_include_directories(libsimulate PUBLIC $<TARGET_PROPERTY:glfw,INTERFACE_I
|
||||
target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
|
||||
|
||||
if(APPLE)
|
||||
target_sources(libsimulate PRIVATE macos_save.mm)
|
||||
target_sources(libsimulate PRIVATE macos_gui.mm)
|
||||
target_link_libraries(libsimulate PUBLIC "-framework Cocoa")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ CXXFLAGS=$(CFLAGS) -std=c++17 -stdlib=libc++
|
||||
ALLFLAGS=$(CXXFLAGS) -L$(GLFWROOT)/lib -Wl,-rpath,$(MUJOCOPATH)
|
||||
|
||||
all:
|
||||
clang++ $(CXXFLAGS) -c macos_save.mm
|
||||
clang++ $(CXXFLAGS) -c macos_gui.mm
|
||||
clang++ $(CXXFLAGS) -c simulate.cc
|
||||
clang $(CFLAGS) -std=c11 -c uitools.c
|
||||
clang++ $(CXXFLAGS) main.cc macos_save.o simulate.o uitools.o -framework mujoco -framework Cocoa -lglfw -o simulate
|
||||
clang++ $(CXXFLAGS) main.cc macos_gui.o simulate.o uitools.o -framework mujoco -framework Cocoa -lglfw -o simulate
|
||||
rm *.o
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
std::string getSavePath(const char* filename) {
|
||||
std::string GetSavePath(const char* filename) {
|
||||
NSSavePanel* panel = [NSSavePanel savePanel];
|
||||
NSURL* userDocumentsDir = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory
|
||||
inDomains:NSUserDomainMask].firstObject;
|
||||
@@ -31,3 +31,14 @@ std::string getSavePath(const char* filename) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __AVX__
|
||||
void DisplayErrorDialogBox(const char* title, const char* msg) {
|
||||
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
|
||||
[alert setMessageText:[NSString stringWithUTF8String:title]];
|
||||
[alert setInformativeText:[NSString stringWithUTF8String:msg]];
|
||||
[alert setAlertStyle:NSAlertStyleCritical];
|
||||
[alert addButtonWithTitle:@"Exit"];
|
||||
[alert runModal];
|
||||
}
|
||||
#endif
|
||||
@@ -437,8 +437,25 @@ void PhysicsThread(mj::Simulate* sim, const char* filename) {
|
||||
|
||||
//------------------------------------------ main --------------------------------------------------
|
||||
|
||||
// machinery for replacing command line error by a macOS dialog box when running under Rosetta
|
||||
#if defined(__APPLE__) && defined(__AVX__)
|
||||
extern void DisplayErrorDialogBox(const char* title, const char* msg);
|
||||
static const char* rosetta_error_msg = nullptr;
|
||||
__attribute__((used, visibility("default"))) extern "C" void _mj_rosettaError(const char* msg) {
|
||||
rosetta_error_msg = msg;
|
||||
}
|
||||
#endif
|
||||
|
||||
// run event loop
|
||||
int main(int argc, const char** argv) {
|
||||
// display an error if running on macOS under Rosetta 2
|
||||
#if defined(__APPLE__) && defined(__AVX__)
|
||||
if (rosetta_error_msg) {
|
||||
DisplayErrorDialogBox("Rosetta 2 is not supported", rosetta_error_msg);
|
||||
std::exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// print version, check compatibility
|
||||
std::printf("MuJoCo version %s\n", mj_versionString());
|
||||
if (mjVERSION_HEADER!=mj_version()) {
|
||||
|
||||
@@ -40,9 +40,9 @@
|
||||
// Since the dialog box logic needs to be written in Objective-C, we separate it into a different
|
||||
// source file.
|
||||
#ifdef __APPLE__
|
||||
std::string getSavePath(const char* filename);
|
||||
std::string GetSavePath(const char* filename);
|
||||
#else
|
||||
static std::string getSavePath(const char* filename) {
|
||||
static std::string GetSavePath(const char* filename) {
|
||||
return filename;
|
||||
}
|
||||
#endif
|
||||
@@ -1077,7 +1077,7 @@ void uiEvent(mjuiState* state) {
|
||||
switch (it->itemid) {
|
||||
case 0: // Save xml
|
||||
{
|
||||
const std::string path = getSavePath("mjmodel.xml");
|
||||
const std::string path = GetSavePath("mjmodel.xml");
|
||||
if (!path.empty() && !mj_saveLastXML(path.c_str(), m, err, 200)) {
|
||||
std::printf("Save XML error: %s", err);
|
||||
}
|
||||
@@ -1086,7 +1086,7 @@ void uiEvent(mjuiState* state) {
|
||||
|
||||
case 1: // Save mjb
|
||||
{
|
||||
const std::string path = getSavePath("mjmodel.mjb");
|
||||
const std::string path = GetSavePath("mjmodel.mjb");
|
||||
if (!path.empty()) {
|
||||
mj_saveModel(m, path.c_str(), nullptr, 0);
|
||||
}
|
||||
@@ -1850,7 +1850,7 @@ void Simulate::render() {
|
||||
// Unfortunately, if we just yank ".xml"/".mjb" from the filename and append .PNG, the macOS
|
||||
// file dialog does not automatically open that location. Thus, we defer to a default
|
||||
// "screenshot.png" for now.
|
||||
const std::string path = getSavePath("screenshot.png");
|
||||
const std::string path = GetSavePath("screenshot.png");
|
||||
if (!path.empty()) {
|
||||
if (lodepng::encode(path, rgb.get(), w, h, LCT_RGB)) {
|
||||
mju_error("could not save screenshot");
|
||||
|
||||
@@ -27,6 +27,7 @@ set(MUJOCO_ENGINE_SRCS
|
||||
engine_core_constraint.h
|
||||
engine_core_smooth.c
|
||||
engine_core_smooth.h
|
||||
engine_crossplatform.c
|
||||
engine_crossplatform.h
|
||||
engine_derivative.c
|
||||
engine_derivative.h
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2022 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if defined(__APPLE__) && defined(__AVX__)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
__attribute__((weak, visibility("default"))) void _mj_rosettaError(const char* msg) {
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
__asm__ __volatile__ ("ud2"); // raises SIGILL but leave this function at the top of the stack
|
||||
}
|
||||
|
||||
__attribute__((constructor(10000), target("no-avx"))) static void _mj_checkRosetta() {
|
||||
int is_translated = 0;
|
||||
{
|
||||
size_t len = sizeof(is_translated);
|
||||
if (sysctlbyname("sysctl.proc_translated", &is_translated, &len, NULL, 0)) {
|
||||
is_translated = 0;
|
||||
}
|
||||
}
|
||||
if (is_translated) {
|
||||
_mj_rosettaError("MuJoCo cannot be run under Rosetta 2 on an Apple Silicon machine.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(__APPLE__) && defined(__AVX__)
|
||||
Reference in New Issue
Block a user