Cemu icon indicating copy to clipboard operation
Cemu copied to clipboard

macOS: Apple Silicon support (interpreter only)

Open exverge-0 opened this issue 1 year ago • 2 comments

Fixes #396(?), supersedes #1211 (I don't own BOTW and haven't been able to reproduce the issue on other games, but this fixes a similar error that only occurred on Apple Silicon)

The re-compiler hasn't been implemented and only the interpreter works. (and there's already an WIP aarch64 recompiler for cemu android, assuming that they plan on merging into this repo)

exverge-0 avatar Jul 11 '24 16:07 exverge-0

I'll be honest, it will probably be a while until we have a decent ARM recompiler that will outperform Rosetta so this seems a bit early. But I generally don't want to stand in the way of adding new target platforms. That said, the changes made here are detrimental for other platforms. Just on a quick glance some obvious issues:

  • Putting gx2WriteGatherPipe access behind a mutex is absolutely gonna wreck performance. Better to work with atomic pointers or manually insert memory barriers where needed.
  • mmuRange_HIGHMEM is supposed to match Wii U's memory map. I get that there is a 16KB page size restriction but other platforms don't have this so it should be conditional.
  • Lots of changes in the h264 decoder, would be nice if you could give an explanation?

Exzap avatar Jul 11 '24 17:07 Exzap

Lots of changes in the h264 decoder, would be nice if you could give an explanation?

On OSX, C functions are compiled with a leading underscore and the header files define the ASM functions as a normal C function, resulting in functions being called with a leading underscore but defined without one. ~~Admittedly now I see this could probably be done by just marking them with extern, I'll do that instead~~ This results in the same thing.

Putting gx2WriteGatherPipe access behind a mutex is absolutely gonna wreck performance. Better to work with atomic pointers or manually insert memory barriers where needed.

Makes sense, I'll revert that and try something else

mmuRange_HIGHMEM is supposed to match Wii U's memory map. I get that there is a 16KB page size restriction but other platforms don't have this so it should be conditional.

Ah, I wasn't sure but that's what I somewhat assumed.

exverge-0 avatar Jul 11 '24 17:07 exverge-0

@exverge-0 If you're trying to solve this error:

/Users/runner/work/Cemu/Cemu/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp:172:16: note: 'jumpStart' declared here
                for (auto&& [jumpStart, jumpInfo] : jumps)
                             ^
/Users/runner/work/Cemu/Cemu/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp:178:45: error: reference to local binding 'jumpStart' declared in enclosing function 'AArch64GenContext_t::processAllJumps'
                                        sint64 addressOffset = targetAddress - jumpStart;
                                                                               ^
/Users/runner/work/Cemu/Cemu/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp:172:16: note: 'jumpStart' declared here
                for (auto&& [jumpStart, jumpInfo] : jumps)
                             ^
2 warnings and 6 errors generated

this worked for me: https://github.com/cemu-project/Cemu/blob/d219a192c927d7913ed1f73109e5e55362cfa5c3/.github/workflows/build.yml#L189-L191 build: https://github.com/neebyA/Cemu/actions/runs/15248824548

I found this related StackOverflow post; instead of changing the code, this issue can be fixed by using version 16+ of the Xcode command line tools. The macos-14 runner by default uses Xcode 15.4, so either explicitly selecting a newer version of Xcode or using the macos-15 runner would work.

neebyA avatar May 26 '25 08:05 neebyA

@neebyA Try changing the start of the loop to this as a workaround:

		for (auto&& [_jumpStart, _jumpInfo] : jumps)
		{
			auto& jumpStart = _jumpStart;
			auto& jumpInfo = _jumpInfo;

I am not familiar with macOS development so I am not sure what the best strategy is in regards of which xcode version to target. But in general I am against bumping a compiler version when something can be worked around easily.

Exzap avatar May 26 '25 10:05 Exzap

I used @Exzap 's workaround in order to avoid said issue, and it seems to compile fine.

Otherwise, with the addition of the aarch64 recompiler, this PR is essentially done. I haven't done much testing, from what I've tested it seems to run smoothly with no new issues and the only real difference I've noticed being that the arm64 seemingly has less lag spikes, whereas under Rosetta it would stutter while compiling shaders, but otherwise it's seemingly the same. (though Rosetta already ran at 60/30 fps on the games I tested, so any performance improvement wouldn't be noticeable) I also reverted the change from https://github.com/cemu-project/Cemu/issues/1255#discussion_r1709465498 so as to separate it from this PR to not have to include the odd workaround (which hadn't prevented any games from running/caused any visible issues in my testing, only console errors)

exverge-0 avatar May 26 '25 17:05 exverge-0

  build-macos:
    runs-on: macos-14
    steps:
    - name: "Checkout repo"
      uses: actions/checkout@v4
      with:
        submodules: "recursive"

    - name: "Select Xcode 16.2"
      run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer

    - name: "Verify Xcode Version"
      run: xcodebuild -version
        
    - name: Setup release mode parameters
      run: |
        echo "BUILD_MODE=release" >> $GITHUB_ENV
        echo "BUILD_FLAGS=" >> $GITHUB_ENV
        echo "Build mode is release"

    - name: Setup build flags for version
      if: ${{ inputs.next_version_major != '' }}
      run: |
        echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
        echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
        
    - name: "Install system dependencies"
      run: |
        brew update
        brew install ninja nasm automake libtool

    - name: "Install molten-vk"
      run: |
        curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.3.0/MoltenVK-macos.tar
        tar xf MoltenVK-macos.tar
        sudo mkdir -p /usr/local/lib
        sudo cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib /usr/local/lib

    - name: "Setup cmake"
      uses: jwlawson/actions-setup-cmake@v2
      with:
        cmake-version: '3.29.0'

    - name: "Bootstrap vcpkg"
      run: |
        bash ./dependencies/vcpkg/bootstrap-vcpkg.sh
        
    - name: 'Setup NuGet Credentials for vcpkg'
      shell: 'bash'
      run: |
        mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \
        sources add \
        -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
        -storepasswordincleartext \
        -name "GitHub" \
        -username "${{ github.repository_owner }}" \
        -password "${{ secrets.GITHUB_TOKEN }}"
        mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \
        setapikey "${{ secrets.GITHUB_TOKEN }}" \
        -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
        
    - name: "cmake x64"
      run: |
        mkdir build
        cd build
        cmake .. ${{ env.BUILD_FLAGS }} \
        -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
        -DCMAKE_OSX_ARCHITECTURES=x86_64 \
        -DMACOS_BUNDLE=ON \
        -G Ninja
        
    - name: "Build Cemu x64"
      run: |
        cmake --build build

    - name: "Move x64 artifact"
      run: |
        mkdir bin/x64
        mv bin/Cemu_release.app bin/x64/Cemu.app
        mv bin/x64/Cemu.app/Contents/MacOS/Cemu_release bin/x64/Cemu.app/Contents/MacOS/Cemu
        sed -i '' 's/Cemu_release/Cemu/g' bin/x64/Cemu.app/Contents/Info.plist
        chmod a+x bin/x64/Cemu.app/Contents/MacOS/{Cemu,update.sh}
        codesign --force --deep --sign - bin/x64/Cemu.app

    - name: "cmake arm64"
      run: |
        rm -rf build
        mkdir build
        cd build
        cmake .. ${{ env.BUILD_FLAGS }} \
        -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
        -DCMAKE_OSX_ARCHITECTURES=arm64 \
        -DMACOS_BUNDLE=ON \
        -G Ninja

    - name: "Build Cemu arm64"
      run: |
        cmake --build build

    - name: "Move arm64 artifact"
      run: |
        mkdir bin/arm64
        mkdir bin/universal
        mv bin/Cemu_release.app bin/arm64/Cemu.app
        cp -R bin/arm64/Cemu.app bin/universal/Cemu.app
        mv bin/arm64/Cemu.app/Contents/MacOS/Cemu_release bin/arm64/Cemu.app/Contents/MacOS/Cemu
        sed -i '' 's/Cemu_release/Cemu/g' bin/arm64/Cemu.app/Contents/Info.plist
        chmod a+x bin/arm64/Cemu.app/Contents/MacOS/{Cemu,update.sh}
        codesign --force --deep --sign - bin/arm64/Cemu.app

    - name: "Create Universal binary"
      run: |
        rm bin/universal/Cemu.app/Contents/MacOS/Cemu_release
        lipo -create -output bin/universal/Cemu.app/Contents/MacOS/Cemu \
        bin/x64/Cemu.app/Contents/MacOS/Cemu \
        bin/arm64/Cemu.app/Contents/MacOS/Cemu
        rm bin/universal/Cemu.app/Contents/Frameworks/libusb-1.0.0.dylib
        lipo -create -output bin/universal/Cemu.app/Contents/Frameworks/libusb-1.0.0.dylib \
        bin/x64/Cemu.app/Contents/Frameworks/libusb-1.0.0.dylib \
        bin/arm64/Cemu.app/Contents/Frameworks/libusb-1.0.0.dylib
        sed -i '' 's/Cemu_release/Cemu/g' bin/universal/Cemu.app/Contents/Info.plist
        chmod a+x bin/universal/Cemu.app/Contents/MacOS/{Cemu,update.sh}
        codesign --force --deep --sign - bin/universal/Cemu.app
        rm -rf bin/x64/Cemu.app bin/arm64/Cemu.app

    - name: Prepare artifacts
      run: |
        mkdir bin/Cemu_app_universal
        mv bin/universal/Cemu.app bin/Cemu_app_universal/Cemu.app
        ln -s /Applications bin/Cemu_app_universal/Applications
        hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app_universal"
        hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu_universal.dmg
        rm bin/tmp.dmg
              
    - name: Upload universal artifact
      uses: actions/upload-artifact@v4
      with:
        name: cemu-bin-macos-universal
        path: ./bin/Cemu_universal.dmg
      - uses: actions/download-artifact@v4
        with:
          name: cemu-bin-macos-universal
          path: cemu-bin-macos-universal

      - name: Create release from macos-bin-universal
        run: cp cemu-bin-macos-universal/Cemu_universal.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-universal.dmg

I suggest building a universal version would be better, to avoid distinguishing between x86_64 and arm64 versions. After all, the universal version can run directly on both x86_64 and arm64 platforms. Moreover, on arm64 platforms, the universal version can switch to running the x86_64 version through the Rosetta 2 option.

hauntek avatar May 28 '25 11:05 hauntek

@hauntek Feel free to open a separate PR for building the universal binary

Exzap avatar Jun 18 '25 08:06 Exzap

Individual arch builds have the benefit of being smaller and launching faster. They should be keep, even if universal is regarded as the main distribution

RedBlackAka avatar Jun 18 '25 16:06 RedBlackAka