Compare commits

...

75 commits
v0.9 ... master

Author SHA1 Message Date
wheremyfoodat
fec4428ebf
iOS: Add file picker (#747)
* iOS: Add file picker

* Fix lock placement
2025-03-17 02:55:17 +02:00
wheremyfoodat
90725252d3
iOS: Fail on build error 2025-03-17 00:47:53 +02:00
wheremyfoodat
e71cbc9bc3
iOS: Add frontend & frontend build files (#746)
* iOS: Add SwiftUI part to repo

* Add iOS build script

* Update SDL2 submodule

* Fix iOS build script

* CI: Update xcode tools for iOS

* Update iOS_Build.yml

* Update iOS build

* Lower XCode version

* A

* Update project.pbxproj

* Update iOS_Build.yml

* Update iOS_Build.yml

* Update build.sh
2025-03-16 23:41:28 +02:00
wheremyfoodat
761f9264ba
Merge pull request #745 from wheremyfoodat/ios
iOS driver & Metal renderer improvements
2025-03-16 17:12:51 +02:00
wheremyfoodat
449c14093d iOS driver: Add doc comments 2025-03-16 16:27:23 +02:00
wheremyfoodat
6d0479d7c1 Metal renderer fixes for iOS 2025-03-16 16:22:23 +02:00
wheremyfoodat
3a4f067313
Merge pull request #743 from SamoZ256/ios
Metal: better texture decoder
2025-03-16 13:57:48 +02:00
Ishan09811
da9dd8522a
AppDataDocumentProvider: Allow to remove documents (#744)
* AppDataDocumentProvider: Allow to remove documents

* Typo
2025-03-16 13:53:50 +02:00
wheremyfoodat
e635b9ec8a Metal: Use std::unique_ptr for texture decode 2025-03-16 13:52:32 +02:00
wheremyfoodat
506bf24775
Readme: Add Chonkystation 3 2025-03-15 21:05:24 +02:00
wheremyfoodat
fa123cea3f Undo submodule changes 2025-03-14 12:42:17 +02:00
wheremyfoodat
c061bb7b47 Format 2025-03-14 12:38:58 +02:00
wheremyfoodat
b286537b69
Shadergen types: Add Metal & MSL 2025-03-14 12:00:14 +02:00
Samuliak
67f0388eae
metal: remove unused texture functions 2025-03-11 08:40:21 +01:00
Samuliak
1a460d73be
metal: implement texture swizzling 2025-03-11 08:35:35 +01:00
Samuliak
2111c94f1e
metal: check for format support 2025-03-11 08:24:16 +01:00
Samuliak
3a654b3609
metal: implement texture decoder 2025-03-11 08:23:59 +01:00
wheremyfoodat
c59ee99364 Metal: Reimplement some texture formats on iOS 2025-03-10 02:47:41 +02:00
wheremyfoodat
5990cb3b02 ios: Remove printf spam 2025-03-10 02:22:16 +02:00
wheremyfoodat
1bd00a87f9 FINALLY IOS GRAPHICS 2025-03-10 02:08:19 +02:00
wheremyfoodat
88e986ca53 Fix bridging cast 2025-03-10 00:03:35 +02:00
wheremyfoodat
90279e6f9e ios: Pass CAMetalLayer instead of void* to Obj-C++ bridging header 2025-03-08 22:11:56 +02:00
wheremyfoodat
e378a52b5a ios: Simplify MTKView interface (still doesn't work though) 2025-03-08 20:32:42 +02:00
wheremyfoodat
fb59320829 More iOS work 2025-03-07 17:36:09 +02:00
Ishan09811
3ea05bd200
AppDataDocumentProvider: Add missing `COLUMN_FLAGS` in the default document projectation (#741)
Fixes unable to copy files from device to app's internal storage problem
2025-03-07 11:31:08 +00:00
wheremyfoodat
432eb0d2b3 More iOS work 2025-03-06 23:45:14 +02:00
wheremyfoodat
9bc50a4b9c More iOS progress 2025-03-06 23:42:12 +02:00
wheremyfoodat
1948bea209 More iOS work 2025-03-06 17:04:52 +02:00
Ishan09811
96e1c8fcd3
AppDataDocumentProvider: Typo (#740) 2025-03-06 11:30:23 +02:00
wheremyfoodat
5a6ad5f02f
Merge pull request #738 from yeager/master
Adding Swedish translation
2025-03-05 09:32:44 +02:00
wheremyfoodat
08fbf6be74
Merge pull request #739 from wheremyfoodat/ios
[Core] Improve iOS compilation workflow
2025-03-05 02:58:45 +02:00
wheremyfoodat
7bfcdbf442 [Qt] Hook Swedish to UI 2025-03-05 02:56:53 +02:00
wheremyfoodat
ebefbdc4db [Core] Improve iOS compilation workflow 2025-03-05 02:31:09 +02:00
wheremyfoodat
0a65519867
Fix Metal renderer compilation on iOS 2025-03-05 02:06:10 +02:00
Daniel Nylander
4616b3bc19
Adding Swedish translation 2025-03-04 20:34:49 +01:00
smiRaphi
7af8736f91
OLED theme config fix (#736)
Co-authored-by: smiRaphi <neogt404@gmail.com>
2025-02-26 12:03:38 +02:00
wheremyfoodat
589402b44c
Merge pull request #735 from smiRaphi/master
OLED theme
2025-02-26 10:56:52 +02:00
smiRaphi
b0fdb8e790 OLED theme 2025-02-26 03:50:03 +01:00
wheremyfoodat
0a0ea75013
GPU registers: Fix writes to some registers ignoring the mask (#725)
Co-authored-by: henry <23128103+atem2069@users.noreply.github.com>
2025-02-21 20:22:51 +02:00
wheremyfoodat
2da79414d6
CI: Fix Vulkan SDK action (#723) 2025-02-11 20:48:49 +02:00
wheremyfoodat
da797831ba
Add more HLE service calls for eshop (#721) 2025-02-10 01:00:12 +02:00
Paris Oplopoios
b1a1b4caa7
Very important work (#720)
* Very important work

* Most important fix
2025-02-09 20:47:57 +00:00
wheremyfoodat
7d5cedf476
Add toggle for libretro audio device (#719) 2025-02-09 14:58:17 +02:00
wheremyfoodat
d42974b5db
Mark audio devices as final 2025-02-09 14:45:56 +02:00
wheremyfoodat
a376bb5c9b
Libretro audio device: Fix frame count 2025-02-09 14:31:19 +02:00
Jonian Guveli
042ab6de03
[WIP] Libretro: Add audio support (#714)
* Libretro: Add audio support

* Adding audio interface part 1

* Audio device pt 2

* More audio device

* More audio device

* Morea uudi odevice

* More audio device

* More audio device

* More audio device

---------

Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
2025-02-09 05:04:36 +02:00
wheremyfoodat
4cb66217c2
Try to cross-compile Libretro core for arm64 (#717)
* Try to cross-compile Libretro core for arm64

* Bonk

* Update Hydra_Build.yml
2025-02-08 15:32:04 +02:00
wheremyfoodat
86d1bde845
Temporarily give 80MB to all processes (#715) 2025-02-02 23:18:54 +02:00
wheremyfoodat
54a78902bc Merge branch 'master' of https://github.com/wheremyfoodat/Panda3DS 2025-01-17 02:13:13 +02:00
wheremyfoodat
d85c963c4e Vk: Fix typo 2025-01-17 02:12:34 +02:00
wheremyfoodat
1cae66f163
Vulkan: Fixing CI pt 3 2025-01-17 02:10:18 +02:00
wheremyfoodat
5b409c39ac
Vk: Fixing CI pt 2 2025-01-17 02:05:27 +02:00
wheremyfoodat
154e927264
Vk: Lock CI runners to SDK version 1.3.301 temporarily 2025-01-17 01:59:58 +02:00
wheremyfoodat
c2fd85647a
Vk: Fix typo 2025-01-17 01:12:15 +02:00
wheremyfoodat
d30f2646ec
Use vk::detail::DynamicLoader instead of vk::DynamicLoader (#710)
* Use vk::detail::DynamicLoader instead of vk::DynamicLoader

* Update renderer_vk.cpp
2025-01-06 16:13:40 +02:00
wheremyfoodat
b559725920
Merge pull request #708 from wheremyfoodat/icache
GPU: Add sw texture copies
2025-01-03 21:40:18 +02:00
wheremyfoodat
86ea40a9e5 GPU: Add sw texture copies 2025-01-03 21:24:46 +02:00
wheremyfoodat
40404ba7ba
Merge pull request #707 from wheremyfoodat/icache
Add Luma icache SVCs and don't flush entire code cache when loading/unloading CROs
2025-01-03 19:38:13 +02:00
wheremyfoodat
5042594f3b Add missing SVC logs 2025-01-03 19:23:46 +02:00
wheremyfoodat
84c358660c Implement Luma icache SVCs 2025-01-03 19:21:45 +02:00
wheremyfoodat
33c3e67b31 CRO: Lighter icache flushes 2025-01-03 19:14:59 +02:00
Auxy6858
0c6c455d4d
Removed dead Citra link in readme (#706) 2025-01-02 10:37:08 +02:00
Ishan09811
ca5cc349e9
Android_Build: Implement ccache (#703)
* Android_Build: Implement ccache

* Update Android_Build.yml

* Update Android_Build.yml

---------

Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
2024-12-30 15:35:56 +02:00
wheremyfoodat
81bdfef0be
Merge pull request #701 from jonian/fix-git-versioning
Fix git versioning
2024-12-29 16:07:31 +02:00
Jonian Guveli
29083f0a08 Fix git versioning 2024-12-29 15:36:26 +02:00
wheremyfoodat
59f51f7d0c
GLES: Properly stub out logic ops 2024-12-29 14:28:04 +02:00
wheremyfoodat
bde51b6d27
Fix disabling Wayland & building on some distros (#700) 2024-12-29 13:37:11 +02:00
wheremyfoodat
4ea15c9170
Merge pull request #699 from DaniElectra/wayland
Qt: Fix Wayland support
2024-12-29 00:30:54 +02:00
wheremyfoodat
8604a98edf
No need to call screen->show() twice 2024-12-29 00:30:31 +02:00
Daniel López Guimaraes
5f48028284
Qt: Fix Wayland support
Qt will only create a Wayland surface when show() is called on the main
window and on the ScreenWidget. Thus, call the function before creating
the GL context.

Doesn't cause regressions on XWayland, untested in other platforms.

Fixes #586
2024-12-28 22:19:17 +00:00
wheremyfoodat
3eb89847ad
Merge pull request #698 from wheremyfoodat/gles
Support GLES on desktop
2024-12-28 20:28:57 +02:00
wheremyfoodat
cb8b13e129 Support GLES on desktop 2024-12-28 19:52:42 +02:00
wheremyfoodat
80ccede765
Wayland fixes part 1 2024-12-28 17:14:11 +02:00
wheremyfoodat
7c2918f3f7
GL: Add usingGLES to driverInfo struct (#694) 2024-12-27 11:45:28 +02:00
Thomas
e8c0b7f9c5
Store configuration file in AppData root if not in working directory (#693)
* Store configuration file in AppData root if not in working directory

This fixes MacOS app bundles, as the emulator cannot write the config
file into the app bundle.

* Remove duplicate fs calls

* I'm an idiot sandwich

---------

Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
2024-12-26 23:26:37 +02:00
88 changed files with 3844 additions and 573 deletions

22
.github/gles.patch vendored
View file

@ -1,25 +1,3 @@
diff --git a/src/host_shaders/opengl_display.frag b/src/host_shaders/opengl_display.frag
index 612671c8..1937f711 100644
--- a/src/host_shaders/opengl_display.frag
+++ b/src/host_shaders/opengl_display.frag
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
in vec2 UV;
out vec4 FragColor;
diff --git a/src/host_shaders/opengl_display.vert b/src/host_shaders/opengl_display.vert
index 990e2f80..2e7842ac 100644
--- a/src/host_shaders/opengl_display.vert
+++ b/src/host_shaders/opengl_display.vert
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
out vec2 UV;
void main() {
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
index 9f07df0b..96a35afa 100644
--- a/src/host_shaders/opengl_fragment_shader.frag

View file

@ -23,6 +23,9 @@ jobs:
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- name: Set up gradle caches
uses: actions/cache@v4
with:
@ -34,9 +37,9 @@ jobs:
${{ runner.os }}-pandroid-x86_64-
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -47,7 +50,7 @@ jobs:
java-version: '17'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: |
@ -88,6 +91,9 @@ jobs:
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- name: Set up gradle caches
uses: actions/cache@v4
with:
@ -99,9 +105,9 @@ jobs:
${{ runner.os }}-pandroid-arm64-
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -112,7 +118,7 @@ jobs:
java-version: '17'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto"
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: |

View file

@ -30,9 +30,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -20,9 +20,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -63,9 +63,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -116,9 +116,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -163,9 +163,9 @@ jobs:
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -180,3 +180,36 @@ jobs:
with:
name: Android Hydra core
path: '${{github.workspace}}/build/libAlber.so'
ARM-Libretro:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install misc packages
run: |
sudo apt-get update && sudo apt install libx11-dev libxext-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
- name: Install newer Clang
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x ./llvm.sh
sudo ./llvm.sh 17
- name: Configure CMake
run: |
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DENABLE_VULKAN=OFF -DCRYPTOPP_OPT_DISABLE_ASM=ON
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Upload Libretro core
uses: actions/upload-artifact@v4
with:
name: Linux arm64 Libretro core
path: |
${{github.workspace}}/build/panda3ds_libretro.so
${{github.workspace}}/docs/libretro/panda3ds_libretro.info

View file

@ -33,9 +33,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -33,9 +33,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -25,9 +25,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -26,9 +26,9 @@ jobs:
version: 6.2.0
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -64,9 +64,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -162,9 +162,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -24,9 +24,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: latest
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

39
.github/workflows/iOS_Build.yml vendored Normal file
View file

@ -0,0 +1,39 @@
name: iOS Simulator Build
on:
push:
branches:
- master
pull_request:
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
# The CMake configure and build commands are platform agnostic and should work equally
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Update Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
with:
vulkan-query-version: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
- name: Build core and frontend
run: cd src/pandios && ./build.sh

7
.gitmodules vendored
View file

@ -1,9 +1,6 @@
[submodule "third_party/elfio"]
path = third_party/elfio
url = https://github.com/serge1/ELFIO
[submodule "third_party/SDL2"]
path = third_party/SDL2
url = https://github.com/libsdl-org/SDL
[submodule "third_party/cryptopp/cryptopp"]
path = third_party/cryptopp/cryptopp
url = https://github.com/weidai11/cryptopp
@ -85,3 +82,7 @@
[submodule "third_party/oaknut"]
path = third_party/oaknut
url = https://github.com/panda3ds-emu/oaknut
[submodule "third_party/SDL2"]
path = third_party/SDL2
url = https://github.com/libsdl-org/SDL
branch = SDL2

View file

@ -50,6 +50,7 @@ option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON)
option(ENABLE_METAL "Enable Metal rendering backend (if available)" ON)
option(ENABLE_WAYLAND "Enable Wayland support on Linux platforms" ON)
option(ENABLE_LTO "Enable link-time optimization" OFF)
option(ENABLE_TESTS "Compile unit-tests" OFF)
option(ENABLE_USER_BUILD "Make a user-facing build. These builds have various assertions disabled, LTO, and more" OFF)
@ -63,6 +64,14 @@ option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)
option(BUILD_LIBRETRO_CORE "Build a Libretro core" OFF)
option(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON)
option(DISABLE_SSE4 "Build with SSE4 instructions disabled, may reduce performance" OFF)
option(USE_LIBRETRO_AUDIO "Enable to use the LR audio device with the LR core. Otherwise our own device is used" OFF)
option(IOS_SIMULATOR_BUILD "Compiling for IOS simulator (Set to off if compiling for a real iPhone)" ON)
# Discord RPC & LuaJIT are currently not supported on iOS
if(IOS)
set(ENABLE_DISCORD_RPC OFF)
set(ENABLE_LUAJIT OFF)
endif()
set(OPENGL_PROFILE ${DEFAULT_OPENGL_PROFILE} CACHE STRING "OpenGL profile to use if OpenGL is enabled. Valid values are 'OpenGL' and 'OpenGLES'.")
set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES)
@ -79,6 +88,10 @@ endif()
if(BUILD_LIBRETRO_CORE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_compile_definitions(__LIBRETRO__)
if(USE_LIBRETRO_AUDIO)
add_compile_definitions(USE_LIBRETRO_AUDIO_DEVICE)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD)
@ -97,22 +110,29 @@ endif()
if(GIT_FOUND AND ENABLE_GIT_VERSIONING)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
OUTPUT_VARIABLE PANDA3DS_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags
OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT PANDA3DS_VERSION STREQUAL git_version_tag)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=7
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
unset(git_version_rev)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT git_version_tag STREQUAL "")
set(PANDA3DS_VERSION "${git_version_tag}")
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags
OUTPUT_VARIABLE git_version_desc OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(git_version_tag STREQUAL git_version_desc)
set(git_version_rev "")
endif()
unset(git_version_desc)
endif()
if(NOT git_version_rev STREQUAL "")
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
endif()
string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}")
unset(git_version_tag)
unset(git_version_rev)
endif()
configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp)
include_directories(${CMAKE_BINARY_DIR}/include/)
@ -140,12 +160,15 @@ add_compile_definitions(NOMINMAX) # Make windows.h not define min/ma
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
add_compile_definitions(SDL_MAIN_HANDLED)
if(ENABLE_WAYLAND)
add_compile_definitions(WAYLAND_ENABLED)
endif()
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
add_subdirectory(third_party/discord-rpc)
include_directories(third_party/discord-rpc/include)
endif()
if (NOT ANDROID)
if (USE_SYSTEM_SDL2)
find_package(SDL2 CONFIG REQUIRED)
@ -304,8 +327,8 @@ set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp src/renderdoc.cpp
src/frontend_settings.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/renderdoc.cpp
src/frontend_settings.cpp src/miniaudio/miniaudio.cpp
)
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
@ -390,9 +413,19 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp include/audio/audio_device.hpp
include/audio/audio_device_interface.hpp include/audio/libretro_audio_device.hpp
)
if(IOS)
set(SOURCE_FILES ${SOURCE_FILES} src/miniaudio/miniaudio.m)
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_IOS=1")
if (IOS_SIMULATOR_BUILD)
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_IOS_SIMULATOR=1")
endif()
endif()
cmrc_add_resource_library(
resources_console_fonts
NAMESPACE ConsoleFonts
@ -422,16 +455,22 @@ if(ENABLE_LUAJIT AND NOT ANDROID)
target_link_libraries(AlberCore PRIVATE uv_a)
endif()
set(GL_CONTEXT_SOURCE_FILES "")
if(ENABLE_QT_GUI)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
if(APPLE)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
elseif(WIN32)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
else()
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_wayland.cpp
third_party/duckstation/gl/context_egl_x11.cpp third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_x11.cpp
third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
if(ENABLE_WAYLAND)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl_wayland.cpp)
endif()
endif()
endif()
@ -445,7 +484,7 @@ source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES})
source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES})
source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES})
source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES} ${GL_CONTEXT_SOURCE_FILES})
set(RENDERER_GL_SOURCE_FILES "") # Empty by default unless we are compiling with the GL renderer
set(RENDERER_VK_SOURCE_FILES "") # Empty by default unless we are compiling with the VK renderer
@ -460,8 +499,9 @@ if(ENABLE_OPENGL)
set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp
src/core/renderer_gl/textures.cpp src/core/renderer_gl/etc1.cpp
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag
src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.vert
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert
src/host_shaders/opengl_display.frag src/host_shaders/opengl_es_display.vert
src/host_shaders/opengl_es_display.frag src/host_shaders/opengl_vertex_shader.vert
src/host_shaders/opengl_fragment_shader.frag
)
@ -474,8 +514,10 @@ if(ENABLE_OPENGL)
resources_renderer_gl
NAMESPACE RendererGL
WHENCE "src/host_shaders/"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_display.vert"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_es_display.vert"
"src/host_shaders/opengl_es_display.frag"
"src/host_shaders/opengl_vertex_shader.vert"
"src/host_shaders/opengl_fragment_shader.frag"
)
@ -562,14 +604,16 @@ if(ENABLE_METAL AND APPLE)
include/renderer_mtl/mtl_common.hpp
include/renderer_mtl/pica_to_mtl.hpp
include/renderer_mtl/objc_helper.hpp
include/renderer_mtl/texture_decoder.hpp
)
set(RENDERER_MTL_SOURCE_FILES src/core/renderer_mtl/metal_cpp_impl.cpp
src/core/renderer_mtl/renderer_mtl.cpp
src/core/renderer_mtl/mtl_texture.cpp
src/core/renderer_mtl/mtl_etc1.cpp
src/core/renderer_mtl/mtl_lut_texture.cpp
src/core/renderer_mtl/pica_to_mtl.cpp
src/core/renderer_mtl/objc_helper.mm
src/core/renderer_mtl/texture_decoder.cpp
src/host_shaders/metal_shaders.metal
src/host_shaders/metal_blit.metal
#src/host_shaders/metal_copy_to_lut_texture.metal
@ -583,15 +627,26 @@ if(ENABLE_METAL AND APPLE)
set(SHADER_SOURCE "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metal")
set(SHADER_IR "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.ir")
set(SHADER_METALLIB "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metallib")
# MacOS, iOS and the iOS simulator all use different compilation options for shaders
set(MetalSDK "macosx")
if(IOS)
if (IOS_SIMULATOR_BUILD)
set(MetalSDK "iphonesimulator")
else()
set(MetalSDK "iphoneos")
endif()
endif()
# TODO: only include sources in debug builds
add_custom_command(
OUTPUT ${SHADER_IR}
COMMAND xcrun -sdk macosx metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
COMMAND xcrun -sdk ${MetalSDK} metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
DEPENDS ${SHADER_SOURCE}
VERBATIM)
add_custom_command(
OUTPUT ${SHADER_METALLIB}
COMMAND xcrun -sdk macosx metallib -o ${SHADER_METALLIB} ${SHADER_IR}
COMMAND xcrun -sdk ${MetalSDK} metallib -o ${SHADER_METALLIB} ${SHADER_IR}
DEPENDS ${SHADER_IR}
VERBATIM)
set(RENDERER_MTL_HOST_SHADERS_SOURCES ${RENDERER_MTL_HOST_SHADERS_SOURCES} ${SHADER_METALLIB})
@ -620,7 +675,7 @@ if(ENABLE_METAL AND APPLE)
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_METAL=1")
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
# TODO: check if all of them are needed
target_link_libraries(AlberCore PRIVATE "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
target_link_libraries(AlberCore PUBLIC "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
endif()
source_group("Header Files\\Core" FILES ${HEADER_FILES})
@ -730,7 +785,10 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
)
# Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts)
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts
docs/translations/sv.ts
)
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
@ -751,11 +809,17 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
endif()
target_link_libraries(Alber PRIVATE AlberCore)
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${APP_RESOURCES})
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES})
elseif(BUILD_HYDRA_CORE)
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
include_directories(third_party/hydra_core/include)
add_library(Alber SHARED src/hydra_core.cpp)
set(SHARED_SOURCE_FILES src/hydra_core.cpp)
if(IOS)
set(SHARED_SOURCE_FILES ${SHARED_SOURCE_FILES} src/ios_driver.mm)
endif()
add_library(Alber SHARED ${SHARED_SOURCE_FILES})
target_link_libraries(Alber PUBLIC AlberCore)
elseif(BUILD_LIBRETRO_CORE)
include_directories(third_party/libretro/include)

774
docs/translations/sv.ts Normal file
View file

@ -0,0 +1,774 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="sv_SE">
<extra-po-header-language>sv</extra-po-header-language>
<extra-po-header-language_team></extra-po-header-language_team>
<extra-po-header-last_translator>Daniel Nylander &lt;github@danielnylander.se&gt;</extra-po-header-last_translator>
<extra-po-header-po_revision_date></extra-po-header-po_revision_date>
<extra-po-header-pot_creation_date></extra-po-header-pot_creation_date>
<extra-po-header-project_id_version></extra-po-header-project_id_version>
<extra-po-header-x_generator>Poedit 3.5</extra-po-header-x_generator>
<extra-po-headers>Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,X-Qt-Contexts,X-Generator</extra-po-headers>
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation>Om Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
<translation>Panda3DS är en Nintendo 3DS-emulator med fri och öppen källkod för Windows, MacOS och Linux</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
<translation>Besök panda3ds.com för att hjälp med Panda3DS och länkar till våra officiella supportwebbplatser.</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation>Panda3DS utvecklas av volontärer deras fritid. Nedan finns en lista över några av dessa volontärer som har gått med att listas här, utan någon särskild ordning.&lt;br&gt;Om du tycker att du också borde listas här, informera oss&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</translation>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation>Redigera fusk</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation>Fusknamn</translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation>Redigera</translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation>Fusk</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation>Lägg till</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation>Ta bort</translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation>Konfiguration</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation>Inställningar för gränssnitt</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation>System</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation>Ljus</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation>Mörk</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation>Hälsningskatt</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation>Grädde</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation>Färgtema</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation>Glad panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation>Glad panda (färgglad)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation>Sömnig panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation>Ko-panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation>Pingvinen från SkyEmu</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation>Fönsterikon</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation>Språk</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation>Visa version fönstertitel</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="15"/>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber v%1</source>
<translation>Alber v%1</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
<source>Remember window position</source>
<translation>Kom ihåg fönstrets position</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation>Allmänna inställningar</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation>Bläddra...</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation>Välj katalog</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation>Standardsökväg för ROMar</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation>Aktivera Discord RPC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation>Använd portabelt bygge</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation>Skriv ut versionen i konsolutmatningen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation>Grafikinställningar</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
<source>Null</source>
<translation>Null</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
<source>OpenGL</source>
<translation>OpenGL</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
<source>Vulkan</source>
<translation>Vulkan</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
<source>GPU renderer</source>
<translation>GPU-rendering</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation>Aktivera Renderdoc</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation>Aktivera shader JIT</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation>Aktivera VSync</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation>Använda ubershaders (inga hackningar, kanske långsammare)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation>Korrekt multiplicering av shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation>Snabbare shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation>Tvinga fram shadergen vid rendering av ljus</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation>Ljuströskel för att tvinga shadergen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation>Ljudinställningar</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
<source>LLE</source>
<translation>LLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
<source>HLE</source>
<translation>HLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
<source>DSP emulation</source>
<translation>DSP-emulering</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation>Aktivera ljud</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation>Aktivera AAC-ljud</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation>Skriv ut firmware för DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation>Stäng av ljudet audioenheten</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation>Kubisk</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation>Linjär</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation>Volymkurva</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation>Ljudenhetens volym</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation>Batteriinställningar</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation>Batteriprocent</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation>Laddaren är ansluten</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation>Inställningar för SD-kort</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation>Aktivera virtuellt SD-kort</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation>Skrivskydd för virtuellt SD-kort</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation>Gränssnitt</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation>Inställningar för användargränssnitt</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation>Allmänt</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation>Allmänna inställningar för emulatorn</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation>Grafik</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation>Inställningar för grafikemulering och utdata</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation>Ljud</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation>Inställningar för ljudemulering och utdata</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation>Batteri</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation>Inställningar för batteriemulering</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation>SD-kort</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation>Inställningar för emulering av SD-kort</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation>Språkändringen lyckades</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation>Starta om Panda3DS för att det nya språket ska kunna användas.</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation>Språkändringen misslyckades</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation>Det språk du valde ingår inte i Panda3DS. Om du ser detta, har någon rört till koden för språkgränssnittet...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
<source>File</source>
<translation>Arkiv</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation>Emulering</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation>Verktyg</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation>Om</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation>Läs in spel</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation>Läs in Lua-skript</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation>Öppna Panda3DS-mappen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation>Pausa</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation>Återuppta</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation>Starta om</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation>Konfigurera</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation>Dumpa RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation>Öppna Lua-redigeraren</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation>Öppna fuskredigeraren</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation>Öppna patchfönstret</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation>Öppna shader-redigeraren</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation>Dumpa inläst DSP-firmware</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation>Om Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation>Välj 3DS ROM att läsa in</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
<translation>Nintendo 3DS ROM (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Select Lua script to load</source>
<translation>Välj Lua-skript som ska läsas in</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation>Lua-skript (*.lua *.txt)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
<source>Select folder to dump RomFS files to</source>
<translation>Välj mapp för att dumpa RomFS-filer till</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation>Ogiltigt format för RomFS-dumpning</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
<source>The currently loaded app is not in a format that supports RomFS</source>
<translation>Den aktuella appen är inte i ett format som stöder RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
<source>OK</source>
<translation>Ok</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS found</source>
<translation>Ingen RomFS hittades</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS partition was found in the loaded app</source>
<translation>Ingen RomFS-partition hittades i den inlästa appen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation>Välj fil</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation>DSP firmware-fil (*.cdc)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation>Ingen firmware för DSP inläst</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
<translation>Den aktuella appen har inte skickat upp någon firmware till DSP:n</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation>Misslyckades med att öppna utdatafilen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
<translation>Den aktuella DSP-firmware som lästes in kunde inte skrivas till den valda filen. Kontrollera att du har behörighet att komma åt den här filen</translation>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation>ROM-patchare</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation>Välj inmatningsfil</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
<source>Select</source>
<translation>Välj</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Select patch file</source>
<translation>Välj patchfil</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation>Applicera patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation>Välj fil som ska patchas</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>All files (*.*)</source>
<translation>Alla filer (*.*)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation>Patch-filer (*.ips *.ups *.bps)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation>Sökvägar anges inte korrekt</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Please provide paths for both the input file and the patch file</source>
<translation>Ange sökvägar för både indatafilen och patchfilen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation>Välj fil</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation>Ingen sökväg för utmatning</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No path was provided for the output file, no patching was done</source>
<translation>Ingen sökväg angavs för utdatafilen, ingen patchning gjordes</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation>Okänt patchformat</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
<translation>Okänt format för patchfil. För närvarande stöds IPS, UPS och BPS</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation>Misslyckades med att öppna indatafiler</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation>Se till att de finns i en katalog som Panda3DS har tillgång till</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation>Patchning lyckades</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation>Din fil patchades.</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation>Kontrollsumman stämmer inte överens</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
<translation>Patchen applicerades men en avvikelse i kontrollsumman upptäcktes. Inmatnings- eller utdatafilerna kanske inte är korrekta</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation>Fel vid patchning</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation>Ett fel uppstod vid patchning</translation>
</message>
</context>
<context>
<name>PatchWindow::PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
<source>OK</source>
<translation>Ok</translation>
</message>
</context>
<context>
<name>ShaderEditorWindow</name>
<message>
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
<source>Reload shader</source>
<translation>Läs om shader</translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation>Lua-redigerare</translation>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation>Läs in skript</translation>
</message>
</context>
</TS>

View file

@ -2,8 +2,8 @@
namespace PICA::ShaderGen {
// Graphics API this shader is targetting
enum class API { GL, GLES, Vulkan };
enum class API { GL, GLES, Vulkan, Metal };
// Shading language to use (Only GLSL for the time being)
enum class Language { GLSL };
} // namespace PICA::ShaderGen
// Shading language to use
enum class Language { GLSL, MSL };
} // namespace PICA::ShaderGen

View file

@ -0,0 +1,9 @@
#pragma once
#if defined(__LIBRETRO__) && defined(USE_LIBRETRO_AUDIO_DEVICE)
#include "audio/libretro_audio_device.hpp"
using AudioDevice = LibretroAudioDevice;
#else
#include "audio/miniaudio_device.hpp"
using AudioDevice = MiniAudioDevice;
#endif

View file

@ -0,0 +1,36 @@
#pragma once
#include <array>
#include "config.hpp"
#include "helpers.hpp"
#include "ring_buffer.hpp"
class AudioDeviceInterface {
protected:
static constexpr usize maxFrameCount = 0x2000;
using Samples = Common::RingBuffer<s16, maxFrameCount * 2>;
using RenderBatchCallback = usize (*)(const s16*, usize);
Samples* samples = nullptr;
const AudioDeviceConfig& audioSettings;
// Store the last stereo sample we output. We play this when underruning to avoid pops.
std::array<s16, 2> lastStereoSample{};
public:
AudioDeviceInterface(Samples* samples, const AudioDeviceConfig& audioSettings) : samples(samples), audioSettings(audioSettings) {}
bool running = false;
Samples* getSamples() { return samples; }
// If safe is on, we create a null audio device
virtual void init(Samples& samples, bool safe = false) = 0;
virtual void close() = 0;
virtual void start() = 0;
virtual void stop() = 0;
// Only used for audio devices that render multiple audio frames in one go, eg the libretro audio device.
virtual void renderBatch(RenderBatchCallback callback) {}
};

View file

@ -0,0 +1,61 @@
#pragma once
#include <cstring>
#include "audio/audio_device_interface.hpp"
class LibretroAudioDevice final : public AudioDeviceInterface {
bool initialized = false;
public:
LibretroAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
running = false;
}
void init(Samples& samples, bool safe = false) override {
this->samples = &samples;
initialized = true;
running = false;
}
void close() override {
initialized = false;
running = false;
};
void start() override { running = true; }
void stop() override { running = false; };
void renderBatch(RenderBatchCallback callback) override {
if (running) {
static constexpr usize sampleRate = 32768; // 3DS samples per second
static constexpr usize frameCount = sampleRate / 60; // 3DS samples per video frame
static constexpr usize channelCount = 2;
static s16 audioBuffer[frameCount * channelCount];
usize samplesWritten = 0;
samplesWritten += samples->pop(audioBuffer, frameCount * channelCount);
// Get the last sample for underrun handling
if (samplesWritten != 0) {
std::memcpy(&lastStereoSample[0], &audioBuffer[(samplesWritten - 1) * 2], sizeof(lastStereoSample));
}
// If underruning, copy the last output sample
{
s16* pointer = &audioBuffer[samplesWritten * 2];
s16 l = lastStereoSample[0];
s16 r = lastStereoSample[1];
for (usize i = samplesWritten; i < frameCount; i++) {
*pointer++ = l;
*pointer++ = r;
}
}
callback(audioBuffer, sizeof(audioBuffer) / (channelCount * sizeof(s16)));
}
}
bool isInitialized() const { return initialized; }
};

View file

@ -3,39 +3,31 @@
#include <string>
#include <vector>
#include "config.hpp"
#include "helpers.hpp"
#include "audio/audio_device_interface.hpp"
#include "miniaudio.h"
#include "ring_buffer.hpp"
class MiniAudioDevice {
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
class MiniAudioDevice final : public AudioDeviceInterface {
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
bool initialized = false;
ma_device device;
ma_context context;
ma_device_config deviceConfig;
Samples* samples = nullptr;
const AudioDeviceConfig& audioSettings;
bool initialized = false;
bool running = false;
// Store the last stereo sample we output. We play this when underruning to avoid pops.
std::array<s16, 2> lastStereoSample;
std::vector<std::string> audioDevices;
public:
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
// If safe is on, we create a null audio device
void init(Samples& samples, bool safe = false);
void close();
void init(Samples& samples, bool safe = false) override;
void close() override;
void start();
void stop();
void start() override;
void stop() override;
bool isInitialized() const { return initialized; }
};

View file

@ -55,6 +55,13 @@ struct EmulatorConfig {
static constexpr bool audioEnabledDefault = false;
#endif
// We default to OpenGL on all platforms other than iOS
#if defined(PANDA3DS_IOS)
static constexpr RendererType rendererDefault = RendererType::Metal;
#else
static constexpr RendererType rendererDefault = RendererType::OpenGL;
#endif
bool shaderJitEnabled = shaderJitDefault;
bool useUbershaders = ubershaderDefault;
bool accelerateShaders = accelerateShadersDefault;
@ -65,7 +72,7 @@ struct EmulatorConfig {
bool forceShadergenForLights = true;
int lightShadergenThreshold = 1;
RendererType rendererType = RendererType::OpenGL;
RendererType rendererType = rendererDefault;
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
bool sdCardInserted = true;

View file

@ -181,5 +181,7 @@ class CPU {
void addTicks(u64 ticks) { env.AddTicks(ticks); }
void clearCache() { jit->ClearCache(); }
void clearCacheRange(u32 start, u32 size) { jit->InvalidateCacheRange(start, size); }
void runFrame();
};

View file

@ -7,8 +7,8 @@
#include <span>
#include "PICA/gpu.hpp"
#include "audio/audio_device.hpp"
#include "audio/dsp_core.hpp"
#include "audio/miniaudio_device.hpp"
#include "cheats.hpp"
#include "config.hpp"
#include "cpu.hpp"
@ -48,14 +48,14 @@ class Emulator {
Scheduler scheduler;
Crypto::AESEngine aesEngine;
MiniAudioDevice audioDevice;
AudioDevice audioDevice;
Cheats cheats;
public:
static constexpr u32 width = 400;
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
ROMType romType = ROMType::None;
bool running = false; // Is the emulator running a game?
bool running = false; // Is the emulator running a game?
private:
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
@ -89,7 +89,6 @@ class Emulator {
~Emulator();
void step();
void render();
void reset(ReloadOption reload);
void runFrame();
// Poll the scheduler for events
@ -127,6 +126,7 @@ class Emulator {
LuaManager& getLua() { return lua; }
Scheduler& getScheduler() { return scheduler; }
Memory& getMemory() { return memory; }
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
RendererType getRendererType() const { return config.rendererType; }
Renderer* getRenderer() { return gpu.getRenderer(); }

View file

@ -11,6 +11,7 @@ struct FrontendSettings {
Dark = 2,
GreetingsCat = 3,
Cream = 4,
Oled = 5,
};
// Different panda-themed window icons

7
include/ios_driver.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
void iosCreateEmulator();
void iosLoadROM(NSString* pathNS);
void iosRunFrame(CAMetalLayer* layer);

View file

@ -175,6 +175,8 @@ public:
void svcSignalEvent();
void svcSetTimer();
void svcSleepThread();
void svcInvalidateInstructionCacheRange();
void svcInvalidateEntireInstructionCache();
void connectToPort();
void outputDebugString();
void waitSynchronization1();
@ -250,4 +252,6 @@ public:
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
void clearInstructionCache();
void clearInstructionCacheRange(u32 start, u32 size);
u32 getSharedFontVaddr();
};

View file

@ -19,7 +19,7 @@ struct ResourceLimitValues {
// APPLICATION resource limit
static constexpr ResourceLimitValues appResourceLimits = {
.maxPriority = 0x18,
.maxCommit = 0x4000000,
.maxCommit = 64_MB + 16_MB, // We're currently giving 80MB to all apps. TODO: Implement extended memory properly
.maxThreads = 0x20,
.maxEvents = 0x20,
.maxMutexes = 0x20,
@ -33,7 +33,7 @@ static constexpr ResourceLimitValues appResourceLimits = {
// SYS_APPLET resource limit
static constexpr ResourceLimitValues sysAppletResourceLimits = {
.maxPriority = 0x4,
.maxCommit = 0x5E00000,
.maxCommit = 0x5E00000 - 16_MB,
.maxThreads = 0x1D,
.maxEvents = 0xB,
.maxMutexes = 0x8,

View file

@ -132,7 +132,7 @@ public:
static constexpr u32 totalPageCount = 1 << (32 - pageShift);
static constexpr u32 FCRAM_SIZE = u32(128_MB);
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB);
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(80_MB);
static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize;
static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize;

View file

@ -53,6 +53,7 @@ class Renderer {
EmulatorConfig* emulatorConfig = nullptr;
void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap);
public:
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
virtual ~Renderer();
@ -81,6 +82,14 @@ class Renderer {
virtual std::string getUbershader() { return ""; }
virtual void setUbershader(const std::string& shader) {}
// Only relevant for OpenGL renderer and other OpenGL-based backends (eg software)
// Called to notify the core to use OpenGL ES and not desktop GL
virtual void setupGLES() {}
// Only relevant for Metal renderer on iOS
// Passes a SwiftUI MTKView's layer (CAMetalLayer) to the renderer
virtual void setMTKLayer(void* layer) {};
// This function is called on every draw call before parsing vertex data.
// It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between
// ubershaders and shadergen, and so on.

View file

@ -4,6 +4,7 @@
// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information
namespace OpenGL {
struct Driver {
bool usingGLES = false;
bool supportsExtFbFetch = false;
bool supportsArmFbFetch = false;

View file

@ -40,7 +40,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexArray hwShaderVAO;
OpenGL::VertexBuffer vbo;
// Data
// Data
struct {
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
@ -157,6 +157,7 @@ class RendererGL final : public Renderer {
void initGraphicsContextInternal();
void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
void compileDisplayShader();
public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
@ -169,14 +170,15 @@ class RendererGL final : public Renderer {
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void deinitGraphicsContext() override;
virtual bool supportsShaderReload() override { return true; }
virtual std::string getUbershader() override;
virtual void setUbershader(const std::string& shader) override;
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) override;
virtual void setupGLES() override;
std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
// Note: The caller is responsible for deleting the currently bound FBO before calling this

View file

@ -57,7 +57,7 @@ namespace Metal {
} else if (std::is_same<Format_t, PICA::DepthFmt>::value) {
pixelFormat = PICA::toMTLPixelFormatDepth((PICA::DepthFmt)format);
} else {
panic("Invalid format type");
Helpers::panic("Invalid format type");
}
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();

View file

@ -8,8 +8,9 @@
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "math_util.hpp"
#include "opengl.hpp"
#include "renderer_mtl/pica_to_mtl.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
template <typename T>
using Interval = boost::icl::right_open_interval<T>;
@ -27,7 +28,8 @@ namespace Metal {
// Range of VRAM taken up by buffer
Interval<u32> range;
PICA::PixelFormatInfo formatInfo;
PICA::MTLPixelFormatInfo formatInfo;
MTL::Texture* base = nullptr;
MTL::Texture* texture = nullptr;
MTL::SamplerState* sampler = nullptr;
@ -52,22 +54,7 @@ namespace Metal {
void free();
u64 sizeInBytes();
u8 decodeTexelU8(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
u16 decodeTexelU16(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
u32 decodeTexelU32(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
// Get the morton interleave offset of a texel based on its U and V values
static u32 mortonInterleave(u32 u, u32 v);
// Get the byte offset of texel (u, v) in the texture
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
// Returns the format of this texture as a string
std::string_view formatToString() { return PICA::textureFormatToString(format); }
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
// TODO: Make hasAlpha a template parameter
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
};
} // namespace Metal

View file

@ -3,31 +3,28 @@
#include <Metal/Metal.hpp>
#include "PICA/regs.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
namespace PICA {
struct PixelFormatInfo {
struct MTLPixelFormatInfo {
MTL::PixelFormat pixelFormat;
size_t bytesPerTexel;
void (*decoder)(OpenGL::uvec2, u32, u32, std::span<const u8>, u8*);
bool needsSwizzle = false;
MTL::TextureSwizzleChannels swizzle{
.red = MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleGreen,
.blue = MTL::TextureSwizzleBlue,
.alpha = MTL::TextureSwizzleAlpha,
};
};
constexpr PixelFormatInfo pixelFormatInfos[14] = {
{MTL::PixelFormatRGBA8Unorm, 4}, // RGBA8
{MTL::PixelFormatRGBA8Unorm, 4}, // RGB8
{MTL::PixelFormatBGR5A1Unorm, 2}, // RGBA5551
{MTL::PixelFormatB5G6R5Unorm, 2}, // RGB565
{MTL::PixelFormatABGR4Unorm, 2}, // RGBA4
{MTL::PixelFormatRGBA8Unorm, 4}, // IA8
{MTL::PixelFormatRG8Unorm, 2}, // RG8
{MTL::PixelFormatRGBA8Unorm, 4}, // I8
{MTL::PixelFormatA8Unorm, 1}, // A8
{MTL::PixelFormatABGR4Unorm, 2}, // IA4
{MTL::PixelFormatABGR4Unorm, 2}, // I4
{MTL::PixelFormatA8Unorm, 1}, // A4
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1A4
};
extern MTLPixelFormatInfo mtlPixelFormatInfos[14];
inline PixelFormatInfo getPixelFormatInfo(TextureFmt format) { return pixelFormatInfos[static_cast<int>(format)]; }
void checkForMTLPixelFormatSupport(MTL::Device* device);
inline MTLPixelFormatInfo getMTLPixelFormatInfo(TextureFmt format) { return mtlPixelFormatInfos[static_cast<int>(format)]; }
inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) {
switch (format) {
@ -35,7 +32,11 @@ namespace PICA {
case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm;
case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm?
case ColorFmt::RGB565: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatB5G6R5Unorm?
#ifdef PANDA3DS_IOS
case ColorFmt::RGBA4: return MTL::PixelFormatRGBA8Unorm; // IOS + Metal doesn't support AGBR4 properly, at least on simulator
#else
case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm;
#endif
}
}

View file

@ -42,11 +42,13 @@ class RendererMTL final : public Renderer {
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
#endif
private:
CA::MetalLayer* metalLayer;
virtual void setMTKLayer(void* layer) override;
MTL::Device* device;
MTL::CommandQueue* commandQueue;
private:
CA::MetalLayer* metalLayer = nullptr;
MTL::Device* device = nullptr;
MTL::CommandQueue* commandQueue = nullptr;
Metal::CommandEncoder commandEncoder;
@ -98,6 +100,7 @@ class RendererMTL final : public Renderer {
void endRenderPass() {
if (renderCommandEncoder) {
renderCommandEncoder->endEncoding();
renderCommandEncoder->release();
renderCommandEncoder = nullptr;
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "helpers.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
void decodeTexelABGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelBGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA1BGR5ToBGR5A1(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA1BGR5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelB5G6R5ToB5G6R5(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelB5G6R5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelABGR4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelABGR4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelAI8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelGR8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelI8ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA8ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelAI4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelAI4ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelI4ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA4ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelETC1ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelETC1A4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);

View file

@ -23,6 +23,7 @@ class CFGService {
void getConfigInfoBlk2(u32 messagePointer);
void getConfigInfoBlk8(u32 messagePointer, u32 commandWord);
void getCountryCodeID(u32 messagePointer);
void getCountryCodeString(u32 messagePointer);
void getLocalFriendCodeSeed(u32 messagePointer);
void getRegionCanadaUSA(u32 messagePointer);
void getSystemModel(u32 messagePointer);

View file

@ -35,7 +35,7 @@ Panda3DS is still in the early stages of development. Many games boot, many don'
For documenting game compatibility, make sure to visit the [games list repository](https://github.com/Panda3DS-emu/Panda3DS-Games-List). For miscellaneous issues or more technical issues, feel free to use this repo's issues tab.
# Why?
The 3DS emulation scene is already pretty mature, with offerings such as [Citra](https://github.com/citra-emu/citra) which can offer a great playing experience for most games in the library, [Corgi3DS](https://github.com/PSI-Rockin/Corgi3DS), an innovative LLE emulator, or [Mikage](https://mikage.app/). However, there's always room for more emulators! While Panda3DS was initially a mere curiosity, there's many different concepts I would like to explore with it in the future, such as:
The 3DS emulation scene is already pretty mature, with offerings such as Citra which can offer a great playing experience for most games in the library, [Corgi3DS](https://github.com/PSI-Rockin/Corgi3DS), an innovative LLE emulator, or [Mikage](https://mikage.app/). However, there's always room for more emulators! While Panda3DS was initially a mere curiosity, there's many different concepts I would like to explore with it in the future, such as:
- Virtualization. What motivated the creation of this emulator was actually a discussion on whether it is possible to get fast 3DS emulation on low-end hardware such as the Raspberry Pi 4, using the KVM API. At the moment, Panda3DS is powered by Dynarmic rather than using virtualization, but this is definitely a concept I want to explore in the future.
@ -113,11 +113,12 @@ Panda3DS also supports controller input using the SDL2 GameController API.
- [SkyEmu](https://github.com/skylersaleh/SkyEmu): A seagull-themed low-level GameBoy, GameBoy Color, GameBoy Advance and Nintendo DS emulator that is designed to be easy to use, cross platform and accurate.
- [NanoBoyAdvance](https://github.com/nba-emu/NanoBoyAdvance): A Game Boy Advance emulator focusing on hardware research and cycle-accurate emulation
- [Dust](https://github.com/kelpsyberry/dust): Nintendo DS emulator for desktop devices and the web
- [felix86](https://github.com/OFFTKP/felix86): A new x86-64 → RISC-V Linux userspace emulator
- [ChonkyStation](https://github.com/liuk7071/ChonkyStation): Work-in-progress PlayStation emulator
- [ChonkyStation 3](https://github.com/liuk7071/ChonkyStation3): Experimental HLE PS3 emulator for Windows, MacOS and Linux
- [MelonDS](https://github.com/melonDS-emu/melonDS): "DS emulator, sorta" - Arisotura
- [Kaizen](https://github.com/SimoneN64/Kaizen): Experimental work-in-progress low-level N64 emulator
- [ChonkyStation](https://github.com/liuk7071/ChonkyStation): Work-in-progress PlayStation emulator
- [shadPS4](https://github.com/shadps4-emu/shadPS4): Work-in-progress PS4 emulator by the founder of PCSX, PCSX2 and more
- [Hydra](https://github.com/hydra-emu/hydra): Cross-platform GameBoy, NES, N64 and Chip-8 emulator
- [Tanuki3DS](https://github.com/burhanr13/Tanuki3DS/): A new 3DS emulator for MacOS and Linux
# Support
If you find this project exciting and want to support the founder, check out [his Patreon](https://www.patreon.com/wheremyfoodat) or [Ko-fi](https://ko-fi.com/wheremyfoodat)

View file

@ -27,6 +27,7 @@ void EmulatorConfig::load() {
return;
}
printf("Loading existing configuration file %s\n", path.string().c_str());
toml::value data;
try {
@ -71,14 +72,14 @@ void EmulatorConfig::load() {
auto gpu = gpuResult.unwrap();
// Get renderer
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", "OpenGL");
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", Renderer::typeToString(rendererDefault));
auto configRendererType = Renderer::typeFromString(rendererName);
if (configRendererType.has_value()) {
rendererType = configRendererType.value();
} else {
Helpers::warn("Invalid renderer specified: %s\n", rendererName.c_str());
rendererType = RendererType::OpenGL;
rendererType = rendererDefault;
}
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault);

View file

@ -284,7 +284,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break;
case VertexShaderOpDescriptorIndex: {
shaderUnit.vs.setOpDescriptorIndex(value);
shaderUnit.vs.setOpDescriptorIndex(newValue);
break;
}
@ -301,7 +301,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
}
case VertexBoolUniform: {
shaderUnit.vs.uploadBoolUniform(value & 0xffff);
shaderUnit.vs.uploadBoolUniform(newValue & 0xffff);
break;
}
@ -309,7 +309,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
case VertexIntUniform1:
case VertexIntUniform2:
case VertexIntUniform3: {
shaderUnit.vs.uploadIntUniform(index - VertexIntUniform0, value);
shaderUnit.vs.uploadIntUniform(index - VertexIntUniform0, newValue);
break;
}
@ -326,7 +326,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
}
case VertexShaderEntrypoint: {
shaderUnit.vs.entrypoint = value & 0xffff;
shaderUnit.vs.entrypoint = newValue & 0xffff;
break;
}
@ -336,13 +336,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break;
*/
case VertexShaderTransferIndex: shaderUnit.vs.setBufferIndex(value); break;
case VertexShaderTransferIndex: shaderUnit.vs.setBufferIndex(newValue); break;
// Command lists can write to the command processor registers and change the command list stream
// Several games are known to do this, including New Super Mario Bros 2 and Super Mario 3D Land
case CmdBufTrigger0:
case CmdBufTrigger1: {
if (value != 0) { // A non-zero value triggers command list processing
if (newValue != 0) { // A non-zero value triggers command list processing
int bufferIndex = index - CmdBufTrigger0; // Index of the command buffer to execute (0 or 1)
u32 addr = (regs[CmdBufAddr0 + bufferIndex] & 0xfffffff) << 3;
u32 size = (regs[CmdBufSize0 + bufferIndex] & 0xfffff) << 3;

View file

@ -7,8 +7,9 @@
#include "helpers.hpp"
MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings)
: initialized(false), running(false), samples(nullptr), audioSettings(audioSettings) {}
MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
running = false;
}
void MiniAudioDevice::init(Samples& samples, bool safe) {
this->samples = &samples;
@ -212,4 +213,4 @@ void MiniAudioDevice::close() {
ma_device_uninit(&device);
ma_context_uninit(&context);
}
}
}

View file

@ -69,6 +69,10 @@ void Kernel::serviceSVC(u32 svc) {
case 0x3A: getResourceLimitCurrentValues(); break;
case 0x3B: getThreadContext(); break;
case 0x3D: outputDebugString(); break;
// Luma SVCs
case 0x93: svcInvalidateInstructionCacheRange(); break;
case 0x94: svcInvalidateEntireInstructionCache(); break;
default: Helpers::panic("Unimplemented svc: %X @ %08X", svc, regs[15]); break;
}
@ -298,6 +302,23 @@ void Kernel::duplicateHandle() {
}
void Kernel::clearInstructionCache() { cpu.clearCache(); }
void Kernel::clearInstructionCacheRange(u32 start, u32 size) { cpu.clearCacheRange(start, size); }
void Kernel::svcInvalidateInstructionCacheRange() {
const u32 start = regs[0];
const u32 size = regs[1];
logSVC("svcInvalidateInstructionCacheRange(start = %08X, size = %08X)\n", start, size);
clearInstructionCacheRange(start, size);
regs[0] = Result::Success;
}
void Kernel::svcInvalidateEntireInstructionCache() {
logSVC("svcInvalidateEntireInstructionCache()\n");
clearInstructionCache();
regs[0] = Result::Success;
}
namespace SystemInfoType {
enum : u32 {

View file

@ -122,7 +122,10 @@ void Kernel::mapMemoryBlock() {
}
if (KernelHandles::isSharedMemHandle(block)) {
if (block == KernelHandles::FontSharedMemHandle && addr == 0) addr = 0x18000000;
if (block == KernelHandles::FontSharedMemHandle && addr == 0) {
addr = getSharedFontVaddr();
}
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
// Pass pointer to shared memory to the appropriate service
@ -216,3 +219,8 @@ void Kernel::unmapMemoryBlock() {
Helpers::warn("Stubbed svcUnmapMemoryBlock!");
regs[0] = Result::Success;
}
u32 Kernel::getSharedFontVaddr() {
// Place shared font at the very beginning of system FCRAM
return mem.getLinearHeapVaddr() + Memory::FCRAM_APPLICATION_SIZE;
}

View file

@ -51,17 +51,12 @@ void RendererGL::reset() {
gl.useProgram(oldProgram); // Switch to old GL program
}
#ifdef USING_GLES
fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL);
#endif
}
void RendererGL::initGraphicsContextInternal() {
gl.reset();
auto gl_resources = cmrc::RendererGL::get_filesystem();
auto vertexShaderSource = gl_resources.open("opengl_vertex_shader.vert");
auto fragmentShaderSource = gl_resources.open("opengl_fragment_shader.frag");
@ -70,16 +65,7 @@ void RendererGL::initGraphicsContextInternal() {
triangleProgram.create({vert, frag});
initUbershader(triangleProgram);
auto displayVertexShaderSource = gl_resources.open("opengl_display.vert");
auto displayFragmentShaderSource = gl_resources.open("opengl_display.frag");
OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex);
OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment);
displayProgram.create({vertDisplay, fragDisplay});
gl.useProgram(displayProgram);
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
compileDisplayShader();
// Create stream buffers for vertex, index and uniform buffers
static constexpr usize hwIndexBufferSize = 2_MB;
static constexpr usize hwVertexBufferSize = 16_MB;
@ -191,6 +177,7 @@ void RendererGL::initGraphicsContextInternal() {
}
reset();
fragShaderGen.setTarget(driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL);
// Populate our driver info structure
driverInfo.supportsExtFbFetch = (GLAD_GL_EXT_shader_framebuffer_fetch != 0);
@ -805,6 +792,8 @@ void RendererGL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
shutUpCounter++;
printf("RendererGL::TextureCopy failed to locate src framebuffer!\n");
}
doSoftwareTextureCopy(inputAddr, outputAddr, copySize, inputWidth, inputGap, outputWidth, outputGap);
return;
}
@ -850,9 +839,9 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
PICA::FragmentConfig fsConfig(regs);
// If we're not on GLES, ignore the logic op configuration and don't generate redundant shaders for it, since we use hw logic ops
#ifndef USING_GLES
fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0);
#endif
if (!driverInfo.usingGLES) {
fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0);
}
OpenGL::Shader& fragShader = shaderCache.fragmentShaderCache[fsConfig];
if (!fragShader.exists()) {
@ -1010,7 +999,7 @@ bool RendererGL::prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration*
std::string picaShaderSource = PICA::ShaderGen::decompileShader(
shaderUnit.vs, *emulatorConfig, shaderUnit.vs.entrypoint,
Helpers::isAndroid() ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL
driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL
);
// Empty source means compilation error, if the source is not empty then we convert the recompiled PICA code into a valid shader and upload
@ -1156,6 +1145,19 @@ void RendererGL::initUbershader(OpenGL::Program& program) {
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
}
void RendererGL::compileDisplayShader() {
auto gl_resources = cmrc::RendererGL::get_filesystem();
auto displayVertexShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.vert") : gl_resources.open("opengl_display.vert");
auto displayFragmentShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.frag") : gl_resources.open("opengl_display.frag");
OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex);
OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment);
displayProgram.create({vertDisplay, fragDisplay});
gl.useProgram(displayProgram);
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
}
void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) {
u32 buffer = 0; // Vertex buffer index for non-fixed attributes
u32 attrCount = 0;
@ -1250,4 +1252,20 @@ void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAccele
);
}
}
}
}
void RendererGL::setupGLES() {
driverInfo.usingGLES = true;
// OpenGL ES hardware is typically way too slow to use the ubershader (eg RPi, mobile phones, handhelds) or has other issues with it.
// So, display a warning and turn them off on OpenGL ES.
if (emulatorConfig->useUbershaders) {
emulatorConfig->useUbershaders = false;
Helpers::warn("Ubershaders enabled on OpenGL ES. This usually results in a worse experience, turning it off...");
}
// Stub out logic operations so that calling them doesn't crash the emulator
if (!glLogicOp) {
glLogicOp = [](GLenum) {};
}
}

View file

@ -1,116 +0,0 @@
#include <algorithm>
#include "colour.hpp"
#include "renderer_mtl/mtl_texture.hpp"
#include "renderer_mtl/renderer_mtl.hpp"
using namespace Helpers;
namespace Metal {
static constexpr u32 signExtend3To32(u32 val) {
return (u32)(s32(val) << 29 >> 29);
}
u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data) {
// Pixel offset of the 8x8 tile based on u, v and the width of the texture
u32 offs = ((u & ~7) * 8) + ((v & ~7) * width);
if (!hasAlpha) {
offs >>= 1;
}
// In-tile offsets for u/v
u &= 7;
v &= 7;
// ETC1(A4) also subdivide the 8x8 tile to 4 4x4 tiles
// Each tile is 8 bytes for ETC1, but since ETC1A4 has 4 alpha bits per pixel, that becomes 16 bytes
const u32 subTileSize = hasAlpha ? 16 : 8;
const u32 subTileIndex = (u / 4) + 2 * (v / 4); // Which of the 4 subtiles is this texel in?
// In-subtile offsets for u/v
u &= 3;
v &= 3;
offs += subTileSize * subTileIndex;
u32 alpha;
const u64* ptr = reinterpret_cast<const u64*>(data.data() + offs); // Cast to u64*
if (hasAlpha) {
// First 64 bits of the 4x4 subtile are alpha data
const u64 alphaData = *ptr++;
alpha = Colour::convert4To8Bit((alphaData >> (4 * (u * 4 + v))) & 0xf);
} else {
alpha = 0xff; // ETC1 without alpha uses ff for every pixel
}
// Next 64 bits of the subtile are colour data
u64 colourData = *ptr;
return decodeETC(alpha, u, v, colourData);
}
u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) {
static constexpr u32 modifiers[8][2] = {
{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
};
// Parse colour data for 4x4 block
const u32 subindices = getBits<0, 16, u32>(colourData);
const u32 negationFlags = getBits<16, 16, u32>(colourData);
const bool flip = getBit<32>(colourData);
const bool diffMode = getBit<33>(colourData);
// Note: index1 is indeed stored on the higher bits, with index2 in the lower bits
const u32 tableIndex1 = getBits<37, 3, u32>(colourData);
const u32 tableIndex2 = getBits<34, 3, u32>(colourData);
const u32 texelIndex = u * 4 + v; // Index of the texel in the block
if (flip) std::swap(u, v);
s32 r, g, b;
if (diffMode) {
r = getBits<59, 5, s32>(colourData);
g = getBits<51, 5, s32>(colourData);
b = getBits<43, 5, s32>(colourData);
if (u >= 2) {
r += signExtend3To32(getBits<56, 3, u32>(colourData));
g += signExtend3To32(getBits<48, 3, u32>(colourData));
b += signExtend3To32(getBits<40, 3, u32>(colourData));
}
// Expand from 5 to 8 bits per channel
r = Colour::convert5To8Bit(r);
g = Colour::convert5To8Bit(g);
b = Colour::convert5To8Bit(b);
} else {
if (u < 2) {
r = getBits<60, 4, s32>(colourData);
g = getBits<52, 4, s32>(colourData);
b = getBits<44, 4, s32>(colourData);
} else {
r = getBits<56, 4, s32>(colourData);
g = getBits<48, 4, s32>(colourData);
b = getBits<40, 4, s32>(colourData);
}
// Expand from 4 to 8 bits per channel
r = Colour::convert4To8Bit(r);
g = Colour::convert4To8Bit(g);
b = Colour::convert4To8Bit(b);
}
const u32 index = (u < 2) ? tableIndex1 : tableIndex2;
s32 modifier = modifiers[index][(subindices >> texelIndex) & 1];
if (((negationFlags >> texelIndex) & 1) != 0) {
modifier = -modifier;
}
r = std::clamp(r + modifier, 0, 255);
g = std::clamp(g + modifier, 0, 255);
b = std::clamp(b + modifier, 0, 255);
return (alpha << 24) | (u32(b) << 16) | (u32(g) << 8) | u32(r);
}
} // namespace Metal

View file

@ -1,16 +1,18 @@
#include "renderer_mtl/mtl_texture.hpp"
#include <fmt/format.h>
#include <array>
#include <memory>
#include "colour.hpp"
#include "renderer_mtl/objc_helper.hpp"
using namespace Helpers;
namespace Metal {
void Texture::allocate() {
formatInfo = PICA::getPixelFormatInfo(format);
formatInfo = PICA::getMTLPixelFormatInfo(format);
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
descriptor->setTextureType(MTL::TextureType2D);
@ -20,11 +22,14 @@ namespace Metal {
descriptor->setUsage(MTL::TextureUsageShaderRead);
descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers?
texture = device->newTexture(descriptor);
texture->setLabel(toNSString(
"Texture " + std::string(PICA::textureFormatToString(format)) + " " + std::to_string(size.u()) + "x" + std::to_string(size.v())
));
texture->setLabel(toNSString(fmt::format("Base texture {} {}x{}", std::string(PICA::textureFormatToString(format)), size.u(), size.v())));
descriptor->release();
if (formatInfo.needsSwizzle) {
base = texture;
texture = base->newTextureView(formatInfo.pixelFormat, MTL::TextureType2D, NS::Range(0, 1), NS::Range(0, 1), formatInfo.swizzle);
}
setNewConfig(config);
}
@ -58,6 +63,11 @@ namespace Metal {
if (texture) {
texture->release();
}
if (base) {
base->release();
}
if (sampler) {
sampler->release();
}
@ -99,210 +109,19 @@ namespace Metal {
}
}
// u and v are the UVs of the relevant texel
// Texture data is stored interleaved in Morton order, ie in a Z - order curve as shown here
// https://en.wikipedia.org/wiki/Z-order_curve
// Textures are split into 8x8 tiles.This function returns the in - tile offset depending on the u & v of the texel
// The in - tile offset is the sum of 2 offsets, one depending on the value of u % 8 and the other on the value of y % 8
// As documented in this picture https ://en.wikipedia.org/wiki/File:Moser%E2%80%93de_Bruijn_addition.svg
u32 Texture::mortonInterleave(u32 u, u32 v) {
static constexpr u32 xOffsets[] = {0, 1, 4, 5, 16, 17, 20, 21};
static constexpr u32 yOffsets[] = {0, 2, 8, 10, 32, 34, 40, 42};
return xOffsets[u & 7] + yOffsets[v & 7];
}
// Get the byte offset of texel (u, v) in the texture
u32 Texture::getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset * bytesPerPixel;
}
// Same as the above code except we need to divide by 2 because 4 bits is smaller than a byte
u32 Texture::getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset / 2;
}
u8 Texture::decodeTexelU8(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data) {
switch (fmt) {
case PICA::TextureFmt::A4: {
const u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 alpha = data[offset] >> ((u % 2) ? 4 : 0);
alpha = Colour::convert4To8Bit(getBits<0, 4>(alpha));
// A8
return alpha;
}
case PICA::TextureFmt::A8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 alpha = data[offset];
// A8
return alpha;
}
default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
}
}
u16 Texture::decodeTexelU16(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data) {
switch (fmt) {
case PICA::TextureFmt::RG8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
constexpr u8 b = 0;
const u8 g = data[offset];
const u8 r = data[offset + 1];
// RG8
return (g << 8) | r;
}
case PICA::TextureFmt::RGBA4: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
u16 texel = u16(data[offset]) | (u16(data[offset + 1]) << 8);
u8 alpha = getBits<0, 4, u8>(texel);
u8 b = getBits<4, 4, u8>(texel);
u8 g = getBits<8, 4, u8>(texel);
u8 r = getBits<12, 4, u8>(texel);
// ABGR4
return (r << 12) | (g << 8) | (b << 4) | alpha;
}
case PICA::TextureFmt::RGBA5551: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(data[offset]) | (u16(data[offset + 1]) << 8);
u8 alpha = getBit<0>(texel) ? 0xff : 0;
u8 b = getBits<1, 5, u8>(texel);
u8 g = getBits<6, 5, u8>(texel);
u8 r = getBits<11, 5, u8>(texel);
// BGR5A1
return (alpha << 15) | (r << 10) | (g << 5) | b;
}
case PICA::TextureFmt::RGB565: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(data[offset]) | (u16(data[offset + 1]) << 8);
const u8 b = getBits<0, 5, u8>(texel);
const u8 g = getBits<5, 6, u8>(texel);
const u8 r = getBits<11, 5, u8>(texel);
// B5G6R5
return (r << 11) | (g << 5) | b;
}
case PICA::TextureFmt::IA4: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 texel = data[offset];
const u8 alpha = texel & 0xf;
const u8 intensity = texel >> 4;
// ABGR4
return (intensity << 12) | (intensity << 8) | (intensity << 4) | alpha;
}
case PICA::TextureFmt::I4: {
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 intensity = data[offset] >> ((u % 2) ? 4 : 0);
intensity = getBits<0, 4>(intensity);
// ABGR4
return (intensity << 12) | (intensity << 8) | (intensity << 4) | 0xff;
}
default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
}
}
u32 Texture::decodeTexelU32(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data) {
switch (fmt) {
case PICA::TextureFmt::RGB8: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 3);
const u8 b = data[offset];
const u8 g = data[offset + 1];
const u8 r = data[offset + 2];
// RGBA8
return (0xff << 24) | (b << 16) | (g << 8) | r;
}
case PICA::TextureFmt::RGBA8: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 4);
const u8 alpha = data[offset];
const u8 b = data[offset + 1];
const u8 g = data[offset + 2];
const u8 r = data[offset + 3];
// RGBA8
return (alpha << 24) | (b << 16) | (g << 8) | r;
}
case PICA::TextureFmt::I8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 intensity = data[offset];
// RGBA8
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case PICA::TextureFmt::IA8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
// Same as I8 except each pixel gets its own alpha value too
const u8 alpha = data[offset];
const u8 intensity = data[offset + 1];
// RGBA8
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
}
}
void Texture::decodeTexture(std::span<const u8> data) {
std::vector<u8> decoded;
decoded.reserve(u64(size.u()) * u64(size.v()) * formatInfo.bytesPerTexel);
std::unique_ptr<u8[]> decodedData(new u8[u64(size.u()) * u64(size.v()) * formatInfo.bytesPerTexel]);
// This pointer will be incremented by our texture decoders
u8* decodePtr = decodedData.get();
// Decode texels line by line
for (u32 v = 0; v < size.v(); v++) {
for (u32 u = 0; u < size.u(); u++) {
if (formatInfo.bytesPerTexel == 1) {
u8 texel = decodeTexelU8(u, v, format, data);
decoded.push_back(texel);
} else if (formatInfo.bytesPerTexel == 2) {
u16 texel = decodeTexelU16(u, v, format, data);
decoded.push_back((texel & 0x00ff) >> 0);
decoded.push_back((texel & 0xff00) >> 8);
} else if (formatInfo.bytesPerTexel == 4) {
u32 texel = decodeTexelU32(u, v, format, data);
decoded.push_back((texel & 0x000000ff) >> 0);
decoded.push_back((texel & 0x0000ff00) >> 8);
decoded.push_back((texel & 0x00ff0000) >> 16);
decoded.push_back((texel & 0xff000000) >> 24);
} else {
Helpers::panic("[Texture::decodeTexture] Unimplemented bytesPerTexel (%u)", formatInfo.bytesPerTexel);
}
formatInfo.decoder(size, u, v, data, decodePtr);
decodePtr += formatInfo.bytesPerTexel;
}
}
texture->replaceRegion(MTL::Region(0, 0, size.u(), size.v()), 0, 0, decoded.data(), formatInfo.bytesPerTexel * size.u(), 0);
texture->replaceRegion(MTL::Region(0, 0, size.u(), size.v()), 0, 0, decodedData.get(), formatInfo.bytesPerTexel * size.u(), 0);
}
} // namespace Metal

View file

@ -0,0 +1,62 @@
#include "renderer_mtl/pica_to_mtl.hpp"
#include "renderer_mtl/texture_decoder.hpp"
using namespace Helpers;
namespace PICA {
MTLPixelFormatInfo mtlPixelFormatInfos[14] = {
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR8ToRGBA8}, // RGBA8
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelBGR8ToRGBA8}, // RGB8
{MTL::PixelFormatBGR5A1Unorm, 2, decodeTexelA1BGR5ToBGR5A1}, // RGBA5551
{MTL::PixelFormatB5G6R5Unorm, 2, decodeTexelB5G6R5ToB5G6R5}, // RGB565
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelABGR4ToABGR4}, // RGBA4
{MTL::PixelFormatRG8Unorm,
2,
decodeTexelAI8ToRG8,
true,
{
.red = MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleRed,
.blue = MTL::TextureSwizzleRed,
.alpha = MTL::TextureSwizzleGreen,
}}, // IA8
{MTL::PixelFormatRG8Unorm, 2, decodeTexelGR8ToRG8}, // RG8
{MTL::PixelFormatR8Unorm,
1,
decodeTexelI8ToR8,
true,
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I8
{MTL::PixelFormatA8Unorm, 1, decodeTexelA8ToA8}, // A8
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelAI4ToABGR4}, // IA4
{MTL::PixelFormatR8Unorm,
1,
decodeTexelI4ToR8,
true,
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I4
{MTL::PixelFormatA8Unorm, 1, decodeTexelA4ToA8}, // A4
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1ToRGBA8}, // ETC1
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1A4ToRGBA8}, // ETC1A4
};
void checkForMTLPixelFormatSupport(MTL::Device* device) {
if (!device->supportsFamily(MTL::GPUFamilyApple1)) {
mtlPixelFormatInfos[2] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelA1BGR5ToRGBA8};
mtlPixelFormatInfos[3] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelB5G6R5ToRGBA8};
mtlPixelFormatInfos[4] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR4ToRGBA8};
mtlPixelFormatInfos[9] = {
MTL::PixelFormatRG8Unorm,
2,
decodeTexelAI4ToRG8,
true,
{
.red = MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleRed,
.blue = MTL::TextureSwizzleRed,
.alpha = MTL::TextureSwizzleGreen,
}
};
}
}
} // namespace PICA

View file

@ -30,7 +30,6 @@ PICA::ColorFmt ToColorFormat(u32 format) {
}
MTL::Library* loadLibrary(MTL::Device* device, const cmrc::file& shaderSource) {
// MTL::CompileOptions* compileOptions = MTL::CompileOptions::alloc()->init();
NS::Error* error = nullptr;
MTL::Library* library = device->newLibrary(Metal::createDispatchData(shaderSource.begin(), shaderSource.size()), &error);
// MTL::Library* library = device->newLibrary(NS::String::string(source.c_str(), NS::ASCIIStringEncoding), compileOptions, &error);
@ -56,12 +55,18 @@ void RendererMTL::reset() {
colorRenderTargetCache.reset();
}
void RendererMTL::setMTKLayer(void* layer) {
metalLayer = (CA::MetalLayer*)layer;
}
void RendererMTL::display() {
CA::MetalDrawable* drawable = metalLayer->nextDrawable();
if (!drawable) {
return;
}
MTL::Texture* texture = drawable->texture();
using namespace PICA::ExternalRegs;
// Top screen
@ -87,13 +92,13 @@ void RendererMTL::display() {
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
colorAttachment->setTexture(drawable->texture());
colorAttachment->setTexture(texture);
colorAttachment->setLoadAction(MTL::LoadActionClear);
colorAttachment->setClearColor(MTL::ClearColor{0.0f, 0.0f, 0.0f, 1.0f});
colorAttachment->setStoreAction(MTL::StoreActionStore);
nextRenderPassName = "Display";
beginRenderPassIfNeeded(renderPassDescriptor, false, drawable->texture());
beginRenderPassIfNeeded(renderPassDescriptor, false, texture);
renderCommandEncoder->setRenderPipelineState(displayPipeline);
renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0);
@ -119,17 +124,22 @@ void RendererMTL::display() {
// Inform the vertex buffer cache that the frame ended
vertexBufferCache.endFrame();
// Release
drawable->release();
}
void RendererMTL::initGraphicsContext(SDL_Window* window) {
// On iOS, the SwiftUI side handles the MetalLayer
#ifdef PANDA3DS_IOS
device = MTL::CreateSystemDefaultDevice();
#else
// TODO: what should be the type of the view?
void* view = SDL_Metal_CreateView(window);
metalLayer = (CA::MetalLayer*)SDL_Metal_GetLayer(view);
device = MTL::CreateSystemDefaultDevice();
metalLayer->setDevice(device);
#endif
checkForMTLPixelFormatSupport(device);
commandQueue = device->newCommandQueue();
// Textures
@ -426,7 +436,7 @@ void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
// Find the source surface.
auto srcFramebuffer = getColorRenderTarget(inputAddr, PICA::ColorFmt::RGBA8, copyStride, copyHeight, false);
if (!srcFramebuffer) {
Helpers::warn("RendererMTL::TextureCopy failed to locate src framebuffer!\n");
doSoftwareTextureCopy(inputAddr, outputAddr, copySize, inputWidth, inputGap, outputWidth, outputGap);
return;
}
nextRenderPassName = "Clear before texture copy";

View file

@ -0,0 +1,334 @@
#include "renderer_mtl/texture_decoder.hpp"
#include <array>
#include <string>
#include "colour.hpp"
#include "math_util.hpp"
using namespace Helpers;
// u and v are the UVs of the relevant texel
// Texture data is stored interleaved in Morton order, ie in a Z - order curve as shown here
// https://en.wikipedia.org/wiki/Z-order_curve
// Textures are split into 8x8 tiles.This function returns the in - tile offset depending on the u & v of the texel
// The in - tile offset is the sum of 2 offsets, one depending on the value of u % 8 and the other on the value of y % 8
// As documented in this picture https ://en.wikipedia.org/wiki/File:Moser%E2%80%93de_Bruijn_addition.svg
u32 mortonInterleave(u32 u, u32 v) {
static constexpr u32 xOffsets[] = {0, 1, 4, 5, 16, 17, 20, 21};
static constexpr u32 yOffsets[] = {0, 2, 8, 10, 32, 34, 40, 42};
return xOffsets[u & 7] + yOffsets[v & 7];
}
// Get the byte offset of texel (u, v) in the texture
u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset * bytesPerPixel;
}
// Same as the above code except we need to divide by 2 because 4 bits is smaller than a byte
u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset / 2;
}
void decodeTexelABGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 4);
const u8 alpha = inData[offset];
const u8 b = inData[offset + 1];
const u8 g = inData[offset + 2];
const u8 r = inData[offset + 3];
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
void decodeTexelBGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 3);
const u8 b = inData[offset];
const u8 g = inData[offset + 1];
const u8 r = inData[offset + 2];
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = 0xff;
}
void decodeTexelA1BGR5ToBGR5A1(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = getBit<0>(texel);
u8 b = getBits<1, 5, u8>(texel);
u8 g = getBits<6, 5, u8>(texel);
u8 r = getBits<11, 5, u8>(texel);
u16 outTexel = (alpha << 15) | (r << 10) | (g << 5) | b;
*outData++ = outTexel & 0xff;
*outData++ = (outTexel >> 8) & 0xff;
}
void decodeTexelA1BGR5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = getBit<0>(texel) ? 0xff : 0;
u8 b = Colour::convert5To8Bit(getBits<1, 5, u8>(texel));
u8 g = Colour::convert5To8Bit(getBits<6, 5, u8>(texel));
u8 r = Colour::convert5To8Bit(getBits<11, 5, u8>(texel));
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
void decodeTexelB5G6R5ToB5G6R5(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
*outData++ = texel & 0xff;
*outData++ = (texel >> 8) & 0xff;
}
void decodeTexelB5G6R5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
const u8 b = Colour::convert5To8Bit(getBits<0, 5, u8>(texel));
const u8 g = Colour::convert6To8Bit(getBits<5, 6, u8>(texel));
const u8 r = Colour::convert5To8Bit(getBits<11, 5, u8>(texel));
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = 0xff;
}
void decodeTexelABGR4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = getBits<0, 4, u8>(texel);
u8 b = getBits<4, 4, u8>(texel);
u8 g = getBits<8, 4, u8>(texel);
u8 r = getBits<12, 4, u8>(texel);
*outData++ = (b << 4) | alpha;
*outData++ = (r << 4) | g;
}
void decodeTexelABGR4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = Colour::convert4To8Bit(getBits<0, 4, u8>(texel));
u8 b = Colour::convert4To8Bit(getBits<4, 4, u8>(texel));
u8 g = Colour::convert4To8Bit(getBits<8, 4, u8>(texel));
u8 r = Colour::convert4To8Bit(getBits<12, 4, u8>(texel));
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
void decodeTexelAI8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
// Same as I8 except each pixel gets its own alpha value too
const u8 alpha = inData[offset];
const u8 intensity = inData[offset + 1];
*outData++ = intensity;
*outData++ = alpha;
}
void decodeTexelGR8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
constexpr u8 b = 0;
const u8 g = inData[offset];
const u8 r = inData[offset + 1];
*outData++ = r;
*outData++ = g;
}
void decodeTexelI8ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 intensity = inData[offset];
*outData++ = intensity;
}
void decodeTexelA8ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 alpha = inData[offset];
*outData++ = alpha;
}
void decodeTexelAI4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 texel = inData[offset];
const u8 alpha = texel & 0xf;
const u8 intensity = texel >> 4;
*outData++ = (intensity << 4) | intensity;
*outData++ = (alpha << 4) | intensity;
}
void decodeTexelAI4ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 texel = inData[offset];
const u8 alpha = Colour::convert4To8Bit(texel & 0xf);
const u8 intensity = Colour::convert4To8Bit(texel >> 4);
*outData++ = intensity;
*outData++ = alpha;
}
void decodeTexelI4ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 intensity = inData[offset] >> ((u % 2) ? 4 : 0);
intensity = Colour::convert4To8Bit(getBits<0, 4>(intensity));
*outData++ = intensity;
}
void decodeTexelA4ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 alpha = inData[offset] >> ((u % 2) ? 4 : 0);
alpha = Colour::convert4To8Bit(getBits<0, 4>(alpha));
*outData++ = alpha;
}
static constexpr u32 signExtend3To32(u32 val) { return (u32)(s32(val) << 29 >> 29); }
void decodeETC(u32 u, u32 v, u64 colourData, u32 alpha, u8* outData) {
static constexpr u32 modifiers[8][2] = {
{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
};
// Parse colour data for 4x4 block
const u32 subindices = getBits<0, 16, u32>(colourData);
const u32 negationFlags = getBits<16, 16, u32>(colourData);
const bool flip = getBit<32>(colourData);
const bool diffMode = getBit<33>(colourData);
// Note: index1 is indeed stored on the higher bits, with index2 in the lower bits
const u32 tableIndex1 = getBits<37, 3, u32>(colourData);
const u32 tableIndex2 = getBits<34, 3, u32>(colourData);
const u32 texelIndex = u * 4 + v; // Index of the texel in the block
if (flip) std::swap(u, v);
s32 r, g, b;
if (diffMode) {
r = getBits<59, 5, s32>(colourData);
g = getBits<51, 5, s32>(colourData);
b = getBits<43, 5, s32>(colourData);
if (u >= 2) {
r += signExtend3To32(getBits<56, 3, u32>(colourData));
g += signExtend3To32(getBits<48, 3, u32>(colourData));
b += signExtend3To32(getBits<40, 3, u32>(colourData));
}
// Expand from 5 to 8 bits per channel
r = Colour::convert5To8Bit(r);
g = Colour::convert5To8Bit(g);
b = Colour::convert5To8Bit(b);
} else {
if (u < 2) {
r = getBits<60, 4, s32>(colourData);
g = getBits<52, 4, s32>(colourData);
b = getBits<44, 4, s32>(colourData);
} else {
r = getBits<56, 4, s32>(colourData);
g = getBits<48, 4, s32>(colourData);
b = getBits<40, 4, s32>(colourData);
}
// Expand from 4 to 8 bits per channel
r = Colour::convert4To8Bit(r);
g = Colour::convert4To8Bit(g);
b = Colour::convert4To8Bit(b);
}
const u32 index = (u < 2) ? tableIndex1 : tableIndex2;
s32 modifier = modifiers[index][(subindices >> texelIndex) & 1];
if (((negationFlags >> texelIndex) & 1) != 0) {
modifier = -modifier;
}
r = std::clamp(r + modifier, 0, 255);
g = std::clamp(g + modifier, 0, 255);
b = std::clamp(b + modifier, 0, 255);
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
template <bool hasAlpha>
void getTexelETC(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
// Pixel offset of the 8x8 tile based on u, v and the width of the texture
u32 offs = ((u & ~7) * 8) + ((v & ~7) * size.u());
if (!hasAlpha) {
offs >>= 1;
}
// In-tile offsets for u/v
u &= 7;
v &= 7;
// ETC1(A4) also subdivide the 8x8 tile to 4 4x4 tiles
// Each tile is 8 bytes for ETC1, but since ETC1A4 has 4 alpha bits per pixel, that becomes 16 bytes
const u32 subTileSize = hasAlpha ? 16 : 8;
const u32 subTileIndex = (u / 4) + 2 * (v / 4); // Which of the 4 subtiles is this texel in?
// In-subtile offsets for u/v
u &= 3;
v &= 3;
offs += subTileSize * subTileIndex;
u32 alpha;
const u64* ptr = reinterpret_cast<const u64*>(inData.data() + offs); // Cast to u64*
if (hasAlpha) {
// First 64 bits of the 4x4 subtile are alpha data
const u64 alphaData = *ptr++;
alpha = Colour::convert4To8Bit((alphaData >> (4 * (u * 4 + v))) & 0xf);
} else {
alpha = 0xff; // ETC1 without alpha uses ff for every pixel
}
// Next 64 bits of the subtile are colour data
u64 colourData = *ptr;
decodeETC(u, v, colourData, alpha, outData);
}
void decodeTexelETC1ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
getTexelETC<false>(size, u, v, inData, outData);
}
void decodeTexelETC1A4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
getTexelETC<true>(size, u, v, inData, outData);
}

View file

@ -885,10 +885,17 @@ void RendererVK::display() {
}
}
// DynamicLoader is in a different namespace in different versions of Vulkan-Hpp
#if VK_HEADER_VERSION >= 301
using VulkanDynamicLoader = vk::detail::DynamicLoader;
#else
using VulkanDynamicLoader = vk::DynamicLoader;
#endif
void RendererVK::initGraphicsContext(SDL_Window* window) {
targetWindow = window;
// Resolve all instance function pointers
static vk::DynamicLoader dl;
static VulkanDynamicLoader dl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
// Create Instance
@ -1588,4 +1595,4 @@ void RendererVK::deinitGraphicsContext() {
// TODO: Make it so that depth and colour buffers get written back to 3DS memory
printf("RendererVK::DeinitGraphicsContext called\n");
}
}

View file

@ -391,7 +391,7 @@ void APTService::setScreencapPostPermission(u32 messagePointer) {
void APTService::getSharedFont(u32 messagePointer) {
log("APT::GetSharedFont\n");
constexpr u32 fontVaddr = 0x18000000;
const u32 fontVaddr = kernel.getSharedFontVaddr();
mem.write32(messagePointer, IPC::responseHeader(0x44, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, fontVaddr);

View file

@ -18,6 +18,7 @@ namespace CFGCommands {
GetSystemModel = 0x00050000,
TranslateCountryInfo = 0x00080080,
GetCountryCodeString = 0x00090040,
GetCountryCodeID = 0x000A0040,
IsFangateSupported = 0x000B0000,
SetConfigInfoBlk4 = 0x04020082,
@ -50,6 +51,7 @@ void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) {
if (type != Type::NOR) {
switch (command) {
case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break;
case CFGCommands::GetCountryCodeString: getCountryCodeString(messagePointer); break;
case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break;
case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break;
case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break;
@ -315,6 +317,24 @@ void CFGService::getCountryCodeID(u32 messagePointer) {
}
}
void CFGService::getCountryCodeString(u32 messagePointer) {
const u16 id = mem.read16(messagePointer + 4);
log("CFG::getCountryCodeString (id = %04X)\n", id);
mem.write32(messagePointer, IPC::responseHeader(0x09, 2, 0));
for (auto [string, code] : countryCodeToTableIDMap) {
if (code == id) {
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, u32(string));
return;
}
}
// Code is not a valid country code, return an appropriate error
mem.write32(messagePointer + 4, 0xD90103FA);
}
void CFGService::secureInfoGetByte101(u32 messagePointer) {
log("CFG::SecureInfoGetByte101\n");

View file

@ -19,7 +19,11 @@ void HTTPService::handleSyncRequest(u32 messagePointer) {
case HTTPCommands::CreateRootCertChain: createRootCertChain(messagePointer); break;
case HTTPCommands::Initialize: initialize(messagePointer); break;
case HTTPCommands::RootCertChainAddDefaultCert: rootCertChainAddDefaultCert(messagePointer); break;
default: Helpers::panic("HTTP service requested. Command: %08X\n", command);
default:
Helpers::warn("HTTP service requested. Command: %08X\n", command);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
break;
}
}

View file

@ -22,6 +22,7 @@ namespace CROHeader {
NameOffset = 0x084,
NextCRO = 0x088,
PrevCRO = 0x08C,
FixedSize = 0x98,
OnUnresolved = 0x0AC,
CodeOffset = 0x0B0,
DataOffset = 0x0B8,
@ -167,6 +168,10 @@ public:
return mem.read32(croPointer + CROHeader::PrevCRO);
}
u32 getFixedSize() {
return mem.read32(croPointer + CROHeader::FixedSize);
}
void setNextCRO(u32 nextCRO) {
mem.write32(croPointer + CROHeader::NextCRO, nextCRO);
}
@ -1248,8 +1253,7 @@ void LDRService::initialize(u32 messagePointer) {
Helpers::panic("Failed to rebase CRS");
}
kernel.clearInstructionCache();
kernel.clearInstructionCacheRange(mapVaddr, size);
loadedCRS = mapVaddr;
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
@ -1278,8 +1282,6 @@ void LDRService::linkCRO(u32 messagePointer) {
Helpers::panic("Failed to link CRO");
}
kernel.clearInstructionCache();
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -1346,8 +1348,7 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
// TODO: add fixing
cro.fix(fixLevel);
kernel.clearInstructionCache();
kernel.clearInstructionCacheRange(mapVaddr, size);
if (isNew) {
mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0));
@ -1377,7 +1378,6 @@ void LDRService::unloadCRO(u32 messagePointer) {
}
CRO cro(mem, mapVaddr, true);
cro.unregisterCRO(loadedCRS);
if (!cro.unlink(loadedCRS)) {
@ -1388,8 +1388,7 @@ void LDRService::unloadCRO(u32 messagePointer) {
Helpers::panic("Failed to unrebase CRO");
}
kernel.clearInstructionCache();
kernel.clearInstructionCacheRange(mapVaddr, cro.getFixedSize());
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -105,13 +105,18 @@ std::filesystem::path Emulator::getConfigPath() {
if constexpr (Helpers::isAndroid()) {
return getAndroidAppPath() / "config.toml";
} else {
return std::filesystem::current_path() / "config.toml";
std::filesystem::path localPath = std::filesystem::current_path() / "config.toml";
if (std::filesystem::exists(localPath)) {
return localPath;
} else {
return getAppDataRoot() / "config.toml";
}
}
}
#endif
void Emulator::step() {}
void Emulator::render() {}
// Only resume if a ROM is properly loaded
void Emulator::resume() {
@ -468,4 +473,4 @@ void Emulator::reloadSettings() {
}
}
#endif
}
}

View file

@ -11,7 +11,7 @@ FrontendSettings::Theme FrontendSettings::themeFromString(std::string inString)
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
static const std::unordered_map<std::string, Theme> map = {
{"system", Theme::System}, {"light", Theme::Light}, {"dark", Theme::Dark}, {"greetingscat", Theme::GreetingsCat}, {"cream", Theme::Cream},
{"system", Theme::System}, {"light", Theme::Light}, {"dark", Theme::Dark}, {"greetingscat", Theme::GreetingsCat}, {"cream", Theme::Cream}, {"oled", Theme::Oled}
};
if (auto search = map.find(inString); search != map.end()) {
@ -28,6 +28,7 @@ const char* FrontendSettings::themeToString(Theme theme) {
case Theme::Light: return "light";
case Theme::GreetingsCat: return "greetingscat";
case Theme::Cream: return "cream";
case Theme::Oled: return "oled";
case Theme::Dark:
default: return "dark";

View file

@ -1,4 +1,6 @@
#include <metal_stdlib>
#include <TargetConditionals.h>
using namespace metal;
struct BasicVertexOut {
@ -219,12 +221,6 @@ struct Globals {
uint GPUREG_LIGHTING_LUTINPUT_SELECT;
uint GPUREG_LIGHTi_CONFIG;
// HACK
//bool lightingEnabled;
//uint8_t lightingNumLights;
//uint32_t lightingConfig1;
//uint16_t alphaControl;
float3 normal;
};
@ -655,14 +651,15 @@ float4 performLogicOp(LogicOp logicOp, float4 s, float4 d) {
return as_type<float4>(performLogicOpU(logicOp, as_type<uint4>(s), as_type<uint4>(d)));
}
fragment float4 fragmentDraw(DrawVertexOut in [[stage_in]], float4 prevColor [[color(0)]], constant PicaRegs& picaRegs [[buffer(0)]], constant FragTEV& tev [[buffer(1)]], constant LogicOp& logicOp [[buffer(2)]], constant uint2& lutSlices [[buffer(3)]], texture2d<float> tex0 [[texture(0)]], texture2d<float> tex1 [[texture(1)]], texture2d<float> tex2 [[texture(2)]], texture2d_array<float> texLightingLut [[texture(3)]], texture1d_array<float> texFogLut [[texture(4)]], sampler samplr0 [[sampler(0)]], sampler samplr1 [[sampler(1)]], sampler samplr2 [[sampler(2)]], sampler linearSampler [[sampler(3)]]) {
Globals globals;
// iOS simulator doesn't support fb fetch, so don't enable it
#ifndef TARGET_OS_SIMULATOR
#define PREVIOUS_COLOR_DECL float4 prevColor [[color(0)]],
#else
#define PREVIOUS_COLOR_DECL
#endif
// HACK
//globals.lightingEnabled = picaRegs.read(0x008Fu) != 0u;
//globals.lightingNumLights = picaRegs.read(0x01C2u);
//globals.lightingConfig1 = picaRegs.read(0x01C4u);
//globals.alphaControl = picaRegs.read(0x104);
fragment float4 fragmentDraw(DrawVertexOut in [[stage_in]], PREVIOUS_COLOR_DECL constant PicaRegs& picaRegs [[buffer(0)]], constant FragTEV& tev [[buffer(1)]], constant LogicOp& logicOp [[buffer(2)]], constant uint2& lutSlices [[buffer(3)]], texture2d<float> tex0 [[texture(0)]], texture2d<float> tex1 [[texture(1)]], texture2d<float> tex2 [[texture(2)]], texture2d_array<float> texLightingLut [[texture(3)]], texture1d_array<float> texFogLut [[texture(4)]], sampler samplr0 [[sampler(0)]], sampler samplr1 [[sampler(1)]], sampler samplr2 [[sampler(2)]], sampler linearSampler [[sampler(3)]]) {
Globals globals;
globals.tevSources[0] = in.color;
if (lightingEnabled) {
@ -755,5 +752,9 @@ fragment float4 fragmentDraw(DrawVertexOut in [[stage_in]], float4 prevColor [[c
}
}
#ifndef TARGET_OS_SIMULATOR
return performLogicOp(logicOp, color, prevColor);
}
#else
return performLogicOp(logicOp, color, float4(0.0));
#endif
}

View file

@ -0,0 +1,10 @@
#version 310 es
precision mediump float;
in vec2 UV;
out vec4 FragColor;
uniform sampler2D u_texture;
void main() {
FragColor = texture(u_texture, UV);
}

View file

@ -0,0 +1,25 @@
#version 310 es
precision mediump float;
out vec2 UV;
void main() {
const vec4 positions[4] = vec4[](
vec4(-1.0, 1.0, 1.0, 1.0), // Top-left
vec4(1.0, 1.0, 1.0, 1.0), // Top-right
vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left
vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right
);
// The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise
// So we adjust our texcoords accordingly
const vec2 texcoords[4] = vec2[](
vec2(1.0, 1.0), // Top-right
vec2(1.0, 0.0), // Bottom-right
vec2(0.0, 1.0), // Top-left
vec2(0.0, 0.0) // Bottom-left
);
gl_Position = positions[gl_VertexID];
UV = texcoords[gl_VertexID];
}

View file

@ -118,6 +118,7 @@ void HydraCore::resetContext() {
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}
emulator->getRenderer()->setupGLES();
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL init failed");

38
src/ios_driver.mm Normal file
View file

@ -0,0 +1,38 @@
#import <Foundation/Foundation.h>
extern "C" {
#include "ios_driver.h"
}
// Apple's Foundation headers define some macros globablly that create issues with our own code, so remove the definitions
#undef ABS
#undef NO
#include <memory>
#include "emulator.hpp"
// The Objective-C++ bridge functions must be exported without name mangling in order for the SwiftUI frontend to be able to call them
#define IOS_EXPORT extern "C" __attribute__((visibility("default")))
std::unique_ptr<Emulator> emulator = nullptr;
HIDService* hidService = nullptr;
IOS_EXPORT void iosCreateEmulator() {
printf("Creating emulator\n");
emulator = std::make_unique<Emulator>();
hidService = &emulator->getServiceManager().getHID();
emulator->initGraphicsContext(nullptr);
}
IOS_EXPORT void iosRunFrame(CAMetalLayer* layer) {
void* layerBridged = (__bridge void*)layer;
emulator->getRenderer()->setMTKLayer(layerBridged);
emulator->runFrame();
}
IOS_EXPORT void iosLoadROM(NSString* pathNS) {
auto path = std::filesystem::path([pathNS UTF8String]);
emulator->loadROM(path);
}

View file

@ -78,6 +78,7 @@ AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) {
}
__android_log_print(ANDROID_LOG_INFO, "AlberDriver", "OpenGL ES %d.%d", GLVersion.major, GLVersion.minor);
emulator->getRenderer()->setupGLES();
emulator->initGraphicsContext(nullptr);
}
@ -153,7 +154,6 @@ int AndroidUtils::openDocument(const char* path, const char* perms) {
jstring uri = env->NewStringUTF(path);
jstring jmode = env->NewStringUTF(perms);
jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode);
env->DeleteLocalRef(uri);

View file

@ -17,7 +17,8 @@ static retro_input_state_t inputStateCallback;
static retro_hw_render_callback hwRender;
static std::filesystem::path savePath;
static bool screenTouched;
static bool screenTouched = false;
static bool usingGLES = false;
std::unique_ptr<Emulator> emulator;
RendererGL* renderer;
@ -35,15 +36,19 @@ static void* getGLProcAddress(const char* name) {
}
static void videoResetContext() {
#ifdef USING_GLES
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL ES init failed");
if (usingGLES) {
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}
emulator->getRenderer()->setupGLES();
}
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL init failed");
else {
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL init failed");
}
}
#endif
emulator->initGraphicsContext(nullptr);
}
@ -73,6 +78,7 @@ static bool setHWRender(retro_hw_context_type type) {
hwRender.version_minor = 1;
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
usingGLES = true;
return true;
}
break;
@ -383,6 +389,8 @@ void retro_run() {
emulator->runFrame();
videoCallback(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0);
// Call audio batch callback
emulator->getAudioDevice().renderBatch(audioBatchCallback);
}
void retro_set_controller_port_device(uint port, uint device) {}

View file

@ -4,4 +4,6 @@
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#ifndef PANDA3DS_IOS
#include "miniaudio.h"
#endif

View file

@ -0,0 +1,8 @@
// We do not need the ability to be able to encode or decode audio files for the time being
// So we disable said functionality to make the executable smaller.
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
// On iOS we have to compile miniaudio as Obj-C
#include "miniaudio.h"

View file

@ -71,6 +71,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
themeSelect->addItem(tr("Dark"));
themeSelect->addItem(tr("Greetings Cat"));
themeSelect->addItem(tr("Cream"));
themeSelect->addItem(tr("OLED"));
themeSelect->setCurrentIndex(static_cast<int>(config.frontendSettings.theme));
connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) {
config.frontendSettings.theme = static_cast<Theme>(index);
@ -437,6 +438,34 @@ void ConfigWindow::setTheme(Theme theme) {
break;
}
case Theme::Oled: {
QApplication::setStyle(QStyleFactory::create("Fusion"));
QPalette p;
p.setColor(QPalette::Window, Qt::black);
p.setColor(QPalette::WindowText, Qt::white);
p.setColor(QPalette::Base, Qt::black);
p.setColor(QPalette::AlternateBase, Qt::black);
p.setColor(QPalette::ToolTipBase, Qt::black);
p.setColor(QPalette::ToolTipText, Qt::white);
p.setColor(QPalette::Text, Qt::white);
p.setColor(QPalette::Button, QColor(5, 5, 5));
p.setColor(QPalette::ButtonText, Qt::white);
p.setColor(QPalette::BrightText, Qt::red);
p.setColor(QPalette::Link, QColor(42, 130, 218));
p.setColor(QPalette::Highlight, QColor(42, 130, 218));
p.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(p);
qApp->setStyleSheet("QLineEdit {"
"background-color: #000000; color: #ffffff; border: 1px solid #a0a0a0; "
"border-radius: 4px; padding: 5px; }"
"QCheckBox::indicator:unchecked {"
"border: 1px solid #808080; border-radius: 4px; }");
break;
}
case Theme::System: {
qApp->setPalette(this->style()->standardPalette());
qApp->setStyle(QStyleFactory::create("WindowsVista"));

View file

@ -7,6 +7,5 @@ int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow window(&app);
window.show();
return app.exec();
}

View file

@ -22,14 +22,13 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
// Enable drop events for loading ROMs
setAcceptDrops(true);
resize(800, 240 * 4);
show();
// We pass a callback to the screen widget that will be triggered every time we resize the screen
screen = new ScreenWidget([this](u32 width, u32 height) { handleScreenResize(width, height); }, this);
setCentralWidget(screen);
screen->show();
appRunning = true;
// Set our menu bar up
menuBar = new QMenuBar(nullptr);
@ -140,6 +139,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
glContext->MakeCurrent();
glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);
if (glContext->IsGLES()) {
emu->getRenderer()->setupGLES();
}
emu->initGraphicsContext(glContext);
} else if (usingVk) {
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
@ -695,4 +698,4 @@ void MainWindow::setupControllerSensors(SDL_GameController* controller) {
if (haveAccelerometer) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
}
}
}

View file

@ -29,6 +29,7 @@ ScreenWidget::ScreenWidget(ResizeCallback resizeCallback, QWidget* parent) : QWi
setAttribute(Qt::WA_KeyCompression, false);
setFocusPolicy(Qt::StrongFocus);
setMouseTracking(true);
show();
if (!createGLContext()) {
Helpers::panic("Failed to create GL context for display");
@ -60,11 +61,12 @@ void ScreenWidget::resizeSurface(u32 width, u32 height) {
}
bool ScreenWidget::createGLContext() {
// List of GL context versions we will try. Anything 4.1+ is good
static constexpr std::array<GL::Context::Version, 6> versionsToTry = {
// List of GL context versions we will try. Anything 4.1+ is good for desktop OpenGL, and 3.1+ for OpenGL ES
static constexpr std::array<GL::Context::Version, 8> versionsToTry = {
GL::Context::Version{GL::Context::Profile::Core, 4, 6}, GL::Context::Version{GL::Context::Profile::Core, 4, 5},
GL::Context::Version{GL::Context::Profile::Core, 4, 4}, GL::Context::Version{GL::Context::Profile::Core, 4, 3},
GL::Context::Version{GL::Context::Profile::Core, 4, 2}, GL::Context::Version{GL::Context::Profile::Core, 4, 1},
GL::Context::Version{GL::Context::Profile::ES, 3, 2}, GL::Context::Version{GL::Context::Profile::ES, 3, 1},
};
std::optional<WindowInfo> windowInfo = getWindowInfo();
@ -72,6 +74,10 @@ bool ScreenWidget::createGLContext() {
this->windowInfo = *windowInfo;
glContext = GL::Context::Create(*getWindowInfo(), versionsToTry);
if (glContext == nullptr) {
return false;
}
glContext->DoneCurrent();
}
@ -79,7 +85,7 @@ bool ScreenWidget::createGLContext() {
}
qreal ScreenWidget::devicePixelRatioFromScreen() const {
const QScreen* screenForRatio = window()->windowHandle()->screen();
const QScreen* screenForRatio = windowHandle()->screen();
if (!screenForRatio) {
screenForRatio = QGuiApplication::primaryScreen();
}

View file

@ -46,12 +46,13 @@ struct LanguageInfo {
// Please keep this list mostly in alphabetical order.
// Also, for Unicode characters in language names, use Unicode keycodes instead of writing out the name,
// as some compilers/toolchains may not enjoy Unicode in source files.
static std::array<LanguageInfo, 5> languages = {
static std::array<LanguageInfo, 6> languages = {
LanguageInfo(QStringLiteral(u"English"), "en"), // English
LanguageInfo(QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"), // Greek
LanguageInfo(QStringLiteral(u"Espa\u00F1ol"), "es"), // Spanish
LanguageInfo(QStringLiteral(u"Nederlands"), "nl"), // Dutch
LanguageInfo(QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_br") // Portuguese (Brazilian)
LanguageInfo(QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_br"), // Portuguese (Brazilian)
LanguageInfo(QStringLiteral(u"Svenska"), "sv"), // Swedish
};
QComboBox* ConfigWindow::createLanguageSelect() {

View file

@ -71,11 +71,27 @@ FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMapp
glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}
Helpers::warn("OpenGL context creation failed: %s\nTrying again with OpenGL ES.", SDL_GetError());
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed");
// Some low end devices (eg RPi, emulation handhelds) don't support desktop GL, but only OpenGL ES, so fall back to that if GL context
// creation failed
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed");
}
emu.getRenderer()->setupGLES();
} else {
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed");
}
}
SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0);

2
src/pandios/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
libAlber.dylib
Alber/Headers/ios_driver.h

View file

@ -0,0 +1,7 @@
#pragma once
#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
void iosCreateEmulator();
void iosLoadROM(NSString* pathNS);
void iosRunFrame(CAMetalLayer* layer);

View file

@ -0,0 +1,5 @@
module AlberDriver {
umbrella "Headers" // for multiple files
link "libAlber"
export *
}

View file

@ -0,0 +1,587 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 70;
objects = {
/* Begin PBXBuildFile section */
4F798C782D8747B000F5D23F /* libAlber.dylib in Copy Files */ = {isa = PBXBuildFile; fileRef = 4F798C772D8747B000F5D23F /* libAlber.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
4F798C7A2D8747F400F5D23F /* libAlber.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F798C792D8747B800F5D23F /* libAlber.dylib */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
4F6E8FD32D77C0140025DD0D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4F6E8FBA2D77C0120025DD0D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4F6E8FC12D77C0120025DD0D;
remoteInfo = Pandios;
};
4F6E8FDD2D77C0140025DD0D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4F6E8FBA2D77C0120025DD0D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4F6E8FC12D77C0120025DD0D;
remoteInfo = Pandios;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
4F9EEBE82D78898B00E0B72D /* Copy Files */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
4F798C782D8747B000F5D23F /* libAlber.dylib in Copy Files */,
);
name = "Copy Files";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
4F6E8FC22D77C0120025DD0D /* Pandios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pandios.app; sourceTree = BUILT_PRODUCTS_DIR; };
4F6E8FD22D77C0140025DD0D /* PandiosTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PandiosTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4F6E8FDC2D77C0140025DD0D /* PandiosUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PandiosUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4F798C772D8747B000F5D23F /* libAlber.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libAlber.dylib; path = ../../build/libAlber.dylib; sourceTree = "<group>"; };
4F798C792D8747B800F5D23F /* libAlber.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libAlber.dylib; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
4F6E8FC42D77C0120025DD0D /* Pandios */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Pandios; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
4F6E8FCF2D77C0140025DD0D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4F6E8FD92D77C0140025DD0D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4F9EEBF82D78963D00E0B72D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4F798C7A2D8747F400F5D23F /* libAlber.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4F6E8FB92D77C0120025DD0D = {
isa = PBXGroup;
children = (
4F798C772D8747B000F5D23F /* libAlber.dylib */,
4F6E8FC42D77C0120025DD0D /* Pandios */,
4F9EEBF62D7895D700E0B72D /* Frameworks */,
4F6E8FC32D77C0120025DD0D /* Products */,
);
sourceTree = "<group>";
};
4F6E8FC32D77C0120025DD0D /* Products */ = {
isa = PBXGroup;
children = (
4F6E8FC22D77C0120025DD0D /* Pandios.app */,
4F6E8FD22D77C0140025DD0D /* PandiosTests.xctest */,
4F6E8FDC2D77C0140025DD0D /* PandiosUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
4F9EEBF62D7895D700E0B72D /* Frameworks */ = {
isa = PBXGroup;
children = (
4F798C792D8747B800F5D23F /* libAlber.dylib */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
4F6E8FC12D77C0120025DD0D /* Pandios */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4F6E8FE62D77C0140025DD0D /* Build configuration list for PBXNativeTarget "Pandios" */;
buildPhases = (
4F6E8FBE2D77C0120025DD0D /* Sources */,
4F6E8FC02D77C0120025DD0D /* Resources */,
4F9EEBE82D78898B00E0B72D /* Copy Files */,
4F9EEBF82D78963D00E0B72D /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
4F6E8FC42D77C0120025DD0D /* Pandios */,
);
name = Pandios;
packageProductDependencies = (
);
productName = Pandios;
productReference = 4F6E8FC22D77C0120025DD0D /* Pandios.app */;
productType = "com.apple.product-type.application";
};
4F6E8FD12D77C0140025DD0D /* PandiosTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4F6E8FE92D77C0140025DD0D /* Build configuration list for PBXNativeTarget "PandiosTests" */;
buildPhases = (
4F6E8FCE2D77C0140025DD0D /* Sources */,
4F6E8FCF2D77C0140025DD0D /* Frameworks */,
4F6E8FD02D77C0140025DD0D /* Resources */,
);
buildRules = (
);
dependencies = (
4F6E8FD42D77C0140025DD0D /* PBXTargetDependency */,
);
name = PandiosTests;
packageProductDependencies = (
);
productName = PandiosTests;
productReference = 4F6E8FD22D77C0140025DD0D /* PandiosTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
4F6E8FDB2D77C0140025DD0D /* PandiosUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4F6E8FEC2D77C0140025DD0D /* Build configuration list for PBXNativeTarget "PandiosUITests" */;
buildPhases = (
4F6E8FD82D77C0140025DD0D /* Sources */,
4F6E8FD92D77C0140025DD0D /* Frameworks */,
4F6E8FDA2D77C0140025DD0D /* Resources */,
);
buildRules = (
);
dependencies = (
4F6E8FDE2D77C0140025DD0D /* PBXTargetDependency */,
);
name = PandiosUITests;
packageProductDependencies = (
);
productName = PandiosUITests;
productReference = 4F6E8FDC2D77C0140025DD0D /* PandiosUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
4F6E8FBA2D77C0120025DD0D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 1620;
TargetAttributes = {
4F6E8FC12D77C0120025DD0D = {
CreatedOnToolsVersion = 16.2;
};
4F6E8FD12D77C0140025DD0D = {
CreatedOnToolsVersion = 16.2;
TestTargetID = 4F6E8FC12D77C0120025DD0D;
};
4F6E8FDB2D77C0140025DD0D = {
CreatedOnToolsVersion = 16.2;
TestTargetID = 4F6E8FC12D77C0120025DD0D;
};
};
};
buildConfigurationList = 4F6E8FBD2D77C0120025DD0D /* Build configuration list for PBXProject "Pandios" */;
compatibilityVersion = "Xcode 16.0.Superseded.1";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 4F6E8FB92D77C0120025DD0D;
minimizedProjectReferenceProxies = 1;
productRefGroup = 4F6E8FC32D77C0120025DD0D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
4F6E8FC12D77C0120025DD0D /* Pandios */,
4F6E8FD12D77C0140025DD0D /* PandiosTests */,
4F6E8FDB2D77C0140025DD0D /* PandiosUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
4F6E8FC02D77C0120025DD0D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4F6E8FD02D77C0140025DD0D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4F6E8FDA2D77C0140025DD0D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
4F6E8FBE2D77C0120025DD0D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4F6E8FCE2D77C0140025DD0D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4F6E8FD82D77C0140025DD0D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
4F6E8FD42D77C0140025DD0D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4F6E8FC12D77C0120025DD0D /* Pandios */;
targetProxy = 4F6E8FD32D77C0140025DD0D /* PBXContainerItemProxy */;
};
4F6E8FDE2D77C0140025DD0D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4F6E8FC12D77C0120025DD0D /* Pandios */;
targetProxy = 4F6E8FDD2D77C0140025DD0D /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
4F6E8FE42D77C0140025DD0D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
4F6E8FE52D77C0140025DD0D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
4F6E8FE72D77C0140025DD0D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Pandios/Preview Content\"";
DEVELOPMENT_TEAM = 877A43U8RR;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Alber.Pandios;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/Alber";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
4F6E8FE82D77C0140025DD0D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Pandios/Preview Content\"";
DEVELOPMENT_TEAM = 877A43U8RR;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Alber.Pandios;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/Alber";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
4F6E8FEA2D77C0140025DD0D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Alber.PandiosTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pandios.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Pandios";
};
name = Debug;
};
4F6E8FEB2D77C0140025DD0D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Alber.PandiosTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pandios.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Pandios";
};
name = Release;
};
4F6E8FED2D77C0140025DD0D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Alber.PandiosUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Pandios;
};
name = Debug;
};
4F6E8FEE2D77C0140025DD0D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Alber.PandiosUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Pandios;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
4F6E8FBD2D77C0120025DD0D /* Build configuration list for PBXProject "Pandios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4F6E8FE42D77C0140025DD0D /* Debug */,
4F6E8FE52D77C0140025DD0D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4F6E8FE62D77C0140025DD0D /* Build configuration list for PBXNativeTarget "Pandios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4F6E8FE72D77C0140025DD0D /* Debug */,
4F6E8FE82D77C0140025DD0D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4F6E8FE92D77C0140025DD0D /* Build configuration list for PBXNativeTarget "PandiosTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4F6E8FEA2D77C0140025DD0D /* Debug */,
4F6E8FEB2D77C0140025DD0D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4F6E8FEC2D77C0140025DD0D /* Build configuration list for PBXNativeTarget "PandiosUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4F6E8FED2D77C0140025DD0D /* Debug */,
4F6E8FEE2D77C0140025DD0D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 4F6E8FBA2D77C0120025DD0D /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Pandios.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,95 @@
import AlberDriver
import SwiftUI
import MetalKit
import Darwin
var emulatorLock = NSLock()
class DocumentViewController: UIViewController, DocumentDelegate {
var documentPicker: DocumentPicker!
override func viewDidLoad() {
super.viewDidLoad()
/// set up the document picker
documentPicker = DocumentPicker(presentationController: self, delegate: self)
/// When the view loads (ie user opens the app) show the file picker
show()
}
/// callback from the document picker
func didPickDocument(document: Document?) {
if let pickedDoc = document {
let fileURL = pickedDoc.fileURL
print("Loading ROM", fileURL)
emulatorLock.lock()
iosLoadROM(fileURL.path(percentEncoded: false))
emulatorLock.unlock()
}
}
func show() {
documentPicker.displayPicker()
}
}
struct DocumentView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> DocumentViewController {
return DocumentViewController()
}
func updateUIViewController(_ uiViewController: DocumentViewController, context: Context) {
// No update needed
}
}
struct ContentView: UIViewRepresentable {
@State var showFileImporter = true
/*
func makeCoordinator() -> Renderer {
Renderer(self)
}
*/
func makeUIView(context: UIViewRepresentableContext<ContentView>) -> MTKView {
let mtkView = MTKView()
mtkView.preferredFramesPerSecond = 60
mtkView.enableSetNeedsDisplay = true
mtkView.isPaused = true
if let metalDevice = MTLCreateSystemDefaultDevice() {
mtkView.device = metalDevice
}
mtkView.framebufferOnly = false
mtkView.drawableSize = mtkView.frame.size
let dispatchQueue = DispatchQueue(label: "QueueIdentification", qos: .background)
let metalLayer = mtkView.layer as! CAMetalLayer;
dispatchQueue.async{
iosCreateEmulator()
while (true) {
emulatorLock.lock()
iosRunFrame(metalLayer);
emulatorLock.unlock()
}
}
return mtkView
}
func updateUIView(_ uiView: MTKView, context: UIViewRepresentableContext<ContentView>) {
print("Updating MTKView");
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
DocumentView();
ContentView();
}
}

View file

@ -0,0 +1,75 @@
// From https://gist.github.com/aheze/dbc7f9b452e4f86f2d8fe278b3c5001f
// DocumentPicker.swift
import UIKit
import MobileCoreServices
import UniformTypeIdentifiers
protocol DocumentDelegate: AnyObject {
func didPickDocument(document: Document?)
}
class Document: UIDocument {
var data: Data?
override func contents(forType typeName: String) throws -> Any {
guard let data = data else { return Data() }
return try NSKeyedArchiver.archivedData(withRootObject:data,
requiringSecureCoding: true)
}
override func load(fromContents contents: Any, ofType typeName:
String?) throws {
guard let data = contents as? Data else { return }
self.data = data
}
}
open class DocumentPicker: NSObject {
private var pickerController: UIDocumentPickerViewController?
private weak var presentationController: UIViewController?
private weak var delegate: DocumentDelegate?
private var pickedDocument: Document?
init(presentationController: UIViewController, delegate: DocumentDelegate) {
super.init()
self.presentationController = presentationController
self.delegate = delegate
}
public func displayPicker() {
self.pickerController = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.data])
self.pickerController!.delegate = self
self.presentationController?.present(self.pickerController!, animated: true)
}
}
extension DocumentPicker: UIDocumentPickerDelegate {
/// delegate method, when the user selects a file
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else {
return
}
documentFromURL(pickedURL: url)
delegate?.didPickDocument(document: pickedDocument)
}
/// delegate method, when the user cancels
public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
delegate?.didPickDocument(document: nil)
}
private func documentFromURL(pickedURL: URL) {
/// start accessing the resource
let shouldStopAccessing = pickedURL.startAccessingSecurityScopedResource()
defer {
if shouldStopAccessing {
pickedURL.stopAccessingSecurityScopedResource()
}
}
NSFileCoordinator().coordinate(readingItemAt: pickedURL, error: NSErrorPointer.none) { (readURL) in
let document = Document(fileURL: readURL)
pickedDocument = document
}
}
}

View file

@ -0,0 +1,11 @@
import SwiftUI
@main
struct PandiosApp: App {
var body: some Scene {
WindowGroup {
ContentView()
DocumentView()
}
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

37
src/pandios/build.sh Executable file
View file

@ -0,0 +1,37 @@
#!/bin/bash
# Settings for the SwiftUI frontend
ARCH=arm64
CONFIGURATION=Release
SDK=iphonesimulator
# Settings for the emulator core
EMULATOR_BUILD_TYPE=Release
# Simulator settings
RUN_SIMULATOR=false
# Fail on error
set -e
# Go to the parent directory and build the emulator core
cd ../..
cmake -B build -DENABLE_VULKAN=OFF -DBUILD_HYDRA_CORE=ON -DENABLE_USER_BUILD=ON -DCMAKE_TOOLCHAIN_FILE=third_party/ios_toolchain/ios.toolchain.cmake -DPLATFORM=SIMULATORARM64 -DDEPLOYMENT_TARGET="13.0"
cmake --build build --config ${EMULATOR_BUILD_TYPE}
# Copy the bridging header and emulator dylib to the iOS folder
cp ./include/ios_driver.h ./src/pandios/Alber/Headers/ios_driver.h
cp ./build/libAlber.dylib ./src/pandios/
# Come back to the iOS directory, build the SwiftUI xcode project and link them together
cd src/pandios
xcodebuild build -configuration ${CONFIGURATION} -sdk ${SDK} -arch ${ARCH}
# To run the app in the simulator:
# Boot the iPhone, install the app on the iphone, then open the sim & launch the app
if [ "$RUN_SIMULATOR" = true ] ; then
xcrun simctl boot "iPhone 16 Pro"
xcrun simctl install "iPhone 16 Pro" "build/Release-iphonesimulator/Pandios.app"
open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app
xcrun simctl launch --console booted "Alber.Pandios"
fi

View file

@ -38,6 +38,7 @@ public class AppDataDocumentProvider extends DocumentsProvider {
Document.COLUMN_DISPLAY_NAME,
Document.COLUMN_MIME_TYPE,
Document.COLUMN_LAST_MODIFIED,
Document.COLUMN_FLAGS,
Document.COLUMN_SIZE
};
@ -101,7 +102,7 @@ public class AppDataDocumentProvider extends DocumentsProvider {
}
cursor.newRow()
.add(Document.COLUMN_DOCUMENT_ID, obtainDocumentId(file))
.add(Document.COLUMN_MIME_TYPE, file.isDirectory() ? Document.MIME_TYPE_DIR : "application/octect-stream")
.add(Document.COLUMN_MIME_TYPE, file.isDirectory() ? Document.MIME_TYPE_DIR : "application/octet-stream")
.add(Document.COLUMN_FLAGS, flags)
.add(Document.COLUMN_LAST_MODIFIED, file.lastModified())
.add(Document.COLUMN_DISPLAY_NAME, file.getName())
@ -157,8 +158,13 @@ public class AppDataDocumentProvider extends DocumentsProvider {
}
}
@Override
public void removeDocument(String documentId, String parentDocumentId) throws FileNotFoundException {
deleteDocument(documentId);
}
@Override
public ParcelFileDescriptor openDocument(String documentId, String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {
return ParcelFileDescriptor.open(obtainFile(documentId), ParcelFileDescriptor.parseMode(mode));
}
}
}

View file

@ -3,6 +3,8 @@
#include <algorithm>
#include <unordered_map>
#include "PICA/gpu.hpp"
Renderer::Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
: gpu(gpu), regs(internalRegs), externalRegs(externalRegs) {}
Renderer::~Renderer() {}
@ -39,3 +41,39 @@ const char* Renderer::typeToString(RendererType rendererType) {
default: return "Invalid";
}
}
void Renderer::doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap) {
u8* inputPointer = gpu.getPointerPhys<u8>(inputAddr);
u8* outputPointer = gpu.getPointerPhys<u8>(outputAddr);
if (inputPointer == nullptr || outputPointer == nullptr) {
return;
}
u32 inputBytesLeft = inputWidth;
u32 outputBytesLeft = outputWidth;
u32 copyBytesLeft = copySize;
while (copyBytesLeft > 0) {
const u32 bytes = std::min<u32>({inputBytesLeft, outputBytesLeft, copyBytesLeft});
std::memcpy(outputPointer, inputPointer, bytes);
inputPointer += bytes;
outputPointer += bytes;
inputBytesLeft -= bytes;
outputBytesLeft -= bytes;
copyBytesLeft -= bytes;
// Apply input and output gap when an input or output line ends
if (inputBytesLeft == 0) {
inputBytesLeft = inputWidth;
inputPointer += inputGap;
}
if (outputBytesLeft == 0) {
outputBytesLeft = outputWidth;
outputPointer += outputGap;
}
}
}

2
third_party/SDL2 vendored

@ -1 +1 @@
Subproject commit 379d4780559690a9836444aeb5637f60953947be
Subproject commit f48a96a976b50d1faae70598e71bfaf8f4526347

View file

@ -0,0 +1,945 @@
# This file is part of the ios-cmake project. It was retrieved from
# https://github.com/leetal/ios-cmake.git, which is a fork of
# https://github.com/gerstrong/ios-cmake.git, which is a fork of
# https://github.com/cristeab/ios-cmake.git, which is a fork of
# https://code.google.com/p/ios-cmake/. Which in turn is based off of
# the Platform/Darwin.cmake and Platform/UnixPaths.cmake files which
# are included with CMake 2.8.4
#
# The ios-cmake project is licensed under the new BSD license.
#
# Copyright (c) 2014, Bogdan Cristea and LTE Engineering Software,
# Kitware, Inc., Insight Software Consortium. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# This file is based off of the Platform/Darwin.cmake and
# Platform/UnixPaths.cmake files which are included with CMake 2.8.4
# It has been altered for iOS development.
#
# Updated by Alex Stewart (alexs.mac@gmail.com)
#
# *****************************************************************************
# Now maintained by Alexander Widerberg (widerbergaren [at] gmail.com)
# under the BSD-3-Clause license
# https://github.com/leetal/ios-cmake
# *****************************************************************************
#
# INFORMATION / HELP
#
# The following options control the behaviour of this toolchain:
#
# PLATFORM: (default "OS64")
# OS = Build for iPhoneOS.
# OS64 = Build for arm64 iphoneOS.
# OS64COMBINED = Build for arm64 x86_64 iphoneOS. Combined into FAT STATIC lib (supported on 3.14+ of CMakewith "-G Xcode" argument ONLY)
# SIMULATOR = Build for x86 i386 iphoneOS Simulator.
# SIMULATOR64 = Build for x86_64 iphoneOS Simulator.
# SIMULATORARM64 = Build for arm64 iphoneOS Simulator.
# TVOS = Build for arm64 tvOS.
# TVOSCOMBINED = Build for arm64 x86_64 tvOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY)
# SIMULATOR_TVOS = Build for x86_64 tvOS Simulator.
# WATCHOS = Build for armv7k arm64_32 for watchOS.
# WATCHOSCOMBINED = Build for armv7k arm64_32 x86_64 watchOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY)
# SIMULATOR_WATCHOS = Build for x86_64 for watchOS Simulator.
# MAC = Build for x86_64 macOS.
# MAC_ARM64 = Build for Apple Silicon macOS.
# MAC_CATALYST = Build for x86_64 macOS with Catalyst support (iOS toolchain on macOS).
# Note: The build argument "MACOSX_DEPLOYMENT_TARGET" can be used to control min-version of macOS
# MAC_CATALYST_ARM64 = Build for Apple Silicon macOS with Catalyst support (iOS toolchain on macOS).
# Note: The build argument "MACOSX_DEPLOYMENT_TARGET" can be used to control min-version of macOS
#
# CMAKE_OSX_SYSROOT: Path to the SDK to use. By default this is
# automatically determined from PLATFORM and xcodebuild, but
# can also be manually specified (although this should not be required).
#
# CMAKE_DEVELOPER_ROOT: Path to the Developer directory for the platform
# being compiled for. By default this is automatically determined from
# CMAKE_OSX_SYSROOT, but can also be manually specified (although this should
# not be required).
#
# DEPLOYMENT_TARGET: Minimum SDK version to target. Default 2.0 on watchOS and 9.0 on tvOS+iOS
#
# ENABLE_BITCODE: (1|0) Enables or disables bitcode support. Default 1 (true)
#
# ENABLE_ARC: (1|0) Enables or disables ARC support. Default 1 (true, ARC enabled by default)
#
# ENABLE_VISIBILITY: (1|0) Enables or disables symbol visibility support. Default 0 (false, visibility hidden by default)
#
# ENABLE_STRICT_TRY_COMPILE: (1|0) Enables or disables strict try_compile() on all Check* directives (will run linker
# to actually check if linking is possible). Default 0 (false, will set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY)
#
# ARCHS: (armv7 armv7s armv7k arm64 arm64_32 i386 x86_64) If specified, will override the default architectures for the given PLATFORM
# OS = armv7 armv7s arm64 (if applicable)
# OS64 = arm64 (if applicable)
# SIMULATOR = i386
# SIMULATOR64 = x86_64
# SIMULATORARM64 = arm64
# TVOS = arm64
# SIMULATOR_TVOS = x86_64 (i386 has since long been deprecated)
# WATCHOS = armv7k arm64_32 (if applicable)
# SIMULATOR_WATCHOS = x86_64 (i386 has since long been deprecated)
# MAC = x86_64
# MAC_ARM64 = arm64
# MAC_CATALYST = x86_64
# MAC_CATALYST_ARM64 = arm64
#
# This toolchain defines the following properties (available via get_property()) for use externally:
#
# PLATFORM: The currently targeted platform.
# XCODE_VERSION: Version number (not including Build version) of Xcode detected.
# SDK_VERSION: Version of SDK being used.
# OSX_ARCHITECTURES: Architectures being compiled for (generated from PLATFORM).
# APPLE_TARGET_TRIPLE: Used by autoconf build systems. NOTE: If "ARCHS" are overridden, this will *NOT* be set!
#
# This toolchain defines the following macros for use externally:
#
# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE XCODE_VARIANT)
# A convenience macro for setting xcode specific properties on targets.
# Available variants are: All, Release, RelWithDebInfo, Debug, MinSizeRel
# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1" "all").
#
# find_host_package (PROGRAM ARGS)
# A macro used to find executable programs on the host system, not within the
# environment. Thanks to the android-cmake project for providing the
# command.
#
cmake_minimum_required(VERSION 3.8.0)
# CMake invokes the toolchain file twice during the first build, but only once during subsequent rebuilds.
if(DEFINED ENV{_IOS_TOOLCHAIN_HAS_RUN})
return()
endif()
set(ENV{_IOS_TOOLCHAIN_HAS_RUN} true)
###############################################################################
# OPTIONS #
###############################################################################
option(DROP_32_BIT "Drops the 32-bit targets universally." YES)
###############################################################################
# END OPTIONS #
###############################################################################
# List of supported platform values
list(APPEND _supported_platforms
"OS" "OS64" "OS64COMBINED" "SIMULATOR" "SIMULATOR64" "SIMULATORARM64"
"TVOS" "TVOSCOMBINED" "SIMULATOR_TVOS"
"WATCHOS" "WATCHOSCOMBINED" "SIMULATOR_WATCHOS"
"MAC" "MAC_ARM64"
"MAC_CATALYST" "MAC_CATALYST_ARM64")
# Cache what generator is used
set(USED_CMAKE_GENERATOR "${CMAKE_GENERATOR}")
# Check if using a CMake version capable of building combined FAT builds (simulator and target slices combined in one static lib)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14")
set(MODERN_CMAKE YES)
endif()
# Get the Xcode version being used.
# Problem: CMake runs toolchain files multiple times, but can't read cache variables on some runs.
# Workaround: On first run (in which cache variables are always accessible), set an intermediary environment variable.
#
# NOTE: This pattern is used i many places in this toolchain to speed up checks of all sorts
if(DEFINED XCODE_VERSION_INT)
# Environment variables are always preserved.
set(ENV{_XCODE_VERSION_INT} "${XCODE_VERSION_INT}")
elseif(DEFINED ENV{_XCODE_VERSION_INT})
set(XCODE_VERSION_INT "$ENV{_XCODE_VERSION_INT}")
elseif(NOT DEFINED XCODE_VERSION_INT)
find_program(XCODEBUILD_EXECUTABLE xcodebuild)
if(NOT XCODEBUILD_EXECUTABLE)
message(FATAL_ERROR "xcodebuild not found. Please install either the standalone commandline tools or Xcode.")
endif()
execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version
OUTPUT_VARIABLE XCODE_VERSION_INT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION_INT "${XCODE_VERSION_INT}")
string(REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION_INT "${XCODE_VERSION_INT}")
set(XCODE_VERSION_INT "${XCODE_VERSION_INT}" CACHE INTERNAL "")
endif()
# Assuming that xcode 12.0 is installed you most probably have ios sdk 14.0 or later installed (tested on Big Sur)
# if you don't set a deployment target it will be set the way you only get 64-bit builds
if(NOT DEFINED DEPLOYMENT_TARGET AND XCODE_VERSION_INT VERSION_GREATER 12.0)
# Temporarily fix the arm64 issues in CMake install-combined by excluding arm64 for simulator builds (needed for Apple Silicon...)
set(CMAKE_XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] "arm64")
endif()
# Check if the platform variable is set
if(DEFINED PLATFORM)
# Environment variables are always preserved.
set(ENV{_PLATFORM} "${PLATFORM}")
elseif(DEFINED ENV{_PLATFORM})
set(PLATFORM "$ENV{_PLATFORM}")
elseif(NOT DEFINED PLATFORM)
message(FATAL_ERROR "PLATFORM argument not set. Bailing configure since I don't know what target you want to build for!")
endif ()
if(PLATFORM MATCHES ".*COMBINED" AND NOT CMAKE_GENERATOR MATCHES "Xcode")
message(FATAL_ERROR "The combined builds support requires Xcode to be used as generator via '-G Xcode' command-line argument in CMake")
endif()
# Safeguard that the platform value is set and is one of the supported values
list(FIND _supported_platforms ${PLATFORM} contains_PLATFORM)
if("${contains_PLATFORM}" EQUAL "-1")
string(REPLACE ";" "\n * " _supported_platforms_formatted "${_supported_platforms}")
message(FATAL_ERROR " Invalid PLATFORM specified! Current value: ${PLATFORM}.\n"
" Supported PLATFORM values: \n * ${_supported_platforms_formatted}")
endif()
# Check if Apple Silicon is supported
if(PLATFORM MATCHES "^(MAC_ARM64)$|^(MAC_CATALYST_ARM64)$" AND ${CMAKE_VERSION} VERSION_LESS "3.19.5")
message(FATAL_ERROR "Apple Silicon builds requires a minimum of CMake 3.19.5")
endif()
# Touch toolchain variable to suppress "unused variable" warning.
# This happens if CMake is invoked with the same command line the second time.
if(CMAKE_TOOLCHAIN_FILE)
endif()
# Fix for PThread library not in path
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(CMAKE_USE_WIN32_THREADS_INIT 0)
set(CMAKE_USE_PTHREADS_INIT 1)
# Specify minimum version of deployment target.
if(NOT DEFINED DEPLOYMENT_TARGET)
if (PLATFORM MATCHES "WATCHOS")
# Unless specified, SDK version 4.0 is used by default as minimum target version (watchOS).
set(DEPLOYMENT_TARGET "4.0")
elseif(PLATFORM STREQUAL "MAC")
# Unless specified, SDK version 10.13 (High sierra) is used by default as minimum target version (macos).
set(DEPLOYMENT_TARGET "10.13")
elseif(PLATFORM STREQUAL "MAC_ARM64")
# Unless specified, SDK version 11.0 (Big Sur) is used by default as minimum target version (macos on arm).
set(DEPLOYMENT_TARGET "11.0")
elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64")
# Unless specified, SDK version 13.0 is used by default as minimum target version (mac catalyst minimum requirement).
set(DEPLOYMENT_TARGET "13.0")
else()
# Unless specified, SDK version 11.0 is used by default as minimum target version (iOS, tvOS).
set(DEPLOYMENT_TARGET "11.0")
endif()
message(STATUS "[DEFAULTS] Using the default min-version since DEPLOYMENT_TARGET not provided!")
elseif(DEFINED DEPLOYMENT_TARGET AND PLATFORM MATCHES "^MAC_CATALYST" AND ${DEPLOYMENT_TARGET} VERSION_LESS "13.0")
message(FATAL_ERROR "Mac Catalyst builds requires a minimum deployment target of 13.0!")
endif()
# Store the DEPLOYMENT_TARGET in the cache
set(DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}" CACHE INTERNAL "")
# Handle the case where we are targeting iOS and a version above 10.3.4 (32-bit support dropped officially)
if(PLATFORM STREQUAL "OS" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4)
set(PLATFORM "OS64")
message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.")
elseif(PLATFORM STREQUAL "SIMULATOR" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4)
set(PLATFORM "SIMULATOR64")
message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.")
endif()
set(PLATFORM_INT "${PLATFORM}")
if(DEFINED ARCHS)
string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}")
endif()
# Determine the platform name and architectures for use in xcodebuild commands
# from the specified PLATFORM_INT name.
if(PLATFORM_INT STREQUAL "OS")
set(SDK_NAME iphoneos)
if(NOT ARCHS)
set(ARCHS armv7 armv7s arm64)
set(APPLE_TARGET_TRIPLE_INT arm-apple-ios$${DEPLOYMENT_TARGET})
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET})
endif()
elseif(PLATFORM_INT STREQUAL "OS64")
set(SDK_NAME iphoneos)
if(NOT ARCHS)
if (XCODE_VERSION_INT VERSION_GREATER 10.0)
set(ARCHS arm64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example
else()
set(ARCHS arm64)
endif()
set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios${DEPLOYMENT_TARGET})
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET})
endif()
elseif(PLATFORM_INT STREQUAL "OS64COMBINED")
set(SDK_NAME iphoneos)
if(MODERN_CMAKE)
if(NOT ARCHS)
if (XCODE_VERSION_INT VERSION_GREATER 10.0)
set(ARCHS arm64 x86_64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "arm64")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "x86_64")
else()
set(ARCHS arm64 x86_64)
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "arm64")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "x86_64")
endif()
set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-ios${DEPLOYMENT_TARGET})
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET})
endif()
else()
message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the OS64COMBINED setting work")
endif()
elseif(PLATFORM_INT STREQUAL "SIMULATOR")
set(SDK_NAME iphonesimulator)
if(NOT ARCHS)
set(ARCHS i386)
set(APPLE_TARGET_TRIPLE_INT i386-apple-ios${DEPLOYMENT_TARGET}-simulator)
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-simulator)
endif()
message(DEPRECATION "SIMULATOR IS DEPRECATED. Consider using SIMULATOR64 instead.")
elseif(PLATFORM_INT STREQUAL "SIMULATOR64")
set(SDK_NAME iphonesimulator)
if(NOT ARCHS)
set(ARCHS x86_64)
set(APPLE_TARGET_TRIPLE_INT x86_64-apple-ios${DEPLOYMENT_TARGET}-simulator)
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-simulator)
endif()
elseif(PLATFORM_INT STREQUAL "SIMULATORARM64")
set(SDK_NAME iphonesimulator)
if(NOT ARCHS)
set(ARCHS arm64)
set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios${DEPLOYMENT_TARGET}-simulator)
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-simulator)
endif()
elseif(PLATFORM_INT STREQUAL "TVOS")
set(SDK_NAME appletvos)
if(NOT ARCHS)
set(ARCHS arm64)
set(APPLE_TARGET_TRIPLE_INT aarch64-apple-tvos${DEPLOYMENT_TARGET})
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos${DEPLOYMENT_TARGET})
endif()
elseif (PLATFORM_INT STREQUAL "TVOSCOMBINED")
set(SDK_NAME appletvos)
if(MODERN_CMAKE)
if(NOT ARCHS)
set(ARCHS arm64 x86_64)
set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-tvos${DEPLOYMENT_TARGET})
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvos*] "arm64")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvsimulator*] "x86_64")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=appletvos*] "arm64")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=appletvsimulator*] "x86_64")
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos${DEPLOYMENT_TARGET})
endif()
else()
message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the TVOSCOMBINED setting work")
endif()
elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS")
set(SDK_NAME appletvsimulator)
if(NOT ARCHS)
set(ARCHS x86_64)
set(APPLE_TARGET_TRIPLE_INT x86_64-apple-tvos${DEPLOYMENT_TARGET}-simulator)
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos${DEPLOYMENT_TARGET}-simulator)
endif()
elseif(PLATFORM_INT STREQUAL "WATCHOS")
set(SDK_NAME watchos)
if(NOT ARCHS)
if (XCODE_VERSION_INT VERSION_GREATER 10.0)
set(ARCHS armv7k arm64_32)
set(APPLE_TARGET_TRIPLE_INT aarch64_32-apple-watchos${DEPLOYMENT_TARGET})
else()
set(ARCHS armv7k)
set(APPLE_TARGET_TRIPLE_INT arm-apple-watchos${DEPLOYMENT_TARGET})
endif()
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET})
endif()
elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED")
set(SDK_NAME watchos)
if(MODERN_CMAKE)
if(NOT ARCHS)
if (XCODE_VERSION_INT VERSION_GREATER 10.0)
set(ARCHS armv7k arm64_32 i386)
set(APPLE_TARGET_TRIPLE_INT aarch64_32-i386-apple-watchos${DEPLOYMENT_TARGET})
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k arm64_32")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k arm64_32")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386")
else()
set(ARCHS armv7k i386)
set(APPLE_TARGET_TRIPLE_INT arm-i386-apple-watchos${DEPLOYMENT_TARGET})
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386")
endif()
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET})
endif()
else()
message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the WATCHOSCOMBINED setting work")
endif()
elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS")
set(SDK_NAME watchsimulator)
if(NOT ARCHS)
set(ARCHS i386)
set(APPLE_TARGET_TRIPLE_INT i386-apple-watchos${DEPLOYMENT_TARGET}-simulator)
else()
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}-simulator)
endif()
elseif(PLATFORM_INT STREQUAL "MAC" OR PLATFORM_INT STREQUAL "MAC_CATALYST")
set(SDK_NAME macosx)
if(NOT ARCHS)
set(ARCHS x86_64)
endif()
string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}")
if(PLATFORM_INT STREQUAL "MAC")
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx${DEPLOYMENT_TARGET})
elseif(PLATFORM_INT STREQUAL "MAC_CATALYST")
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi)
endif()
elseif(PLATFORM_INT MATCHES "^(MAC_ARM64)$|^(MAC_CATALYST_ARM64)$")
set(SDK_NAME macosx)
if(NOT ARCHS)
set(ARCHS arm64)
endif()
string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}")
if(PLATFORM_INT STREQUAL "MAC_ARM64")
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx${DEPLOYMENT_TARGET})
elseif(PLATFORM_INT STREQUAL "MAC_CATALYST_ARM64")
set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi)
endif()
else()
message(FATAL_ERROR "Invalid PLATFORM: ${PLATFORM_INT}")
endif()
if(MODERN_CMAKE AND PLATFORM_INT MATCHES ".*COMBINED" AND NOT CMAKE_GENERATOR MATCHES "Xcode")
message(FATAL_ERROR "The COMBINED options only work with Xcode generator, -G Xcode")
endif()
if(CMAKE_GENERATOR MATCHES "Xcode" AND PLATFORM_INT MATCHES "^MAC_CATALYST")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
set(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "macosx")
set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-maccatalyst")
if(NOT DEFINED MACOSX_DEPLOYMENT_TARGET)
set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "10.15")
else()
set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "${MACOSX_DEPLOYMENT_TARGET}")
endif()
elseif(CMAKE_GENERATOR MATCHES "Xcode")
set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}")
if(NOT PLATFORM_INT MATCHES ".*COMBINED")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=${SDK_NAME}*] "${ARCHS}")
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=${SDK_NAME}*] "${ARCHS}")
endif()
endif()
# If user did not specify the SDK root to use, then query xcodebuild for it.
if(DEFINED CMAKE_OSX_SYSROOT_INT)
# Environment variables are always preserved.
set(ENV{_CMAKE_OSX_SYSROOT_INT} "${CMAKE_OSX_SYSROOT_INT}")
elseif(DEFINED ENV{_CMAKE_OSX_SYSROOT_INT})
set(CMAKE_OSX_SYSROOT_INT "$ENV{_CMAKE_OSX_SYSROOT_INT}")
elseif(NOT DEFINED CMAKE_OSX_SYSROOT_INT)
execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk ${SDK_NAME} Path
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT_INT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if (NOT DEFINED CMAKE_OSX_SYSROOT_INT AND NOT DEFINED CMAKE_OSX_SYSROOT)
message(SEND_ERROR "Please make sure that Xcode is installed and that the toolchain"
"is pointing to the correct path. Please run:"
"sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"
"and see if that fixes the problem for you.")
message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} "
"does not exist.")
elseif(DEFINED CMAKE_OSX_SYSROOT_INT)
set(CMAKE_OSX_SYSROOT_INT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "")
# Specify the location or name of the platform SDK to be used in CMAKE_OSX_SYSROOT.
set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "")
endif()
# Use bitcode or not
if(NOT DEFINED ENABLE_BITCODE AND NOT ARCHS MATCHES "((^|;|, )(i386|x86_64))+")
# Unless specified, enable bitcode support by default
message(STATUS "[DEFAULTS] Enabling bitcode support by default. ENABLE_BITCODE not provided!")
set(ENABLE_BITCODE TRUE)
elseif(NOT DEFINED ENABLE_BITCODE)
message(STATUS "[DEFAULTS] Disabling bitcode support by default on simulators. ENABLE_BITCODE not provided for override!")
set(ENABLE_BITCODE FALSE)
endif()
set(ENABLE_BITCODE_INT ${ENABLE_BITCODE} CACHE BOOL
"Whether or not to enable bitcode" FORCE)
# Use ARC or not
if(NOT DEFINED ENABLE_ARC)
# Unless specified, enable ARC support by default
set(ENABLE_ARC TRUE)
message(STATUS "[DEFAULTS] Enabling ARC support by default. ENABLE_ARC not provided!")
endif()
set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" FORCE)
# Use hidden visibility or not
if(NOT DEFINED ENABLE_VISIBILITY)
# Unless specified, disable symbols visibility by default
set(ENABLE_VISIBILITY FALSE)
message(STATUS "[DEFAULTS] Hiding symbols visibility by default. ENABLE_VISIBILITY not provided!")
endif()
set(ENABLE_VISIBILITY_INT ${ENABLE_VISIBILITY} CACHE BOOL "Whether or not to hide symbols from the dynamic linker (-fvisibility=hidden)" FORCE)
# Set strict compiler checks or not
if(NOT DEFINED ENABLE_STRICT_TRY_COMPILE)
# Unless specified, disable strict try_compile()
set(ENABLE_STRICT_TRY_COMPILE FALSE)
message(STATUS "[DEFAULTS] Using NON-strict compiler checks by default. ENABLE_STRICT_TRY_COMPILE not provided!")
endif()
set(ENABLE_STRICT_TRY_COMPILE_INT ${ENABLE_STRICT_TRY_COMPILE} CACHE BOOL
"Whether or not to use strict compiler checks" FORCE)
# Get the SDK version information.
if(DEFINED SDK_VERSION)
# Environment variables are always preserved.
set(ENV{_SDK_VERSION} "${SDK_VERSION}")
elseif(DEFINED ENV{_SDK_VERSION})
set(SDK_VERSION "$ENV{_SDK_VERSION}")
elseif(NOT DEFINED SDK_VERSION)
execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -sdk ${CMAKE_OSX_SYSROOT_INT} -version SDKVersion
OUTPUT_VARIABLE SDK_VERSION
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
# Find the Developer root for the specific iOS platform being compiled for
# from CMAKE_OSX_SYSROOT. Should be ../../ from SDK specified in
# CMAKE_OSX_SYSROOT. There does not appear to be a direct way to obtain
# this information from xcrun or xcodebuild.
if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT CMAKE_GENERATOR MATCHES "Xcode")
get_filename_component(PLATFORM_SDK_DIR ${CMAKE_OSX_SYSROOT_INT} PATH)
get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH)
if (NOT EXISTS "${CMAKE_DEVELOPER_ROOT}")
message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: ${CMAKE_DEVELOPER_ROOT} does not exist.")
endif()
endif()
# Find the C & C++ compilers for the specified SDK.
if(DEFINED CMAKE_C_COMPILER)
# Environment variables are always preserved.
set(ENV{_CMAKE_C_COMPILER} "${CMAKE_C_COMPILER}")
elseif(DEFINED ENV{_CMAKE_C_COMPILER})
set(CMAKE_C_COMPILER "$ENV{_CMAKE_C_COMPILER}")
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
elseif(NOT DEFINED CMAKE_C_COMPILER)
execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find clang
OUTPUT_VARIABLE CMAKE_C_COMPILER
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
endif()
if(DEFINED CMAKE_CXX_COMPILER)
# Environment variables are always preserved.
set(ENV{_CMAKE_CXX_COMPILER} "${CMAKE_CXX_COMPILER}")
elseif(DEFINED ENV{_CMAKE_CXX_COMPILER})
set(CMAKE_CXX_COMPILER "$ENV{_CMAKE_CXX_COMPILER}")
elseif(NOT DEFINED CMAKE_CXX_COMPILER)
execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find clang++
OUTPUT_VARIABLE CMAKE_CXX_COMPILER
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
# Find (Apple's) libtool.
if(DEFINED BUILD_LIBTOOL)
# Environment variables are always preserved.
set(ENV{_BUILD_LIBTOOL} "${BUILD_LIBTOOL}")
elseif(DEFINED ENV{_BUILD_LIBTOOL})
set(BUILD_LIBTOOL "$ENV{_BUILD_LIBTOOL}")
elseif(NOT DEFINED BUILD_LIBTOOL)
execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find libtool
OUTPUT_VARIABLE BUILD_LIBTOOL
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
# Find the toolchain's provided install_name_tool if none is found on the host
if(DEFINED CMAKE_INSTALL_NAME_TOOL)
# Environment variables are always preserved.
set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}")
elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL})
set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}")
elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find install_name_tool
OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "")
endif()
# Configure libtool to be used instead of ar + ranlib to build static libraries.
# This is required on Xcode 7+, but should also work on previous versions of
# Xcode.
get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach(lang ${languages})
set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o <TARGET> <LINK_FLAGS> <OBJECTS> " CACHE INTERNAL "")
endforeach()
# CMake 3.14+ support building for iOS, watchOS and tvOS out of the box.
if(MODERN_CMAKE)
if(SDK_NAME MATCHES "iphone")
set(CMAKE_SYSTEM_NAME iOS)
elseif(SDK_NAME MATCHES "macosx")
set(CMAKE_SYSTEM_NAME Darwin)
elseif(SDK_NAME MATCHES "appletv")
set(CMAKE_SYSTEM_NAME tvOS)
elseif(SDK_NAME MATCHES "watch")
set(CMAKE_SYSTEM_NAME watchOS)
endif()
# Provide flags for a combined FAT library build on newer CMake versions
if(PLATFORM_INT MATCHES ".*COMBINED")
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO")
set(CMAKE_IOS_INSTALL_COMBINED YES)
message(STATUS "Will combine built (static) artifacts into FAT lib...")
endif()
elseif(NOT DEFINED CMAKE_SYSTEM_NAME AND ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.10")
# Legacy code path prior to CMake 3.14 or fallback if no CMAKE_SYSTEM_NAME specified
set(CMAKE_SYSTEM_NAME iOS)
elseif(NOT DEFINED CMAKE_SYSTEM_NAME)
# Legacy code path prior to CMake 3.14 or fallback if no CMAKE_SYSTEM_NAME specified
set(CMAKE_SYSTEM_NAME Darwin)
endif()
# Standard settings.
set(CMAKE_SYSTEM_VERSION ${SDK_VERSION} CACHE INTERNAL "")
set(UNIX TRUE CACHE BOOL "")
set(APPLE TRUE CACHE BOOL "")
if(PLATFORM STREQUAL "MAC" OR PLATFORM STREQUAL "MAC_ARM64")
set(IOS FALSE CACHE BOOL "")
set(MACOS TRUE CACHE BOOL "")
elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64")
set(IOS TRUE CACHE BOOL "")
set(MACOS TRUE CACHE BOOL "")
else()
set(IOS TRUE CACHE BOOL "")
endif()
set(CMAKE_AR ar CACHE FILEPATH "" FORCE)
set(CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE)
set(CMAKE_STRIP strip CACHE FILEPATH "" FORCE)
# Set the architectures for which to build.
set(CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE INTERNAL "")
# Change the type of target generated for try_compile() so it'll work when cross-compiling, weak compiler checks
if(NOT ENABLE_STRICT_TRY_COMPILE_INT)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
endif()
# All iOS/Darwin specific settings - some may be redundant.
set(CMAKE_MACOSX_BUNDLE YES)
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
set(CMAKE_SHARED_LIBRARY_PREFIX "lib")
set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
set(CMAKE_SHARED_MODULE_PREFIX "lib")
set(CMAKE_SHARED_MODULE_SUFFIX ".so")
set(CMAKE_C_COMPILER_ABI ELF)
set(CMAKE_CXX_COMPILER_ABI ELF)
set(CMAKE_C_HAS_ISYSROOT 1)
set(CMAKE_CXX_HAS_ISYSROOT 1)
set(CMAKE_MODULE_EXISTS 1)
set(CMAKE_DL_LIBS "")
set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
if(ARCHS MATCHES "((^|;|, )(arm64|arm64e|x86_64))+")
set(CMAKE_C_SIZEOF_DATA_PTR 8)
set(CMAKE_CXX_SIZEOF_DATA_PTR 8)
if(ARCHS MATCHES "((^|;|, )(arm64|arm64e))+")
set(CMAKE_SYSTEM_PROCESSOR "aarch64")
else()
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
endif()
else()
set(CMAKE_C_SIZEOF_DATA_PTR 4)
set(CMAKE_CXX_SIZEOF_DATA_PTR 4)
set(CMAKE_SYSTEM_PROCESSOR "arm")
endif()
# Note that only Xcode 7+ supports the newer more specific:
# -m${SDK_NAME}-version-min flags, older versions of Xcode use:
# -m(ios/ios-simulator)-version-min instead.
if(${CMAKE_VERSION} VERSION_LESS "3.11")
if(PLATFORM_INT STREQUAL "OS" OR PLATFORM_INT STREQUAL "OS64")
if(XCODE_VERSION_INT VERSION_LESS 7.0)
set(SDK_NAME_VERSION_FLAGS
"-mios-version-min=${DEPLOYMENT_TARGET}")
else()
# Xcode 7.0+ uses flags we can build directly from SDK_NAME.
set(SDK_NAME_VERSION_FLAGS
"-m${SDK_NAME}-version-min=${DEPLOYMENT_TARGET}")
endif()
elseif(PLATFORM_INT STREQUAL "TVOS")
set(SDK_NAME_VERSION_FLAGS
"-mtvos-version-min=${DEPLOYMENT_TARGET}")
elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS")
set(SDK_NAME_VERSION_FLAGS
"-mtvos-simulator-version-min=${DEPLOYMENT_TARGET}")
elseif(PLATFORM_INT STREQUAL "WATCHOS")
set(SDK_NAME_VERSION_FLAGS
"-mwatchos-version-min=${DEPLOYMENT_TARGET}")
elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS")
set(SDK_NAME_VERSION_FLAGS
"-mwatchos-simulator-version-min=${DEPLOYMENT_TARGET}")
elseif(PLATFORM_INT STREQUAL "MAC")
set(SDK_NAME_VERSION_FLAGS
"-mmacosx-version-min=${DEPLOYMENT_TARGET}")
else()
# SIMULATOR or SIMULATOR64 both use -mios-simulator-version-min.
set(SDK_NAME_VERSION_FLAGS
"-mios-simulator-version-min=${DEPLOYMENT_TARGET}")
endif()
elseif(NOT PLATFORM_INT MATCHES "^MAC_CATALYST")
# Newer versions of CMake sets the version min flags correctly, skip this for Mac Catalyst targets
set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET})
endif()
if(DEFINED APPLE_TARGET_TRIPLE_INT)
set(APPLE_TARGET_TRIPLE ${APPLE_TARGET_TRIPLE_INT} CACHE INTERNAL "")
set(CMAKE_C_COMPILER_TARGET ${APPLE_TARGET_TRIPLE})
set(CMAKE_CXX_COMPILER_TARGET ${APPLE_TARGET_TRIPLE})
set(CMAKE_ASM_COMPILER_TARGET ${APPLE_TARGET_TRIPLE})
endif()
if(PLATFORM_INT MATCHES "^MAC_CATALYST")
set(C_TARGET_FLAGS "-isystem ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/usr/include -iframework ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/System/Library/Frameworks")
endif()
if(ENABLE_BITCODE_INT)
set(BITCODE "-fembed-bitcode")
set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode")
set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
else()
set(BITCODE "")
set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
endif()
if(ENABLE_ARC_INT)
set(FOBJC_ARC "-fobjc-arc")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES")
else()
set(FOBJC_ARC "-fno-objc-arc")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "NO")
endif()
if(NOT ENABLE_VISIBILITY_INT)
foreach(lang ${languages})
set(CMAKE_${lang}_VISIBILITY_PRESET "hidden" CACHE INTERNAL "")
endforeach()
set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "YES")
set(VISIBILITY "-fvisibility=hidden -fvisibility-inlines-hidden")
else()
foreach(lang ${languages})
set(CMAKE_${lang}_VISIBILITY_PRESET "default" CACHE INTERNAL "")
endforeach()
set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "NO")
set(VISIBILITY "-fvisibility=default")
endif()
if(DEFINED APPLE_TARGET_TRIPLE)
set(APPLE_TARGET_TRIPLE_FLAG "-target ${APPLE_TARGET_TRIPLE}")
endif()
#Check if Xcode generator is used, since that will handle these flags automagically
if(CMAKE_GENERATOR MATCHES "Xcode")
message(STATUS "Not setting any manual command-line buildflags, since Xcode is selected as generator.")
else()
# Hidden visibility is required for C++ on iOS.
set(CMAKE_C_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g ${CMAKE_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DNDEBUG -Os ${CMAKE_CXX_FLAGS_MINSIZEREL}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -DNDEBUG -O2 -g ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_C_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
set(CMAKE_CXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -arch ${CMAKE_OSX_ARCHITECTURES} ${APPLE_TARGET_TRIPLE_FLAG}")
endif()
## Print status messages to inform of the current state
message(STATUS "Configuring ${SDK_NAME} build for platform: ${PLATFORM_INT}, architecture(s): ${ARCHS}")
message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT_INT}")
message(STATUS "Using C compiler: ${CMAKE_C_COMPILER}")
message(STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}")
message(STATUS "Using libtool: ${BUILD_LIBTOOL}")
message(STATUS "Using install name tool: ${CMAKE_INSTALL_NAME_TOOL}")
if(DEFINED APPLE_TARGET_TRIPLE)
message(STATUS "Autoconf target triple: ${APPLE_TARGET_TRIPLE}")
endif()
message(STATUS "Using minimum deployment version: ${DEPLOYMENT_TARGET}"
" (SDK version: ${SDK_VERSION})")
if(MODERN_CMAKE)
message(STATUS "Merging integrated CMake 3.14+ iOS,tvOS,watchOS,macOS toolchain(s) with this toolchain!")
endif()
if(CMAKE_GENERATOR MATCHES "Xcode")
message(STATUS "Using Xcode version: ${XCODE_VERSION_INT}")
endif()
message(STATUS "CMake version: ${CMAKE_VERSION}")
if(DEFINED SDK_NAME_VERSION_FLAGS)
message(STATUS "Using version flags: ${SDK_NAME_VERSION_FLAGS}")
endif()
message(STATUS "Using a data_ptr size of: ${CMAKE_CXX_SIZEOF_DATA_PTR}")
if(ENABLE_BITCODE_INT)
message(STATUS "Bitcode: Enabled")
else()
message(STATUS "Bitcode: Disabled")
endif()
if(ENABLE_ARC_INT)
message(STATUS "ARC: Enabled")
else()
message(STATUS "ARC: Disabled")
endif()
if(ENABLE_VISIBILITY_INT)
message(STATUS "Hiding symbols: Disabled")
else()
message(STATUS "Hiding symbols: Enabled")
endif()
# Set global properties
set_property(GLOBAL PROPERTY PLATFORM "${PLATFORM}")
set_property(GLOBAL PROPERTY APPLE_TARGET_TRIPLE "${APPLE_TARGET_TRIPLE_INT}")
set_property(GLOBAL PROPERTY SDK_VERSION "${SDK_VERSION}")
set_property(GLOBAL PROPERTY XCODE_VERSION "${XCODE_VERSION_INT}")
set_property(GLOBAL PROPERTY OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}")
# Export configurable variables for the try_compile() command.
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
PLATFORM
XCODE_VERSION_INT
SDK_VERSION
DEPLOYMENT_TARGET
CMAKE_DEVELOPER_ROOT
CMAKE_OSX_SYSROOT_INT
ENABLE_BITCODE
ENABLE_ARC
CMAKE_ASM_COMPILER
CMAKE_C_COMPILER
CMAKE_C_COMPILER_TARGET
CMAKE_CXX_COMPILER
CMAKE_CXX_COMPILER_TARGET
BUILD_LIBTOOL
CMAKE_INSTALL_NAME_TOOL
CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_LINK_FLAGS
CMAKE_CXX_LINK_FLAGS
CMAKE_ASM_FLAGS
)
set(CMAKE_PLATFORM_HAS_INSTALLNAME 1)
set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names")
set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names")
set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a")
set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name")
# Set the find root to the SDK developer roots.
# Note: CMAKE_FIND_ROOT_PATH is only useful when cross-compiling. Thus, do not set on macOS builds.
if(NOT PLATFORM_INT STREQUAL "MAC" AND NOT PLATFORM_INT STREQUAL "MAC_ARM64")
list(APPEND CMAKE_FIND_ROOT_PATH "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "")
set(CMAKE_IGNORE_PATH "/System/Library/Frameworks;/usr/local/lib" CACHE INTERNAL "")
endif()
# Default to searching for frameworks first.
set(CMAKE_FIND_FRAMEWORK FIRST)
# Set up the default search directories for frameworks.
if(PLATFORM_INT MATCHES "^MAC_CATALYST")
set(CMAKE_FRAMEWORK_PATH
${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks
${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks
${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/System/Library/Frameworks
${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "")
else()
set(CMAKE_FRAMEWORK_PATH
${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks
${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks
${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "")
endif()
# By default, search both the specified iOS SDK and the remainder of the host filesystem.
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE INTERNAL "")
endif()
if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH CACHE INTERNAL "")
endif()
if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH CACHE INTERNAL "")
endif()
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH CACHE INTERNAL "")
endif()
#
# Some helper-macros below to simplify and beautify the CMakeFile
#
# This little macro lets you set any Xcode specific property.
macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE XCODE_RELVERSION)
set(XCODE_RELVERSION_I "${XCODE_RELVERSION}")
if(XCODE_RELVERSION_I STREQUAL "All")
set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} "${XCODE_VALUE}")
else()
set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION_I}] "${XCODE_VALUE}")
endif()
endmacro(set_xcode_property)
# This macro lets you find executable programs on the host system.
macro(find_host_package)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER)
set(_TOOLCHAIN_IOS ${IOS})
set(IOS FALSE)
find_package(${ARGN})
set(IOS ${_TOOLCHAIN_IOS})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
endmacro(find_host_package)