Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide support for ARM64/M1 devices (Windows and macOS) #601

Closed
brunoborges opened this issue Nov 18, 2020 · 25 comments
Closed

Provide support for ARM64/M1 devices (Windows and macOS) #601

brunoborges opened this issue Nov 18, 2020 · 25 comments

Comments

@brunoborges
Copy link

Hi all,

I work on the Microsoft Java Engineering Group, and I'd like to openly discuss how we can help port LWJGL to Windows on ARM64 devices (e.g. Surface X) as well as macOS on Apple Silicon M1.

Best,
Bruno Borges

@Spasi
Copy link
Member

Spasi commented Nov 21, 2020

Hey Bruno,

Porting LWJGL means getting the CI pipelines to build its native dependencies for the new platform/architecture. LWJGL has a simple core that's easy to build, but the different bindings depend on native libraries with varying implementation quality and build complexity. Some will be trivial to build, others may require simple fixes we can apply on our own, sometimes we may have to wait for the upstream maintainers to do the work. A few might even be impossible to build. In any case, we don't need every dependency to release LWJGL and our build customizer can handle platforms/architectures with missing bindings.

I'm guessing that Microsoft is mostly interested in getting Minecraft to run on ARM64. We could focus on bindings that Minecraft uses first, deal with the rest later. Those most likely include: GLFW, OpenGL, OpenAL and stb. Could you please confirm and check if they use anything else?

We have a separate account for CI (LWJGL-CI). Each repository has a single "LWJGL CI" commit on top of the upstream commits. This commit includes the build scripts and any fixes that need to be applied. When the binding is updated, the CI commit is rebased on top of the new upstream commit. This keeps the history clean and enables interested users to easily identify in which state each binding has been built (it's always the HEAD~1 commit). There are two interesting files in each CI commit: .travis.yml and appveyor.yml (note: we completely overwrite those files if they exist in the upstream project). We use Travis CI for Linux and macOS builds, AppVeyor for Windows builds. Each build file contains a matrix of builds for the various architectures. Artifacts are uploaded to LWJGL's S3 build bucket. Before opening a PR and while testing a new build, you probably want to comment out the awscli installation and aws s3 cp commands, to avoid build errors. Ideally, replace them with your own upload procedure to test the artifacts.

As a test, I got GLFW to build on Windows ARM64 and macOS ARM64. It was straightforward:

One problem is that I don't have any hardware to test on, but I'm getting:

dumpbin /HEADERS glfw.dll | findstr machine

AA64 machine (ARM64)

for https://build.lwjgl.org/nightly/windows/arm64/glfw.dll and

file libglfw.dylib

libglfw.dylib: Mach-O 64-bit dynamically linked shared library arm64

for https://build.lwjgl.org/nightly/macosx/arm64/libglfw.dylib.

@Spasi
Copy link
Member

Spasi commented Nov 22, 2020

I've hit a showstopper. Looks like dyncall does not support Windows ARM64 yet. LWJGL uses dyncall to implement upcalls/callbacks from C to Java. It's the only dependency that is statically linked into the core LWJGL shared library.

The only workaround I can think of is going back to libffi, but I'm not looking forward to it at all (it's a pita to build on Windows).

@brunoborges
Copy link
Author

brunoborges commented Nov 22, 2020

Let me bring this to the attention of the engineering team and see if they can help on this dyncall port.

Thank you so much for the excellent and detailed summary of what needs to be done!

// @lewurm, @luhenry, @mo-beck, and @karianna

@lewurm
Copy link

lewurm commented Nov 23, 2020

Thank you @Spasi, that is really helpful.

I'll look into the dyncall port to Windows+Arm64 probably next week, and report back.

@fiddlerwoaroof
Copy link

I can test macOS ARM64 with Minecraft, if there's a build available

@r58Playz
Copy link

r58Playz commented Dec 18, 2020

It seems like someone got LWJGL to build according to this.

Edit: I built dyncall and it seems that it worked!
(Edited to remove sensitive info)

$ ./../configure   
Configuration written to Makefile.config
$ make
cd dyncall && /Library/Developer/CommandLineTools/usr/bin/make all
cc    -c -o dyncall_vector.o /dyncall-1.1/./dyncall/dyncall_vector.c
cc    -c -o dyncall_api.o /dyncall-1.1/./dyncall/dyncall_api.c
cc    -c -o dyncall_callvm.o  dyncall-1.1/./dyncall/dyncall_callvm.c
cc    -c -o dyncall_callvm_base.o  dyncall-1.1/./dyncall/dyncall_callvm_base.c
cc    -c -o dyncall_call.o  dyncall-1.1/./dyncall/dyncall_call.S
cc    -c -o dyncall_callf.o  dyncall-1.1/./dyncall/dyncall_callf.c
cc    -c -o dyncall_struct.o  dyncall-1.1/./dyncall/dyncall_struct.c
libtool -static -o libdyncall_s.a dyncall_vector.o dyncall_api.o dyncall_callvm.o dyncall_callvm_base.o dyncall_call.o dyncall_callf.o dyncall_struct.o
cd dyncallback && /Library/Developer/CommandLineTools/usr/bin/make all
cc  -I dyncall-1.1/./dyncallback/../dyncall    -c -o dyncall_alloc_wx.o  dyncall-1.1/./dyncallback/dyncall_alloc_wx.c
cc  -I dyncall-1.1/./dyncallback/../dyncall    -c -o dyncall_args.o  dyncall-1.1/./dyncallback/dyncall_args.c
cc  -I dyncall-1.1/./dyncallback/../dyncall    -c -o dyncall_callback.o  dyncall-1.1/./dyncallback/dyncall_callback.c
cc    -c -o dyncall_callback_arch.o  dyncall-1.1/./dyncallback/dyncall_callback_arch.S
cc  -I dyncall-1.1/./dyncallback/../dyncall    -c -o dyncall_thunk.o  dyncall-1.1/./dyncallback/dyncall_thunk.c
libtool -static -o libdyncallback_s.a dyncall_alloc_wx.o dyncall_args.o dyncall_callback.o dyncall_callback_arch.o dyncall_thunk.o
cd dynload && /Library/Developer/CommandLineTools/usr/bin/make all
cc    -c -o dynload.o  dyncall-1.1/./dynload/dynload.c
cc    -c -o dynload_syms.o  dyncall-1.1/./dynload/dynload_syms.c
libtool -static -o libdynload_s.a dynload.o dynload_syms.o
$ echo $?
0

@lewurm
Copy link

lewurm commented Dec 22, 2020

Sorry for the late update.

dyncall on macOS+AArch64

I can confirm that dyncall works as-is on macOS+AArch64 (aka. Apple Silicon). There are a few test bugs, I included fixes for them in the patchset below.

dyncall on Windows+AArch64

They following things had to be done:

  1. workaround lack of ARMASM64 support in CMake
  2. port existing AArch64 assembly snippets to ARMASM64 syntax
  3. support var args on Windows+AArch64

The patches are here (generated via hg export): https://gist.githubusercontent.com/lewurm/63a2c93a13d20c3ca53cfe7049dbfd48/raw/e03e859b57fae929ce6ffed5684aa7424a78296f/aarch64-fun.export.patch

@Spasi I'm not sure how you go about https://github.com/LWJGL-CI/dyncall, but I assume you manually import it? I sent the patches to the dyncall maintainers, let's see if they merge it upstream at https://dyncall.org/pub/dyncall/dyncall/ . Also the CI config for Windows+AArch64 needs a slight update: Due to the CMake workaround, it's required to call vcvarsall.bat amd64_arm64 before the first cmake call. I didn't know where to PR such change.

(baby steps) Minecraft on Windows+AArch64

Based on the Apple Silicon instructions that have been posted here ( https://gist.github.com/tanmayb123/d55b16c493326945385e815453de411a ), I tried to port it to Windows+AArch64: https://gist.github.com/lewurm/1b028bf3ebe46b46e406abc00bb580b1 .
Changes:

  • it's supposed to be executed in WSL
  • swap : to ; in class path string
  • convert each path from WSL to Windows via wslpath -w
  • download/copy native .dlls for Windows+AArch64 into ./lwjglnatives/

Alas the glfw.dll posted by @Spasi doesn't cut it, it fails to initialize some OpenGL context (crash log in the gist). I tried to look into it, but that's not the abstraction layer I usually operate in; I was quite lost to say the least 🙂 Maybe someone is willing to look into it further, now that dyncall is unblocked.

@r58Playz
Copy link

Looks like you need to install the OpenGL compatibility pack.

@dustContributor
Copy link

dustContributor commented Dec 26, 2020

Ah cool, OpenGL is literally not supported on Windows 10 for arm64, unless it's exposed as a GL->D3D12 layer, with all the caveats it implies. Well at least it should be enough for Minecraft, but nothing else for now (ie, no GL4, nor GL 3 with commonly supported GL 4 extensions, possibly no performance improvement mods either since some of them use more advanced GL features).

Is OpenGL even supported on the M1 OS version Apple ships? Or do you need some form of GL->Metal translation layer there too?

@r58Playz
Copy link

r58Playz commented Dec 27, 2020

GL is supported but the version is ancient(GL 1.2) so no shaders a max of 3.3. I ran a test GLFW program that uses 3.3 and it works.

EDIT: Optifine works on Apple Silicon.

@SaadAbbassi4
Copy link

GL is supported but the version is ancient(GL 1.2) so no shaders.

EDIT: Optifine works on Apple Silicon.

Could you please tell me how did you make it work?

@r58Playz
Copy link

Use this to get MultiMC(the x86_64 version) to run vanilla 1.16.
After that, install Forge on a 1.16 instance. Add Optifine HD_U_G5 to the instance. I added -Dfml.earlyprogresswindow=false as a JVM argument, though it might not work for you.
optionsof.txt
I attached my Optifine options file.

@r58Playz
Copy link

r58Playz commented Jan 6, 2021

By the way, can LWJGL2 get ported to arm64?

@lewurm
Copy link

lewurm commented Jan 12, 2021

Looks like you need to install the OpenGL compatibility pack.

Thank you for that @r58Playz! That indeed brings me a bit further with a black window popping up, but the application is shutting down after loading lwjgl_stb.dll (not crashing, or least it doesn't look like it): https://gist.github.com/lewurm/c05450a77e6eacab0dc0f70896d1e21b

I run with -Dorg.lwgl.util.Debug=true -Dorg.lwjgl.util.DebugLoader=true. Are there other flags that would help me to track down what is going on?

@r58Playz
Copy link

r58Playz commented Jan 14, 2021

jemalloc.dll and opengl32.dll are not found. Not all the natives for LWJGL are added to the library folder.

@lewurm
Copy link

lewurm commented Jan 14, 2021

Thank you for your input @r58Playz!

jemalloc.dll and opengl32.dll are not found. Not all the natives for LWJGL are added to the library folder.

Is that really the problem?

[LWJGL] [14:15:52] [Render thread/INFO]: [STDERR]: Warning: Failed to instantiate memory allocator: org.lwjgl.system.jemalloc.JEmallocAllocator. Using the system default.
[LWJGL] [14:15:52] [Render thread/INFO]: [STDERR]: MemoryUtil allocator: StdlibAllocator

static MemoryAllocator getInstance() {
Object allocator = Configuration.MEMORY_ALLOCATOR.get();
if (allocator instanceof MemoryAllocator) {
return (MemoryAllocator)allocator;
}
if (!"system".equals(allocator)) {
String className;
if (allocator == null || "jemalloc".equals(allocator)) {
className = "org.lwjgl.system.jemalloc.JEmallocAllocator";
} else if ("rpmalloc".equals(allocator)) {
className = "org.lwjgl.system.rpmalloc.RPmallocAllocator";
} else {
className = allocator.toString();
}
try {
Class<?> allocatorClass = Class.forName(className);
return (MemoryAllocator)allocatorClass.getConstructor().newInstance();
} catch (Throwable t) {
if (Checks.DEBUG && allocator != null) {
t.printStackTrace(DEBUG_STREAM);
}
apiLog(String.format("Warning: Failed to instantiate memory allocator: %s. Using the system default.", className));
}
}
return new StdlibAllocator();
}

That looks okay to me. Am I missing something?

And regarding opengl:

[14:15:52] [Render thread/INFO]: [STDERR]: Loaded from system paths: C:\WINDOWS\SYSTEM32\opengl32.dll

Sounds okay to me as well.

@Spasi
Copy link
Member

Spasi commented Feb 25, 2021

Hello, an update:

The dyncall bindings in LWJGL have now been replaced with libffi. Apologies to @lewurm and anyone else that spent time on getting dyncall to work on ARM64, your effort is much appreciated. However, I believe this was a necessary step towards supporting more platforms/architectures (libffi supports virtually everything we could ever need). This migration has also resolved a range of long-standing issues in LWJGL (e.g. dyncall couldn't handle structs returned by-value) and the new support for upcalls is both simpler and more efficient.

For users that would like to help with the migration process or simply want to experiment with LWJGL, the nightly build (https://www.lwjgl.org/browse/nightly) now includes an up-to-date libffi static library for all current+future supported platform/architectures:

  • Linux: arm32, arm64, mips64, x64
  • macOS: arm64, x64
  • Windows: arm64, x64, x86

Next step is updating the CI infrastructure. Unfortunately, there was a major setback recently, with the changes to Travis CI's pricing model. The new plan is to migrate everything to Github Actions. I've made some testing with libffi (workflow here) and I'm happy with what I'm seeing so far. It will be great having everything in one place (vs some on Travis CI, some on AppVeyor).

A couple of issues atm:

  • The macOS/ARM64 build on Github Actions worked fine when I first set it up, but is no longer available. The macos-11.0 runner is in private preview atm and I'm not sure when it's going to be available again.
  • I can't get libffi to build on Windows (on any architecture). Basically, I've been trying to port this AppVeyor script, which builds just fine (output here), to Github Actions. Any tips on what I may be doing wrong will be appreciated!

@Spasi
Copy link
Member

Spasi commented Feb 27, 2021

The migration is moving along nicely. I found solutions to the above two issues and the following projects are now building with Github Actions:

@Spasi Spasi closed this as completed in bd9557d Mar 6, 2021
@Spasi
Copy link
Member

Spasi commented Mar 6, 2021

We're all green! 🎉

Users with access to ARM hardware should now be able to build and test LWJGL 3 locally on macOS and Windows. Please open new issues (or contact me on Slack/Discord) if you face any trouble.

The first 3.3.0 snapshot will also be available very soon (hopefully tomorrow).

@Spasi
Copy link
Member

Spasi commented Mar 7, 2021

LWJGL 3.3.0 snapshot 7 is now available.

The build customizer has been updated to include the two new architectures. You can now use it to download a bundle with the natives or get them via Maven/Gradle (the new classifiers are natives-macos-arm64 and natives-windows-arm64).

@Helic0pter
Copy link

Thanks for your work. Currently I check LWJGL 3.3.0 snapshot 7 with the jmonkeyengine. I have problems with the initialization of OpenAL. In jme3, this works like this:
public void createALC() {
device = ALC10.alcOpenDevice((ByteBuffer) null);
ALCCapabilities deviceCaps = ALC.createCapabilities(device);
context = ALC10.alcCreateContext(device, (IntBuffer) null);
ALC10.alcMakeContextCurrent(context);
AL.createCapabilities(deviceCaps);
}

I get then:
java.lang.NullPointerException
at org.lwjgl.system.Checks.check(Checks.java:188)

I guess, you see immediately, how this has to be changed? Thanks!

@Helic0pter
Copy link

I just saw, that the OpenAL.dll in 3.2.3 is about 1MB, here it is only 300 KB. Could there be a problem i the build process? (Windows 64 bit and also Windows 32 bit)

@Helic0pter
Copy link

Ok, on Windows it is so that the new lwjgl-openal.jar works with the OLD OpenAL.dll on Windows 10. OLD means from 3.2.3 stable. Fr me it looks as if in the generation of the OpenAL binaries, there is something wrong ...

@Spasi
Copy link
Member

Spasi commented Mar 16, 2021

LWJGL 3.3.0 snapshot 8 is available and the OpenAL-Soft natives will now work on Windows. Thanks for reporting this @Helic0pter!

@adiantek
Copy link

adiantek commented Mar 26, 2021

I ran Minecraft on Surface Pro X with native architecture (arm64). Details: MultiMC/Launcher#3025 (comment)
I wrote here bcuz it's the first result when I type minecraft windows aarch64 in Google

Edit: easier version: https://github.com/adiantek/mc-spx

Sorenon added a commit to Sorenon/lwjgl3 that referenced this issue Jul 8, 2021
* fix(GL): gl(En/Dis)ableClientState function lookup. Close LWJGL#602

Fixes lookup of deprecated functions that have been undeprecated by an
OpenGL extension.

* feat(libffi): restore libffi bindings

* feat(build): minor improvements and fixes

* feat(core): replace dyncall with libffi. Close LWJGL#283

* feat(core): remove dyncall bindings

The only feature maintained is the dlGetLibraryPath function from
dynload.h (very useful when org.lwjgl.system.DebugLoader is enabled).

* build: bump version to 3.3.0

The replacement of dyncall with libffi is a major breaking change, so
3.2.4 is canceled and the next release will be 3.3.0.

* feat(assimp): update to 5.0.1

* feat(bgfx): update to API version 112

* build: update dependencies

* feat(generator): add link to invoke in callback class javadoc

This enables better javadoc navigation inside IDEs.

* feat(assimp): javadoc improvements

* feat(demo): use custom aiFileIO to import models

Creating a correct/complete custom aiFileIO solution is a bit tricky, so
this implementation may be a good reference for LWJGL users.

* feat(shaderc): update to 2020.5

* feat(spvc): update to 0.45.0

* fix(build): use https DTD URLs

* feat(rpmalloc): update to 1.4.2

This is a pre-release build, needed for macos/arm64 compatibility.

* build: add support for windows-arm64 & macos-arm64. Close LWJGL#601

* fix(rpmalloc): spin intrinsic on windows-arm64

* build: simplify cache-kotlinc

Remove the annoying AWS credential check and allow skipping Kotlin code
recompilation.

* build: move kotlinc cache to the ci folder

* build: javadoc fixes

* build: increase javadoc Xmx

* build: release macOS/Windows ARM64 natives

* build: skip bgfx natives for Windows ARM64

* build: fix build.gradle.kts error

* fix(core): bump version to 3.3.0

* fix(demo): font error check in nanovg demo

* chore: remove deprecated methods

* docs: new build status badge, add discord server

* build: add .gitattributes file

* feat(generator): automate virtual bitfield member generation

* feat(Vulkan): update to 1.2.172

* feat(Vulkan): update to 1.2.172 (generated)

* fix(generator): move library init before constants. Close LWJGL#630

A constant may be initialized with an expression that ends up calling a
native method. If the library has not been initialized first, it will
trigger an UnsatisfiedLinkError.

* fix(core): use Unsafe cookies for field reads. Close LWJGL#632

* fix(Vulkan): javadoc issues

* fix(Vulkan): make VK12 extend VK11

* fix(Vulkan): add 1.2 to the known versions array

* fix(Vulkan): values of aliased tokens

* build(vma): update Vulkan headers to 1.2.172

* feat(vma): update to 3.3.0-development

* build(vma): fix compilation on clang

* fix(vma): javadoc issues

* feat(lmdb): update to 0.9.28

* chore(Vulkan): remove redundant statements

* fix(Vulkan): default instance/device API versions

The default instance & device API versions, when not specified by the
user, now match what the Vulkan specification says they should be.

- The default instance API version, when VkApplicationInfo is not
specified or when its apiVersion value is 0, is now VK_API_VERSION_1_0
instead of the maximum version supported by the instance.
- The default device API version, when not specified in the VkDevice
constructor, is now the minimum of the instance API version and the
API version supported by the physical device.
- The API version stored in VKCapabilitiesDevice is now the version
specified/derived in the VkDevice constructor, instead of always equal
to the instance API version.

Note that the highest API version the application targets, set with
VkApplicationInfo and stored in VKCapabilitiesInstance, can be higher
than the actual maximum version supported by the instance, as described
in the Vulkan specification.

* chore(Vulkan): remove experimental extension

* docs: caps buffer factories must zero prefill

* fix(Vulkan): VkAccelerationStructureInstance bitfield order

* fix(stb): remove overloads with allocation context

stb is already configured to use the LWJGL allocator internally. Passing
a custom allocation context is unnecessary.

* chore(Vulkan): simplify bitwise operations

* feat(tinyfd): update to 3.8.7. Close LWJGL#623

* fix(tinyfd): javadoc issue

* fix(GL): ListDrawCommandsStatesClientNV parameter type

* feat(generator): move struct member javadoc to getters

* feat(generator): struct member javadoc (generated)

* feat(nanovg): update to latest version

* feat(tinyexr): update to 1.0.0

* perf(xxhash): enable XXH3 dispatching

* feat(zstd): update to 1.4.9

* build(xxhash): disabled AVX-512

Until LWJGL moves to a modern GCC.

* build(xxhash): use GCC 4.9+ for AVX-512

* build(demo): update google fonts branch

* fix(bullet): remove misplaced greek characters in JavaDoc

* feat(lz4): update to 1.9.3

* fix(core): closure allocation tracking

Also now logging which closure registry implementation is being used. On
Linux, libffi switched to static trampolines and the executable address
is now different from the closure address, so the ConcurrentHashMap
implementation is necessary.

* Start with the example for Vulkan with OpenXR

* Create the session loop for the OpenXR-Vulkan example

* My work-arounds to get my dev env for lwjgl working. DONT commit this in the actual lwjgl repository!

* Create the swapchains

* Prepare for rendering on the swapchains

* Use an empty command buffer to prepare the actual rendering. This gets rid of the warnings that were shown due to some monado command buffer being used simultaneously

* Remove some debug printlns

* Process the improvements done by Sorenon

* Start with the render pass and graphics pipeline

* Continue with the graphics pipeline

* Continue with the graphics pipeline

* Nearly finish the initial graphics pipeline(s)

* Fix the shaders and compile them

* Finish initial graphics pipeline creation

* Start with the render pass commands and some preparation I forgot

* Nearly finish framebuffers

* Clear color works now

* Start with the vertex buffer and index buffer

* DONT MAKE THIS STUPID MISTAKE EVER AGAIN

* Finish the initial vertex and index buffer

* Add a hardcoded camera matrix

* Make it possible to reuse some code in the OpenXR-Vulkan example

* Use the right camera matrix rather than the hardcoded one

* Start with the small cube

* Fix the small model

* Start with tracking the hand position

* Add hand pose tracking, but it is not really used yet.

* Continue with hands tracking

* Add push constant to invert colors (but still needs to be bound to inputs)

* Split some utilities into a separate XRHelper class

* Fix a stupid string terminator problem that messed things up on a Windows machine

* Fix the handpose tracking math

* Invert the Y-axis of the projection matrix (needed due to the Vulkan coordinate system)

* Use a better projection fix

* Improve the checking and handling of presence/absence of Vulkan extensions

* Improve checking of Vulkan and OpenXR results

* Improve selection of swapchain depth format

* Performance improvements (not needed for 5k cubes, but examples are supposed to show the right way of doing things)

* Invert the hand cube colors if the corresponding trigger is pressed. Unfortunately, I cant test this, so lets hope it works

* Work on XRHelper

* Use XRHelper also in the OpenXR with OpenGL example

* Rename HelloOpenXR to HelloOpenXRGL

* Rename Shaders to ShadersGL

* Restore ShadersGL

* Remove unneeded line

* Document XRHelper.createGraphicsBindingOpenGL

* Disable debugging safety check by default

* Update OpenXR generated bindings

Co-authored-by: Ioannis Tsakpinis <iotsakp@gmail.com>
Co-authored-by: adam-risberg <35687607+adam-risberg@users.noreply.github.com>
Co-authored-by: Leon Linhart <themrmilchmann@gmail.com>
Co-authored-by: Sorenon <sorenonandstuffs@gmail.com>
kalkafox pushed a commit to kalkafox/lwjgl3 that referenced this issue Sep 14, 2021
theofficialgman pushed a commit to theofficialgman/lwjgl3 that referenced this issue Feb 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants