mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-13 18:58:29 +12:00
Update home menu branch (#759)
* Fix typo (#680) Co-authored-by: Noumi <139501014+noumidev@users.noreply.github.com> * More PTM stuff Co-Authored-By: Noumi <139501014+noumidev@users.noreply.github.com> * Make system language configurable * Fix building crypto++ for x64 target on Apple silicon MacOS * Attempt to switch to M1 runners again * Prevent selecting Vulkan renderer in Qt frontend and present a message * Libretro: Add system language option * Only enable audio by default on libretro for now * CMake: Bump version * 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> * GL: Add usingGLES to driverInfo struct (#694) * Wayland fixes part 1 * Support GLES on desktop * 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 * No need to call screen->show() twice * Fix disabling Wayland & building on some distros (#700) * GLES: Properly stub out logic ops * Fix git versioning * 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> * Removed dead Citra link in readme (#706) * CRO: Lighter icache flushes * Implement Luma icache SVCs * Add missing SVC logs * GPU: Add sw texture copies * Use vk::detail::DynamicLoader instead of vk::DynamicLoader (#710) * Use vk::detail::DynamicLoader instead of vk::DynamicLoader * Update renderer_vk.cpp * Vk: Fix typo * Vk: Lock CI runners to SDK version 1.3.301 temporarily * Vk: Fixing CI pt 2 * Vulkan: Fixing CI pt 3 * Vk: Fix typo * Temporarily give 80MB to all processes (#715) * Try to cross-compile Libretro core for arm64 (#717) * Try to cross-compile Libretro core for arm64 * Bonk * Update Hydra_Build.yml * [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> * Libretro audio device: Fix frame count * Mark audio devices as final * Add toggle for libretro audio device (#719) * Very important work (#720) * Very important work * Most important fix * Add more HLE service calls for eshop (#721) * CI: Fix Vulkan SDK action (#723) * GPU registers: Fix writes to some registers ignoring the mask (#725) Co-authored-by: henry <23128103+atem2069@users.noreply.github.com> * OLED theme * OLED theme config fix (#736) Co-authored-by: smiRaphi <neogt404@gmail.com> * Adding Swedish translation * Fix Metal renderer compilation on iOS * [Core] Improve iOS compilation workflow * [Qt] Hook Swedish to UI * AppDataDocumentProvider: Typo (#740) * More iOS work * More iOS progress * More iOS work * AppDataDocumentProvider: Add missing ``COLUMN_FLAGS`` in the default document projectation (#741) Fixes unable to copy files from device to app's internal storage problem * More iOS work * ios: Simplify MTKView interface (still doesn't work though) * ios: Pass CAMetalLayer instead of void* to Obj-C++ bridging header * Fix bridging cast * FINALLY IOS GRAPHICS * ios: Remove printf spam * Metal: Reimplement some texture formats on iOS * metal: implement texture decoder * metal: check for format support * metal: implement texture swizzling * metal: remove unused texture functions * Shadergen types: Add Metal & MSL * Format * Undo submodule changes * Readme: Add Chonkystation 3 * Metal: Use std::unique_ptr for texture decode * AppDataDocumentProvider: Allow to remove documents (#744) * AppDataDocumentProvider: Allow to remove documents * Typo * Metal renderer fixes for iOS * iOS driver: Add doc comments * 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 * iOS: Fail on build error * iOS: Add file picker (#747) * iOS: Add file picker * Fix lock placement * Qt: Add runpog icon (#752) * Update discord-rpc submodule (#753) * Remove cryptoppwin submodule (#754) * Add optional texture hashing * Fix build on new Vk SDK (#757) Co-authored-by: Nadia Holmquist Pedersen <893884+nadiaholmquist@users.noreply.github.com> * CI: Use new Vulkan SDK --------- Co-authored-by: Noumi <139501014+noumidev@users.noreply.github.com> Co-authored-by: Thomas <thomas@thomasw.dev> Co-authored-by: Thomas <twvd@users.noreply.github.com> Co-authored-by: Daniel López Guimaraes <danielectra@outlook.com> Co-authored-by: Jonian Guveli <jonian@hardpixel.eu> Co-authored-by: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Co-authored-by: Auxy6858 <71662994+Auxy6858@users.noreply.github.com> Co-authored-by: Paris Oplopoios <parisoplop@gmail.com> Co-authored-by: henry <23128103+atem2069@users.noreply.github.com> Co-authored-by: smiRaphi <neogt404@gmail.com> Co-authored-by: smiRaphi <87574679+smiRaphi@users.noreply.github.com> Co-authored-by: Daniel Nylander <po@danielnylander.se> Co-authored-by: Samuliak <samuliak77@gmail.com> Co-authored-by: Albert <45282415+ggrtk@users.noreply.github.com> Co-authored-by: Nadia Holmquist Pedersen <893884+nadiaholmquist@users.noreply.github.com>
This commit is contained in:
parent
d5506f311f
commit
082b6216b3
302 changed files with 55525 additions and 747 deletions
22
.github/gles.patch
vendored
22
.github/gles.patch
vendored
|
@ -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
|
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
|
||||||
index 9f07df0b..96a35afa 100644
|
index 9f07df0b..96a35afa 100644
|
||||||
--- a/src/host_shaders/opengl_fragment_shader.frag
|
--- a/src/host_shaders/opengl_fragment_shader.frag
|
||||||
|
|
14
.github/workflows/Android_Build.yml
vendored
14
.github/workflows/Android_Build.yml
vendored
|
@ -23,6 +23,9 @@ jobs:
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
- name: Setup CCache
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
- name: Set up gradle caches
|
- name: Set up gradle caches
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
|
@ -34,7 +37,7 @@ jobs:
|
||||||
${{ runner.os }}-pandroid-x86_64-
|
${{ runner.os }}-pandroid-x86_64-
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -47,7 +50,7 @@ jobs:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Configure CMake
|
- 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
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
@ -88,6 +91,9 @@ jobs:
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
- name: Setup CCache
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
- name: Set up gradle caches
|
- name: Set up gradle caches
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
|
@ -99,7 +105,7 @@ jobs:
|
||||||
${{ runner.os }}-pandroid-arm64-
|
${{ runner.os }}-pandroid-arm64-
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -112,7 +118,7 @@ jobs:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Configure CMake
|
- 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
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|
2
.github/workflows/HTTP_Build.yml
vendored
2
.github/workflows/HTTP_Build.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
|
43
.github/workflows/Hydra_Build.yml
vendored
43
.github/workflows/Hydra_Build.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -55,7 +55,7 @@ jobs:
|
||||||
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
||||||
|
|
||||||
MacOS:
|
MacOS:
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -63,7 +63,7 @@ jobs:
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -116,7 +116,7 @@ jobs:
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -163,7 +163,7 @@ jobs:
|
||||||
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -180,3 +180,36 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: Android Hydra core
|
name: Android Hydra core
|
||||||
path: '${{github.workspace}}/build/libAlber.so'
|
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
|
||||||
|
|
2
.github/workflows/Linux_AppImage_Build.yml
vendored
2
.github/workflows/Linux_AppImage_Build.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
|
2
.github/workflows/Linux_Build.yml
vendored
2
.github/workflows/Linux_Build.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
|
6
.github/workflows/MacOS_Build.yml
vendored
6
.github/workflows/MacOS_Build.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
arch: [x86_64, arm64]
|
arch: [x86_64, arm64]
|
||||||
|
|
||||||
name: MacOS-${{ matrix.arch }}
|
name: MacOS-${{ matrix.arch }}
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -25,7 +25,7 @@ jobs:
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -61,7 +61,7 @@ jobs:
|
||||||
MacOS-Universal:
|
MacOS-Universal:
|
||||||
name: MacOS-Universal
|
name: MacOS-Universal
|
||||||
needs: [build]
|
needs: [build]
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download x86_64
|
- name: Download x86_64
|
||||||
|
|
10
.github/workflows/Qt_Build.yml
vendored
10
.github/workflows/Qt_Build.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
version: 6.2.0
|
version: 6.2.0
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -56,7 +56,7 @@ jobs:
|
||||||
arch: [x86_64, arm64]
|
arch: [x86_64, arm64]
|
||||||
|
|
||||||
name: MacOS-${{ matrix.arch }}
|
name: MacOS-${{ matrix.arch }}
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -64,7 +64,7 @@ jobs:
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
@ -109,7 +109,7 @@ jobs:
|
||||||
MacOS-Universal:
|
MacOS-Universal:
|
||||||
name: MacOS-Universal
|
name: MacOS-Universal
|
||||||
needs: [MacOS]
|
needs: [MacOS]
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download x86_64
|
- name: Download x86_64
|
||||||
|
@ -162,7 +162,7 @@ jobs:
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
|
2
.github/workflows/Windows_Build.yml
vendored
2
.github/workflows/Windows_Build.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Setup Vulkan SDK
|
- name: Setup Vulkan SDK
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
uses: humbletim/setup-vulkan-sdk@main
|
||||||
with:
|
with:
|
||||||
vulkan-query-version: latest
|
vulkan-query-version: latest
|
||||||
vulkan-use-cache: true
|
vulkan-use-cache: true
|
||||||
|
|
39
.github/workflows/iOS_Build.yml
vendored
Normal file
39
.github/workflows/iOS_Build.yml
vendored
Normal 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: latest
|
||||||
|
vulkan-use-cache: true
|
||||||
|
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||||
|
|
||||||
|
- name: Build core and frontend
|
||||||
|
run: cd src/pandios && ./build.sh
|
10
.gitmodules
vendored
10
.gitmodules
vendored
|
@ -1,9 +1,6 @@
|
||||||
[submodule "third_party/elfio"]
|
[submodule "third_party/elfio"]
|
||||||
path = third_party/elfio
|
path = third_party/elfio
|
||||||
url = https://github.com/serge1/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"]
|
[submodule "third_party/cryptopp/cryptopp"]
|
||||||
path = third_party/cryptopp/cryptopp
|
path = third_party/cryptopp/cryptopp
|
||||||
url = https://github.com/weidai11/cryptopp
|
url = https://github.com/weidai11/cryptopp
|
||||||
|
@ -79,9 +76,10 @@
|
||||||
[submodule "third_party/fdk-aac"]
|
[submodule "third_party/fdk-aac"]
|
||||||
path = third_party/fdk-aac
|
path = third_party/fdk-aac
|
||||||
url = https://github.com/Panda3DS-emu/fdk-aac/
|
url = https://github.com/Panda3DS-emu/fdk-aac/
|
||||||
[submodule "third_party/cryptoppwin"]
|
|
||||||
path = third_party/cryptoppwin
|
|
||||||
url = https://github.com/shadps4-emu/ext-cryptoppwin
|
|
||||||
[submodule "third_party/oaknut"]
|
[submodule "third_party/oaknut"]
|
||||||
path = third_party/oaknut
|
path = third_party/oaknut
|
||||||
url = https://github.com/panda3ds-emu/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
|
||||||
|
|
122
CMakeLists.txt
122
CMakeLists.txt
|
@ -50,6 +50,7 @@ option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
|
||||||
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
|
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
|
||||||
option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON)
|
option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON)
|
||||||
option(ENABLE_METAL "Enable Metal rendering backend (if available)" 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_LTO "Enable link-time optimization" OFF)
|
||||||
option(ENABLE_TESTS "Compile unit-tests" 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)
|
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(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(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(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(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)
|
set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES)
|
||||||
|
@ -79,6 +88,10 @@ endif()
|
||||||
if(BUILD_LIBRETRO_CORE)
|
if(BUILD_LIBRETRO_CORE)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
add_compile_definitions(__LIBRETRO__)
|
add_compile_definitions(__LIBRETRO__)
|
||||||
|
|
||||||
|
if(USE_LIBRETRO_AUDIO)
|
||||||
|
add_compile_definitions(USE_LIBRETRO_AUDIO_DEVICE)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD)
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD)
|
||||||
|
@ -88,7 +101,7 @@ endif()
|
||||||
|
|
||||||
# Generate versioning files
|
# Generate versioning files
|
||||||
find_package(Git)
|
find_package(Git)
|
||||||
set(PANDA3DS_VERSION "0.8")
|
set(PANDA3DS_VERSION "0.9")
|
||||||
|
|
||||||
if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/version.hpp.in)
|
if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/version.hpp.in)
|
||||||
file(WRITE ${CMAKE_BINARY_DIR}/include/version.hpp.in "#define PANDA3DS_VERSION \"\${PANDA3DS_VERSION}\"")
|
file(WRITE ${CMAKE_BINARY_DIR}/include/version.hpp.in "#define PANDA3DS_VERSION \"\${PANDA3DS_VERSION}\"")
|
||||||
|
@ -97,22 +110,29 @@ endif()
|
||||||
if(GIT_FOUND AND ENABLE_GIT_VERSIONING)
|
if(GIT_FOUND AND ENABLE_GIT_VERSIONING)
|
||||||
execute_process(
|
execute_process(
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
|
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
|
OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
if(NOT PANDA3DS_VERSION STREQUAL git_version_tag)
|
|
||||||
execute_process(
|
execute_process(
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=7
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
|
||||||
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
|
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}")
|
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
|
||||||
unset(git_version_rev)
|
|
||||||
endif()
|
endif()
|
||||||
string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}")
|
string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}")
|
||||||
unset(git_version_tag)
|
unset(git_version_tag)
|
||||||
|
unset(git_version_rev)
|
||||||
endif()
|
endif()
|
||||||
configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp)
|
configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp)
|
||||||
include_directories(${CMAKE_BINARY_DIR}/include/)
|
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(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
|
||||||
add_compile_definitions(SDL_MAIN_HANDLED)
|
add_compile_definitions(SDL_MAIN_HANDLED)
|
||||||
|
|
||||||
|
if(ENABLE_WAYLAND)
|
||||||
|
add_compile_definitions(WAYLAND_ENABLED)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
||||||
add_subdirectory(third_party/discord-rpc)
|
add_subdirectory(third_party/discord-rpc)
|
||||||
include_directories(third_party/discord-rpc/include)
|
include_directories(third_party/discord-rpc/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if (NOT ANDROID)
|
if (NOT ANDROID)
|
||||||
if (USE_SYSTEM_SDL2)
|
if (USE_SYSTEM_SDL2)
|
||||||
find_package(SDL2 CONFIG REQUIRED)
|
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/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/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/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/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/renderdoc.cpp
|
||||||
src/frontend_settings.cpp
|
src/frontend_settings.cpp src/miniaudio/miniaudio.cpp
|
||||||
)
|
)
|
||||||
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.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
|
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/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/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/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/services/news_s.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 include/services/news_s.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(
|
cmrc_add_resource_library(
|
||||||
resources_console_fonts
|
resources_console_fonts
|
||||||
NAMESPACE ConsoleFonts
|
NAMESPACE ConsoleFonts
|
||||||
|
@ -422,16 +455,22 @@ if(ENABLE_LUAJIT AND NOT ANDROID)
|
||||||
target_link_libraries(AlberCore PRIVATE uv_a)
|
target_link_libraries(AlberCore PRIVATE uv_a)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(GL_CONTEXT_SOURCE_FILES "")
|
||||||
|
|
||||||
if(ENABLE_QT_GUI)
|
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)
|
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)
|
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()
|
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
|
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_egl_x11.cpp third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.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()
|
||||||
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\\PICA" FILES ${PICA_SOURCE_FILES})
|
||||||
source_group("Source Files\\Core\\Audio" FILES ${AUDIO_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\\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_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
|
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
|
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/textures.cpp src/core/renderer_gl/etc1.cpp
|
||||||
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag
|
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert
|
||||||
src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.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
|
src/host_shaders/opengl_fragment_shader.frag
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -474,8 +514,10 @@ if(ENABLE_OPENGL)
|
||||||
resources_renderer_gl
|
resources_renderer_gl
|
||||||
NAMESPACE RendererGL
|
NAMESPACE RendererGL
|
||||||
WHENCE "src/host_shaders/"
|
WHENCE "src/host_shaders/"
|
||||||
"src/host_shaders/opengl_display.frag"
|
|
||||||
"src/host_shaders/opengl_display.vert"
|
"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_vertex_shader.vert"
|
||||||
"src/host_shaders/opengl_fragment_shader.frag"
|
"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/mtl_common.hpp
|
||||||
include/renderer_mtl/pica_to_mtl.hpp
|
include/renderer_mtl/pica_to_mtl.hpp
|
||||||
include/renderer_mtl/objc_helper.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
|
set(RENDERER_MTL_SOURCE_FILES src/core/renderer_mtl/metal_cpp_impl.cpp
|
||||||
src/core/renderer_mtl/renderer_mtl.cpp
|
src/core/renderer_mtl/renderer_mtl.cpp
|
||||||
src/core/renderer_mtl/mtl_texture.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/mtl_lut_texture.cpp
|
||||||
|
src/core/renderer_mtl/pica_to_mtl.cpp
|
||||||
src/core/renderer_mtl/objc_helper.mm
|
src/core/renderer_mtl/objc_helper.mm
|
||||||
|
src/core/renderer_mtl/texture_decoder.cpp
|
||||||
src/host_shaders/metal_shaders.metal
|
src/host_shaders/metal_shaders.metal
|
||||||
src/host_shaders/metal_blit.metal
|
src/host_shaders/metal_blit.metal
|
||||||
#src/host_shaders/metal_copy_to_lut_texture.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_SOURCE "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metal")
|
||||||
set(SHADER_IR "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.ir")
|
set(SHADER_IR "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.ir")
|
||||||
set(SHADER_METALLIB "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metallib")
|
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
|
# TODO: only include sources in debug builds
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${SHADER_IR}
|
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}
|
DEPENDS ${SHADER_SOURCE}
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${SHADER_METALLIB}
|
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}
|
DEPENDS ${SHADER_IR}
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
set(RENDERER_MTL_HOST_SHADERS_SOURCES ${RENDERER_MTL_HOST_SHADERS_SOURCES} ${SHADER_METALLIB})
|
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_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_METAL=1")
|
||||||
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
|
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
|
||||||
# TODO: check if all of them are needed
|
# 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()
|
endif()
|
||||||
|
|
||||||
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
||||||
|
@ -726,11 +781,14 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||||
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png
|
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png
|
||||||
docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png
|
docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png
|
||||||
docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png
|
docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png
|
||||||
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png
|
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png docs/img/runpog_icon.png
|
||||||
)
|
)
|
||||||
|
|
||||||
# Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable
|
# 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")
|
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
|
||||||
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
|
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
|
||||||
|
|
||||||
|
@ -751,11 +809,17 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(Alber PRIVATE AlberCore)
|
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)
|
elseif(BUILD_HYDRA_CORE)
|
||||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
|
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
|
||||||
include_directories(third_party/hydra_core/include)
|
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)
|
target_link_libraries(Alber PUBLIC AlberCore)
|
||||||
elseif(BUILD_LIBRETRO_CORE)
|
elseif(BUILD_LIBRETRO_CORE)
|
||||||
include_directories(third_party/libretro/include)
|
include_directories(third_party/libretro/include)
|
||||||
|
|
BIN
docs/img/runpog_icon.png
Normal file
BIN
docs/img/runpog_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
774
docs/translations/sv.ts
Normal file
774
docs/translations/sv.ts
Normal 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 <github@danielnylander.se></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 få 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've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||||
|
<translation>Panda3DS utvecklas av volontärer på deras fritid. Nedan finns en lista över några av dessa volontärer som har gått med på att listas här, utan någon särskild ordning.<br>Om du tycker att du också borde listas här, informera oss<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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 på 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 på 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'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'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>
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace PICA::ShaderGen {
|
namespace PICA::ShaderGen {
|
||||||
// Graphics API this shader is targetting
|
// 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)
|
// Shading language to use
|
||||||
enum class Language { GLSL };
|
enum class Language { GLSL, MSL };
|
||||||
} // namespace PICA::ShaderGen
|
} // namespace PICA::ShaderGen
|
9
include/audio/audio_device.hpp
Normal file
9
include/audio/audio_device.hpp
Normal 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
|
36
include/audio/audio_device_interface.hpp
Normal file
36
include/audio/audio_device_interface.hpp
Normal 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) {}
|
||||||
|
};
|
61
include/audio/libretro_audio_device.hpp
Normal file
61
include/audio/libretro_audio_device.hpp
Normal 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; }
|
||||||
|
};
|
|
@ -3,39 +3,31 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "audio/audio_device_interface.hpp"
|
||||||
#include "helpers.hpp"
|
|
||||||
#include "miniaudio.h"
|
#include "miniaudio.h"
|
||||||
#include "ring_buffer.hpp"
|
|
||||||
|
|
||||||
class MiniAudioDevice {
|
class MiniAudioDevice final : public AudioDeviceInterface {
|
||||||
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
|
|
||||||
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
|
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
|
||||||
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
|
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
ma_device device;
|
ma_device device;
|
||||||
ma_context context;
|
ma_context context;
|
||||||
ma_device_config deviceConfig;
|
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.
|
// 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;
|
std::vector<std::string> audioDevices;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
|
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
|
||||||
|
|
||||||
// If safe is on, we create a null audio device
|
// If safe is on, we create a null audio device
|
||||||
void init(Samples& samples, bool safe = false);
|
void init(Samples& samples, bool safe = false) override;
|
||||||
void close();
|
void close() override;
|
||||||
|
|
||||||
void start();
|
void start() override;
|
||||||
void stop();
|
void stop() override;
|
||||||
|
|
||||||
bool isInitialized() const { return initialized; }
|
bool isInitialized() const { return initialized; }
|
||||||
};
|
};
|
|
@ -5,6 +5,7 @@
|
||||||
#include "audio/dsp_core.hpp"
|
#include "audio/dsp_core.hpp"
|
||||||
#include "frontend_settings.hpp"
|
#include "frontend_settings.hpp"
|
||||||
#include "renderer.hpp"
|
#include "renderer.hpp"
|
||||||
|
#include "services/region_codes.hpp"
|
||||||
|
|
||||||
struct AudioDeviceConfig {
|
struct AudioDeviceConfig {
|
||||||
// Audio curve to use for volumes between 0-100
|
// Audio curve to use for volumes between 0-100
|
||||||
|
@ -48,9 +49,26 @@ struct EmulatorConfig {
|
||||||
#endif
|
#endif
|
||||||
static constexpr bool accelerateShadersDefault = true;
|
static constexpr bool accelerateShadersDefault = true;
|
||||||
|
|
||||||
|
#if defined(__LIBRETRO__)
|
||||||
|
static constexpr bool audioEnabledDefault = true;
|
||||||
|
#else
|
||||||
|
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
|
||||||
|
|
||||||
|
static constexpr bool hashTexturesDefault = true;
|
||||||
|
|
||||||
bool shaderJitEnabled = shaderJitDefault;
|
bool shaderJitEnabled = shaderJitDefault;
|
||||||
bool useUbershaders = ubershaderDefault;
|
bool useUbershaders = ubershaderDefault;
|
||||||
bool accelerateShaders = accelerateShadersDefault;
|
bool accelerateShaders = accelerateShadersDefault;
|
||||||
|
bool hashTextures = hashTexturesDefault;
|
||||||
|
|
||||||
bool accurateShaderMul = false;
|
bool accurateShaderMul = false;
|
||||||
bool discordRpcEnabled = false;
|
bool discordRpcEnabled = false;
|
||||||
|
|
||||||
|
@ -58,14 +76,14 @@ struct EmulatorConfig {
|
||||||
bool forceShadergenForLights = true;
|
bool forceShadergenForLights = true;
|
||||||
int lightShadergenThreshold = 1;
|
int lightShadergenThreshold = 1;
|
||||||
|
|
||||||
RendererType rendererType = RendererType::OpenGL;
|
RendererType rendererType = rendererDefault;
|
||||||
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
|
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
|
||||||
|
|
||||||
bool sdCardInserted = true;
|
bool sdCardInserted = true;
|
||||||
bool sdWriteProtected = false;
|
bool sdWriteProtected = false;
|
||||||
bool usePortableBuild = false;
|
bool usePortableBuild = false;
|
||||||
|
|
||||||
bool audioEnabled = false;
|
bool audioEnabled = audioEnabledDefault;
|
||||||
bool vsyncEnabled = true;
|
bool vsyncEnabled = true;
|
||||||
bool aacEnabled = true; // Enable AAC audio?
|
bool aacEnabled = true; // Enable AAC audio?
|
||||||
|
|
||||||
|
@ -77,6 +95,8 @@ struct EmulatorConfig {
|
||||||
// Default to 3% battery to make users suffer
|
// Default to 3% battery to make users suffer
|
||||||
int batteryPercentage = 3;
|
int batteryPercentage = 3;
|
||||||
|
|
||||||
|
LanguageCodes systemLanguage = LanguageCodes::English;
|
||||||
|
|
||||||
// Default ROM path to open in Qt and misc frontends
|
// Default ROM path to open in Qt and misc frontends
|
||||||
std::filesystem::path defaultRomPath = "";
|
std::filesystem::path defaultRomPath = "";
|
||||||
std::filesystem::path filePath;
|
std::filesystem::path filePath;
|
||||||
|
@ -104,4 +124,7 @@ struct EmulatorConfig {
|
||||||
EmulatorConfig(const std::filesystem::path& path);
|
EmulatorConfig(const std::filesystem::path& path);
|
||||||
void load();
|
void load();
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
|
static LanguageCodes languageCodeFromString(std::string inString);
|
||||||
|
static const char* languageCodeToString(LanguageCodes code);
|
||||||
};
|
};
|
|
@ -181,5 +181,7 @@ class CPU {
|
||||||
void addTicks(u64 ticks) { env.AddTicks(ticks); }
|
void addTicks(u64 ticks) { env.AddTicks(ticks); }
|
||||||
|
|
||||||
void clearCache() { jit->ClearCache(); }
|
void clearCache() { jit->ClearCache(); }
|
||||||
|
void clearCacheRange(u32 start, u32 size) { jit->InvalidateCacheRange(start, size); }
|
||||||
|
|
||||||
void runFrame();
|
void runFrame();
|
||||||
};
|
};
|
|
@ -7,8 +7,8 @@
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
|
#include "audio/audio_device.hpp"
|
||||||
#include "audio/dsp_core.hpp"
|
#include "audio/dsp_core.hpp"
|
||||||
#include "audio/miniaudio_device.hpp"
|
|
||||||
#include "cheats.hpp"
|
#include "cheats.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "cpu.hpp"
|
#include "cpu.hpp"
|
||||||
|
@ -48,7 +48,7 @@ class Emulator {
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
|
|
||||||
Crypto::AESEngine aesEngine;
|
Crypto::AESEngine aesEngine;
|
||||||
MiniAudioDevice audioDevice;
|
AudioDevice audioDevice;
|
||||||
Cheats cheats;
|
Cheats cheats;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -89,7 +89,6 @@ class Emulator {
|
||||||
~Emulator();
|
~Emulator();
|
||||||
|
|
||||||
void step();
|
void step();
|
||||||
void render();
|
|
||||||
void reset(ReloadOption reload);
|
void reset(ReloadOption reload);
|
||||||
void runFrame();
|
void runFrame();
|
||||||
// Poll the scheduler for events
|
// Poll the scheduler for events
|
||||||
|
@ -127,6 +126,7 @@ class Emulator {
|
||||||
LuaManager& getLua() { return lua; }
|
LuaManager& getLua() { return lua; }
|
||||||
Scheduler& getScheduler() { return scheduler; }
|
Scheduler& getScheduler() { return scheduler; }
|
||||||
Memory& getMemory() { return memory; }
|
Memory& getMemory() { return memory; }
|
||||||
|
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
|
||||||
|
|
||||||
RendererType getRendererType() const { return config.rendererType; }
|
RendererType getRendererType() const { return config.rendererType; }
|
||||||
Renderer* getRenderer() { return gpu.getRenderer(); }
|
Renderer* getRenderer() { return gpu.getRenderer(); }
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct FrontendSettings {
|
||||||
Dark = 2,
|
Dark = 2,
|
||||||
GreetingsCat = 3,
|
GreetingsCat = 3,
|
||||||
Cream = 4,
|
Cream = 4,
|
||||||
|
Oled = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Different panda-themed window icons
|
// Different panda-themed window icons
|
||||||
|
@ -20,6 +21,7 @@ struct FrontendSettings {
|
||||||
Rnap = 2,
|
Rnap = 2,
|
||||||
Rcow = 3,
|
Rcow = 3,
|
||||||
SkyEmu = 4,
|
SkyEmu = 4,
|
||||||
|
Runpog = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
Theme theme = Theme::Dark;
|
Theme theme = Theme::Dark;
|
||||||
|
|
7
include/ios_driver.h
Normal file
7
include/ios_driver.h
Normal 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);
|
|
@ -5,7 +5,7 @@ namespace IPC {
|
||||||
namespace BufferType {
|
namespace BufferType {
|
||||||
enum : std::uint32_t {
|
enum : std::uint32_t {
|
||||||
Send = 1,
|
Send = 1,
|
||||||
Receive,
|
Receive = 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace KernelHandles {
|
||||||
CFG_U, // CFG service (Console & region info)
|
CFG_U, // CFG service (Console & region info)
|
||||||
CFG_I,
|
CFG_I,
|
||||||
CFG_S, // Used by most system apps in lieu of cfg:u
|
CFG_S, // Used by most system apps in lieu of cfg:u
|
||||||
|
CFG_NOR, // Used by system settings app
|
||||||
CSND, // Plays audio directly from PCM samples
|
CSND, // Plays audio directly from PCM samples
|
||||||
DLP_SRVR, // Download Play: Server. Used for network play.
|
DLP_SRVR, // Download Play: Server. Used for network play.
|
||||||
DSP, // DSP service (Used for audio decoding and output)
|
DSP, // DSP service (Used for audio decoding and output)
|
||||||
|
@ -39,9 +40,10 @@ namespace KernelHandles {
|
||||||
NIM_U, // Updates
|
NIM_U, // Updates
|
||||||
NDM, // ?????
|
NDM, // ?????
|
||||||
NS_S, // Nintendo Shell service
|
NS_S, // Nintendo Shell service
|
||||||
|
NWM_EXT, // ?????
|
||||||
NWM_UDS, // Local multiplayer
|
NWM_UDS, // Local multiplayer
|
||||||
NEWS_S, // news:u on steroids
|
NEWS_S, // news:u on steroids
|
||||||
NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does
|
NEWS_U, // This service literally has 1 command (AddNotification)
|
||||||
PTM_U, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
|
PTM_U, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
|
||||||
PTM_SYSM, // PTM system service
|
PTM_SYSM, // PTM system service
|
||||||
PTM_PLAY, // PTM Play service, used for retrieving play history
|
PTM_PLAY, // PTM Play service, used for retrieving play history
|
||||||
|
@ -85,6 +87,8 @@ namespace KernelHandles {
|
||||||
case CECD: return "CECD";
|
case CECD: return "CECD";
|
||||||
case CFG_U: return "CFG:U";
|
case CFG_U: return "CFG:U";
|
||||||
case CFG_I: return "CFG:I";
|
case CFG_I: return "CFG:I";
|
||||||
|
case CFG_S: return "CFG:S";
|
||||||
|
case CFG_NOR: return "CFG:NOR";
|
||||||
case CSND: return "CSND";
|
case CSND: return "CSND";
|
||||||
case DSP: return "DSP";
|
case DSP: return "DSP";
|
||||||
case DLP_SRVR: return "DLP::SRVR";
|
case DLP_SRVR: return "DLP::SRVR";
|
||||||
|
@ -102,6 +106,7 @@ namespace KernelHandles {
|
||||||
case NDM: return "NDM";
|
case NDM: return "NDM";
|
||||||
case NEWS_S: return "NEWS_S";
|
case NEWS_S: return "NEWS_S";
|
||||||
case NEWS_U: return "NEWS_U";
|
case NEWS_U: return "NEWS_U";
|
||||||
|
case NWM_EXT: return "nwm::EXT";
|
||||||
case NWM_UDS: return "nwm::UDS";
|
case NWM_UDS: return "nwm::UDS";
|
||||||
case NFC: return "NFC";
|
case NFC: return "NFC";
|
||||||
case NIM_AOC: return "NIM:AOC";
|
case NIM_AOC: return "NIM:AOC";
|
||||||
|
@ -109,6 +114,7 @@ namespace KernelHandles {
|
||||||
case PTM_U: return "PTM:U";
|
case PTM_U: return "PTM:U";
|
||||||
case PTM_SYSM: return "PTM:SYSM";
|
case PTM_SYSM: return "PTM:SYSM";
|
||||||
case PTM_PLAY: return "PTM:PLAY";
|
case PTM_PLAY: return "PTM:PLAY";
|
||||||
|
case PTM_GETS: return "PTM:GETS";
|
||||||
case SOC: return "SOC";
|
case SOC: return "SOC";
|
||||||
case SSL: return "SSL";
|
case SSL: return "SSL";
|
||||||
case Y2R: return "Y2R";
|
case Y2R: return "Y2R";
|
||||||
|
|
|
@ -175,6 +175,8 @@ public:
|
||||||
void svcSignalEvent();
|
void svcSignalEvent();
|
||||||
void svcSetTimer();
|
void svcSetTimer();
|
||||||
void svcSleepThread();
|
void svcSleepThread();
|
||||||
|
void svcInvalidateInstructionCacheRange();
|
||||||
|
void svcInvalidateEntireInstructionCache();
|
||||||
void connectToPort();
|
void connectToPort();
|
||||||
void outputDebugString();
|
void outputDebugString();
|
||||||
void waitSynchronization1();
|
void waitSynchronization1();
|
||||||
|
@ -250,4 +252,6 @@ public:
|
||||||
|
|
||||||
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
|
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
|
||||||
void clearInstructionCache();
|
void clearInstructionCache();
|
||||||
|
void clearInstructionCacheRange(u32 start, u32 size);
|
||||||
|
u32 getSharedFontVaddr();
|
||||||
};
|
};
|
|
@ -19,7 +19,7 @@ struct ResourceLimitValues {
|
||||||
// APPLICATION resource limit
|
// APPLICATION resource limit
|
||||||
static constexpr ResourceLimitValues appResourceLimits = {
|
static constexpr ResourceLimitValues appResourceLimits = {
|
||||||
.maxPriority = 0x18,
|
.maxPriority = 0x18,
|
||||||
.maxCommit = 0x4000000,
|
.maxCommit = 64_MB + 16_MB, // We're currently giving 80MB to all apps. TODO: Implement extended memory properly
|
||||||
.maxThreads = 0x20,
|
.maxThreads = 0x20,
|
||||||
.maxEvents = 0x20,
|
.maxEvents = 0x20,
|
||||||
.maxMutexes = 0x20,
|
.maxMutexes = 0x20,
|
||||||
|
@ -33,7 +33,7 @@ static constexpr ResourceLimitValues appResourceLimits = {
|
||||||
// SYS_APPLET resource limit
|
// SYS_APPLET resource limit
|
||||||
static constexpr ResourceLimitValues sysAppletResourceLimits = {
|
static constexpr ResourceLimitValues sysAppletResourceLimits = {
|
||||||
.maxPriority = 0x4,
|
.maxPriority = 0x4,
|
||||||
.maxCommit = 0x5E00000,
|
.maxCommit = 0x5E00000 - 16_MB,
|
||||||
.maxThreads = 0x1D,
|
.maxThreads = 0x1D,
|
||||||
.maxEvents = 0xB,
|
.maxEvents = 0xB,
|
||||||
.maxMutexes = 0x8,
|
.maxMutexes = 0x8,
|
||||||
|
|
|
@ -132,7 +132,7 @@ public:
|
||||||
static constexpr u32 totalPageCount = 1 << (32 - pageShift);
|
static constexpr u32 totalPageCount = 1 << (32 - pageShift);
|
||||||
|
|
||||||
static constexpr u32 FCRAM_SIZE = u32(128_MB);
|
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_PAGE_COUNT = FCRAM_SIZE / pageSize;
|
||||||
static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize;
|
static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize;
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,12 @@ class Renderer {
|
||||||
u32 outputWindowWidth = 400;
|
u32 outputWindowWidth = 400;
|
||||||
u32 outputWindowHeight = 240 * 2;
|
u32 outputWindowHeight = 240 * 2;
|
||||||
|
|
||||||
|
// Should hw renderers hash textures? Stored separately from emulatorConfig because we'll be accessing it constantly, might be merged eventually
|
||||||
|
bool hashTextures = false;
|
||||||
|
|
||||||
EmulatorConfig* emulatorConfig = nullptr;
|
EmulatorConfig* emulatorConfig = nullptr;
|
||||||
|
|
||||||
|
void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap);
|
||||||
public:
|
public:
|
||||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||||
virtual ~Renderer();
|
virtual ~Renderer();
|
||||||
|
@ -81,6 +85,14 @@ class Renderer {
|
||||||
virtual std::string getUbershader() { return ""; }
|
virtual std::string getUbershader() { return ""; }
|
||||||
virtual void setUbershader(const std::string& shader) {}
|
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.
|
// 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
|
// 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.
|
// ubershaders and shadergen, and so on.
|
||||||
|
@ -114,4 +126,5 @@ class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setConfig(EmulatorConfig* config) { emulatorConfig = config; }
|
void setConfig(EmulatorConfig* config) { emulatorConfig = config; }
|
||||||
|
void setHashTextures(bool setting) { hashTextures = setting; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information
|
// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
struct Driver {
|
struct Driver {
|
||||||
|
bool usingGLES = false;
|
||||||
bool supportsExtFbFetch = false;
|
bool supportsExtFbFetch = false;
|
||||||
bool supportsArmFbFetch = false;
|
bool supportsArmFbFetch = false;
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,7 @@ class RendererGL final : public Renderer {
|
||||||
void initGraphicsContextInternal();
|
void initGraphicsContextInternal();
|
||||||
|
|
||||||
void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
|
void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
|
||||||
|
void compileDisplayShader();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
|
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
|
||||||
|
@ -176,6 +177,7 @@ class RendererGL final : public Renderer {
|
||||||
virtual std::string getUbershader() override;
|
virtual std::string getUbershader() override;
|
||||||
virtual void setUbershader(const std::string& shader) override;
|
virtual void setUbershader(const std::string& shader) override;
|
||||||
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) 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);
|
std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "PICA/pica_hash.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include "boost/icl/interval.hpp"
|
#include "boost/icl/interval.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
@ -11,8 +13,12 @@ template <typename T>
|
||||||
using Interval = boost::icl::right_open_interval<T>;
|
using Interval = boost::icl::right_open_interval<T>;
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
|
using Hash = PICAHash::HashType;
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||||
|
Hash hash = Hash(0);
|
||||||
|
|
||||||
PICA::TextureFmt format;
|
PICA::TextureFmt format;
|
||||||
OpenGL::uvec2 size;
|
OpenGL::uvec2 size;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
@ -26,7 +32,6 @@ struct Texture {
|
||||||
|
|
||||||
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
|
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
|
||||||
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
// Check if start and end are valid here
|
// Check if start and end are valid here
|
||||||
range = Interval<u32>(loc, (u32)endLoc);
|
range = Interval<u32>(loc, (u32)endLoc);
|
||||||
|
@ -35,8 +40,7 @@ struct Texture {
|
||||||
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
||||||
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
||||||
bool matches(Texture& other) {
|
bool matches(Texture& other) {
|
||||||
return location == other.location && format == other.format &&
|
return location == other.location && hash == other.hash && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
|
||||||
size.x() == other.size.x() && size.y() == other.size.y();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void allocate();
|
void allocate();
|
||||||
|
@ -54,9 +58,7 @@ struct Texture {
|
||||||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||||
|
|
||||||
// Returns the format of this texture as a string
|
// Returns the format of this texture as a string
|
||||||
std::string_view formatToString() {
|
std::string_view formatToString() { return PICA::textureFormatToString(format); }
|
||||||
return PICA::textureFormatToString(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
||||||
// TODO: Make hasAlpha a template parameter
|
// TODO: Make hasAlpha a template parameter
|
||||||
|
|
|
@ -57,7 +57,7 @@ namespace Metal {
|
||||||
} else if (std::is_same<Format_t, PICA::DepthFmt>::value) {
|
} else if (std::is_same<Format_t, PICA::DepthFmt>::value) {
|
||||||
pixelFormat = PICA::toMTLPixelFormatDepth((PICA::DepthFmt)format);
|
pixelFormat = PICA::toMTLPixelFormatDepth((PICA::DepthFmt)format);
|
||||||
} else {
|
} else {
|
||||||
panic("Invalid format type");
|
Helpers::panic("Invalid format type");
|
||||||
}
|
}
|
||||||
|
|
||||||
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
|
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
|
||||||
|
|
|
@ -4,22 +4,28 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "PICA/pica_hash.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include "boost/icl/interval.hpp"
|
#include "boost/icl/interval.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "math_util.hpp"
|
#include "math_util.hpp"
|
||||||
#include "opengl.hpp"
|
|
||||||
#include "renderer_mtl/pica_to_mtl.hpp"
|
#include "renderer_mtl/pica_to_mtl.hpp"
|
||||||
|
// TODO: remove dependency on OpenGL
|
||||||
|
#include "opengl.hpp"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using Interval = boost::icl::right_open_interval<T>;
|
using Interval = boost::icl::right_open_interval<T>;
|
||||||
|
|
||||||
namespace Metal {
|
namespace Metal {
|
||||||
struct Texture {
|
struct Texture {
|
||||||
|
using Hash = PICAHash::HashType;
|
||||||
|
|
||||||
MTL::Device* device;
|
MTL::Device* device;
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||||
|
Hash hash = Hash(0);
|
||||||
|
|
||||||
PICA::TextureFmt format;
|
PICA::TextureFmt format;
|
||||||
OpenGL::uvec2 size;
|
OpenGL::uvec2 size;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
@ -27,7 +33,8 @@ namespace Metal {
|
||||||
// Range of VRAM taken up by buffer
|
// Range of VRAM taken up by buffer
|
||||||
Interval<u32> range;
|
Interval<u32> range;
|
||||||
|
|
||||||
PICA::PixelFormatInfo formatInfo;
|
PICA::MTLPixelFormatInfo formatInfo;
|
||||||
|
MTL::Texture* base = nullptr;
|
||||||
MTL::Texture* texture = nullptr;
|
MTL::Texture* texture = nullptr;
|
||||||
MTL::SamplerState* sampler = nullptr;
|
MTL::SamplerState* sampler = nullptr;
|
||||||
|
|
||||||
|
@ -43,7 +50,8 @@ namespace Metal {
|
||||||
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
||||||
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
||||||
bool matches(Texture& other) {
|
bool matches(Texture& other) {
|
||||||
return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
|
return location == other.location && hash == other.hash && format == other.format && size.x() == other.size.x() &&
|
||||||
|
size.y() == other.size.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
void allocate();
|
void allocate();
|
||||||
|
@ -52,22 +60,7 @@ namespace Metal {
|
||||||
void free();
|
void free();
|
||||||
u64 sizeInBytes();
|
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
|
// Returns the format of this texture as a string
|
||||||
std::string_view formatToString() { return PICA::textureFormatToString(format); }
|
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
|
} // namespace Metal
|
||||||
|
|
|
@ -3,31 +3,28 @@
|
||||||
#include <Metal/Metal.hpp>
|
#include <Metal/Metal.hpp>
|
||||||
|
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
|
// TODO: remove dependency on OpenGL
|
||||||
|
#include "opengl.hpp"
|
||||||
|
|
||||||
namespace PICA {
|
namespace PICA {
|
||||||
struct PixelFormatInfo {
|
struct MTLPixelFormatInfo {
|
||||||
MTL::PixelFormat pixelFormat;
|
MTL::PixelFormat pixelFormat;
|
||||||
size_t bytesPerTexel;
|
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] = {
|
extern MTLPixelFormatInfo mtlPixelFormatInfos[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
|
|
||||||
};
|
|
||||||
|
|
||||||
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) {
|
inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
@ -35,7 +32,11 @@ namespace PICA {
|
||||||
case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm;
|
case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm;
|
||||||
case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm?
|
case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm?
|
||||||
case ColorFmt::RGB565: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatB5G6R5Unorm?
|
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;
|
case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,11 +42,13 @@ class RendererMTL final : public Renderer {
|
||||||
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
|
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
virtual void setMTKLayer(void* layer) override;
|
||||||
CA::MetalLayer* metalLayer;
|
|
||||||
|
|
||||||
MTL::Device* device;
|
private:
|
||||||
MTL::CommandQueue* commandQueue;
|
CA::MetalLayer* metalLayer = nullptr;
|
||||||
|
|
||||||
|
MTL::Device* device = nullptr;
|
||||||
|
MTL::CommandQueue* commandQueue = nullptr;
|
||||||
|
|
||||||
Metal::CommandEncoder commandEncoder;
|
Metal::CommandEncoder commandEncoder;
|
||||||
|
|
||||||
|
@ -98,6 +100,7 @@ class RendererMTL final : public Renderer {
|
||||||
void endRenderPass() {
|
void endRenderPass() {
|
||||||
if (renderCommandEncoder) {
|
if (renderCommandEncoder) {
|
||||||
renderCommandEncoder->endEncoding();
|
renderCommandEncoder->endEncoding();
|
||||||
|
renderCommandEncoder->release();
|
||||||
renderCommandEncoder = nullptr;
|
renderCommandEncoder = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
include/renderer_mtl/texture_decoder.hpp
Normal file
24
include/renderer_mtl/texture_decoder.hpp
Normal 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);
|
|
@ -9,8 +9,8 @@
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
|
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
|
||||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
|
vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType,
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
|
const vk::DebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
|
||||||
);
|
);
|
||||||
|
|
||||||
void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...);
|
void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
@ -11,6 +12,8 @@ class CFGService {
|
||||||
using Handle = HorizonHandle;
|
using Handle = HorizonHandle;
|
||||||
|
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
const EmulatorConfig& settings;
|
||||||
|
|
||||||
CountryCodes country = CountryCodes::US; // Default to USA
|
CountryCodes country = CountryCodes::US; // Default to USA
|
||||||
MAKE_LOG_FUNCTION(log, cfgLogger)
|
MAKE_LOG_FUNCTION(log, cfgLogger)
|
||||||
|
|
||||||
|
@ -18,8 +21,9 @@ class CFGService {
|
||||||
|
|
||||||
// Service functions
|
// Service functions
|
||||||
void getConfigInfoBlk2(u32 messagePointer);
|
void getConfigInfoBlk2(u32 messagePointer);
|
||||||
void getConfigInfoBlk8(u32 messagePointer);
|
void getConfigInfoBlk8(u32 messagePointer, u32 commandWord);
|
||||||
void getCountryCodeID(u32 messagePointer);
|
void getCountryCodeID(u32 messagePointer);
|
||||||
|
void getCountryCodeString(u32 messagePointer);
|
||||||
void getLocalFriendCodeSeed(u32 messagePointer);
|
void getLocalFriendCodeSeed(u32 messagePointer);
|
||||||
void getRegionCanadaUSA(u32 messagePointer);
|
void getRegionCanadaUSA(u32 messagePointer);
|
||||||
void getSystemModel(u32 messagePointer);
|
void getSystemModel(u32 messagePointer);
|
||||||
|
@ -29,6 +33,11 @@ class CFGService {
|
||||||
void setConfigInfoBlk4(u32 messagePointer);
|
void setConfigInfoBlk4(u32 messagePointer);
|
||||||
void updateConfigNANDSavegame(u32 messagePointer);
|
void updateConfigNANDSavegame(u32 messagePointer);
|
||||||
void translateCountryInfo(u32 messagePointer);
|
void translateCountryInfo(u32 messagePointer);
|
||||||
|
void isFangateSupported(u32 messagePointer);
|
||||||
|
|
||||||
|
// cfg:nor functions
|
||||||
|
void norInitialize(u32 messagePointer);
|
||||||
|
void norReadData(u32 messagePointer);
|
||||||
|
|
||||||
void getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask);
|
void getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask);
|
||||||
|
|
||||||
|
@ -40,7 +49,7 @@ class CFGService {
|
||||||
NOR, // cfg:nor
|
NOR, // cfg:nor
|
||||||
};
|
};
|
||||||
|
|
||||||
CFGService(Memory& mem) : mem(mem) {}
|
CFGService(Memory& mem, const EmulatorConfig& settings) : mem(mem), settings(settings) {}
|
||||||
void reset();
|
void reset();
|
||||||
void handleSyncRequest(u32 messagePointer, Type type);
|
void handleSyncRequest(u32 messagePointer, Type type);
|
||||||
};
|
};
|
|
@ -6,9 +6,6 @@
|
||||||
#include "result/result.hpp"
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class LCDService {
|
class LCDService {
|
||||||
using Handle = HorizonHandle;
|
|
||||||
|
|
||||||
Handle handle = KernelHandles::LCD;
|
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
MAKE_LOG_FUNCTION(log, gspLCDLogger)
|
MAKE_LOG_FUNCTION(log, gspLCDLogger)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ class NSService {
|
||||||
// Service commands
|
// Service commands
|
||||||
void launchTitle(u32 messagePointer);
|
void launchTitle(u32 messagePointer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
S, // ns:s
|
S, // ns:s
|
||||||
P, // ns:p
|
P, // ns:p
|
||||||
|
|
|
@ -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.
|
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?
|
# 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.
|
- 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.
|
- [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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [Tanuki3DS](https://github.com/burhanr13/Tanuki3DS/): A new 3DS emulator for MacOS and Linux
|
||||||
# Support
|
# 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)
|
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)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "toml.hpp"
|
#include "toml.hpp"
|
||||||
|
@ -26,6 +27,7 @@ void EmulatorConfig::load() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("Loading existing configuration file %s\n", path.string().c_str());
|
||||||
toml::value data;
|
toml::value data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -45,6 +47,7 @@ void EmulatorConfig::load() {
|
||||||
defaultRomPath = toml::find_or<std::string>(general, "DefaultRomPath", "");
|
defaultRomPath = toml::find_or<std::string>(general, "DefaultRomPath", "");
|
||||||
|
|
||||||
printAppVersion = toml::find_or<toml::boolean>(general, "PrintAppVersion", true);
|
printAppVersion = toml::find_or<toml::boolean>(general, "PrintAppVersion", true);
|
||||||
|
systemLanguage = languageCodeFromString(toml::find_or<std::string>(general, "SystemLanguage", "en"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,14 +72,14 @@ void EmulatorConfig::load() {
|
||||||
auto gpu = gpuResult.unwrap();
|
auto gpu = gpuResult.unwrap();
|
||||||
|
|
||||||
// Get renderer
|
// 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);
|
auto configRendererType = Renderer::typeFromString(rendererName);
|
||||||
|
|
||||||
if (configRendererType.has_value()) {
|
if (configRendererType.has_value()) {
|
||||||
rendererType = configRendererType.value();
|
rendererType = configRendererType.value();
|
||||||
} else {
|
} else {
|
||||||
Helpers::warn("Invalid renderer specified: %s\n", rendererName.c_str());
|
Helpers::warn("Invalid renderer specified: %s\n", rendererName.c_str());
|
||||||
rendererType = RendererType::OpenGL;
|
rendererType = rendererDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault);
|
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault);
|
||||||
|
@ -87,6 +90,7 @@ void EmulatorConfig::load() {
|
||||||
|
|
||||||
forceShadergenForLights = toml::find_or<toml::boolean>(gpu, "ForceShadergenForLighting", true);
|
forceShadergenForLights = toml::find_or<toml::boolean>(gpu, "ForceShadergenForLighting", true);
|
||||||
lightShadergenThreshold = toml::find_or<toml::integer>(gpu, "ShadergenLightThreshold", 1);
|
lightShadergenThreshold = toml::find_or<toml::integer>(gpu, "ShadergenLightThreshold", 1);
|
||||||
|
hashTextures = toml::find_or<toml::boolean>(gpu, "HashTextures", hashTexturesDefault);
|
||||||
enableRenderdoc = toml::find_or<toml::boolean>(gpu, "EnableRenderdoc", false);
|
enableRenderdoc = toml::find_or<toml::boolean>(gpu, "EnableRenderdoc", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +103,7 @@ void EmulatorConfig::load() {
|
||||||
auto dspCoreName = toml::find_or<std::string>(audio, "DSPEmulation", "HLE");
|
auto dspCoreName = toml::find_or<std::string>(audio, "DSPEmulation", "HLE");
|
||||||
dspType = Audio::DSPCore::typeFromString(dspCoreName);
|
dspType = Audio::DSPCore::typeFromString(dspCoreName);
|
||||||
|
|
||||||
audioEnabled = toml::find_or<toml::boolean>(audio, "EnableAudio", false);
|
audioEnabled = toml::find_or<toml::boolean>(audio, "EnableAudio", audioEnabledDefault);
|
||||||
aacEnabled = toml::find_or<toml::boolean>(audio, "EnableAACAudio", true);
|
aacEnabled = toml::find_or<toml::boolean>(audio, "EnableAACAudio", true);
|
||||||
printDSPFirmware = toml::find_or<toml::boolean>(audio, "PrintDSPFirmware", false);
|
printDSPFirmware = toml::find_or<toml::boolean>(audio, "PrintDSPFirmware", false);
|
||||||
|
|
||||||
|
@ -169,6 +173,7 @@ void EmulatorConfig::save() {
|
||||||
data["General"]["UsePortableBuild"] = usePortableBuild;
|
data["General"]["UsePortableBuild"] = usePortableBuild;
|
||||||
data["General"]["DefaultRomPath"] = defaultRomPath.string();
|
data["General"]["DefaultRomPath"] = defaultRomPath.string();
|
||||||
data["General"]["PrintAppVersion"] = printAppVersion;
|
data["General"]["PrintAppVersion"] = printAppVersion;
|
||||||
|
data["General"]["SystemLanguage"] = languageCodeToString(systemLanguage);
|
||||||
|
|
||||||
data["Window"]["AppVersionOnWindow"] = windowSettings.showAppVersion;
|
data["Window"]["AppVersionOnWindow"] = windowSettings.showAppVersion;
|
||||||
data["Window"]["RememberWindowPosition"] = windowSettings.rememberPosition;
|
data["Window"]["RememberWindowPosition"] = windowSettings.rememberPosition;
|
||||||
|
@ -186,6 +191,7 @@ void EmulatorConfig::save() {
|
||||||
data["GPU"]["ShadergenLightThreshold"] = lightShadergenThreshold;
|
data["GPU"]["ShadergenLightThreshold"] = lightShadergenThreshold;
|
||||||
data["GPU"]["AccelerateShaders"] = accelerateShaders;
|
data["GPU"]["AccelerateShaders"] = accelerateShaders;
|
||||||
data["GPU"]["EnableRenderdoc"] = enableRenderdoc;
|
data["GPU"]["EnableRenderdoc"] = enableRenderdoc;
|
||||||
|
data["GPU"]["HashTextures"] = hashTextures;
|
||||||
|
|
||||||
data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType));
|
data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType));
|
||||||
data["Audio"]["EnableAudio"] = audioEnabled;
|
data["Audio"]["EnableAudio"] = audioEnabled;
|
||||||
|
@ -232,3 +238,33 @@ const char* AudioDeviceConfig::volumeCurveToString(AudioDeviceConfig::VolumeCurv
|
||||||
default: return "cubic";
|
default: return "cubic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LanguageCodes EmulatorConfig::languageCodeFromString(std::string inString) { // Transform to lower-case to make the setting case-insensitive
|
||||||
|
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
|
static const std::unordered_map<std::string, LanguageCodes> map = {
|
||||||
|
{"ja", LanguageCodes::Japanese}, {"en", LanguageCodes::English}, {"fr", LanguageCodes::French}, {"de", LanguageCodes::German},
|
||||||
|
{"it", LanguageCodes::Italian}, {"es", LanguageCodes::Spanish}, {"zh", LanguageCodes::Chinese}, {"ko", LanguageCodes::Korean},
|
||||||
|
{"nl", LanguageCodes::Dutch}, {"pt", LanguageCodes::Portuguese}, {"ru", LanguageCodes::Russian}, {"tw", LanguageCodes::Taiwanese},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto search = map.find(inString); search != map.end()) {
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to English if no language code in our map matches
|
||||||
|
return LanguageCodes::English;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* EmulatorConfig::languageCodeToString(LanguageCodes code) {
|
||||||
|
static constexpr std::array<const char*, 12> codes = {
|
||||||
|
"ja", "en", "fr", "de", "it", "es", "zh", "ko", "nl", "pt", "ru", "tw",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invalid country code, return english
|
||||||
|
if (static_cast<u32>(code) > static_cast<u32>(LanguageCodes::Taiwanese)) {
|
||||||
|
return "en";
|
||||||
|
} else {
|
||||||
|
return codes[static_cast<u32>(code)];
|
||||||
|
}
|
||||||
|
}
|
|
@ -284,7 +284,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VertexShaderOpDescriptorIndex: {
|
case VertexShaderOpDescriptorIndex: {
|
||||||
shaderUnit.vs.setOpDescriptorIndex(value);
|
shaderUnit.vs.setOpDescriptorIndex(newValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case VertexBoolUniform: {
|
case VertexBoolUniform: {
|
||||||
shaderUnit.vs.uploadBoolUniform(value & 0xffff);
|
shaderUnit.vs.uploadBoolUniform(newValue & 0xffff);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
case VertexIntUniform1:
|
case VertexIntUniform1:
|
||||||
case VertexIntUniform2:
|
case VertexIntUniform2:
|
||||||
case VertexIntUniform3: {
|
case VertexIntUniform3: {
|
||||||
shaderUnit.vs.uploadIntUniform(index - VertexIntUniform0, value);
|
shaderUnit.vs.uploadIntUniform(index - VertexIntUniform0, newValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case VertexShaderEntrypoint: {
|
case VertexShaderEntrypoint: {
|
||||||
shaderUnit.vs.entrypoint = value & 0xffff;
|
shaderUnit.vs.entrypoint = newValue & 0xffff;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,13 +336,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
break;
|
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
|
// 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
|
// Several games are known to do this, including New Super Mario Bros 2 and Super Mario 3D Land
|
||||||
case CmdBufTrigger0:
|
case CmdBufTrigger0:
|
||||||
case CmdBufTrigger1: {
|
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)
|
int bufferIndex = index - CmdBufTrigger0; // Index of the command buffer to execute (0 or 1)
|
||||||
u32 addr = (regs[CmdBufAddr0 + bufferIndex] & 0xfffffff) << 3;
|
u32 addr = (regs[CmdBufAddr0 + bufferIndex] & 0xfffffff) << 3;
|
||||||
u32 size = (regs[CmdBufSize0 + bufferIndex] & 0xfffff) << 3;
|
u32 size = (regs[CmdBufSize0 + bufferIndex] & 0xfffff) << 3;
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings)
|
MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
|
||||||
: initialized(false), running(false), samples(nullptr), audioSettings(audioSettings) {}
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
void MiniAudioDevice::init(Samples& samples, bool safe) {
|
void MiniAudioDevice::init(Samples& samples, bool safe) {
|
||||||
this->samples = &samples;
|
this->samples = &samples;
|
||||||
|
|
|
@ -69,6 +69,10 @@ void Kernel::serviceSVC(u32 svc) {
|
||||||
case 0x3A: getResourceLimitCurrentValues(); break;
|
case 0x3A: getResourceLimitCurrentValues(); break;
|
||||||
case 0x3B: getThreadContext(); break;
|
case 0x3B: getThreadContext(); break;
|
||||||
case 0x3D: outputDebugString(); 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;
|
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::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 {
|
namespace SystemInfoType {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
|
|
|
@ -122,7 +122,10 @@ void Kernel::mapMemoryBlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KernelHandles::isSharedMemHandle(block)) {
|
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
|
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
|
||||||
|
|
||||||
// Pass pointer to shared memory to the appropriate service
|
// Pass pointer to shared memory to the appropriate service
|
||||||
|
@ -216,3 +219,8 @@ void Kernel::unmapMemoryBlock() {
|
||||||
Helpers::warn("Stubbed svcUnmapMemoryBlock!");
|
Helpers::warn("Stubbed svcUnmapMemoryBlock!");
|
||||||
regs[0] = Result::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 Kernel::getSharedFontVaddr() {
|
||||||
|
// Place shared font at the very beginning of system FCRAM
|
||||||
|
return mem.getLinearHeapVaddr() + Memory::FCRAM_APPLICATION_SIZE;
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
#include "PICA/float_types.hpp"
|
#include "PICA/float_types.hpp"
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
#include "PICA/pica_frag_uniforms.hpp"
|
#include "PICA/pica_frag_uniforms.hpp"
|
||||||
|
#include "PICA/pica_hash.hpp"
|
||||||
#include "PICA/pica_simd.hpp"
|
#include "PICA/pica_simd.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include "PICA/shader_decompiler.hpp"
|
#include "PICA/shader_decompiler.hpp"
|
||||||
|
@ -51,17 +52,12 @@ void RendererGL::reset() {
|
||||||
|
|
||||||
gl.useProgram(oldProgram); // Switch to old GL program
|
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() {
|
void RendererGL::initGraphicsContextInternal() {
|
||||||
gl.reset();
|
gl.reset();
|
||||||
|
|
||||||
auto gl_resources = cmrc::RendererGL::get_filesystem();
|
auto gl_resources = cmrc::RendererGL::get_filesystem();
|
||||||
|
|
||||||
auto vertexShaderSource = gl_resources.open("opengl_vertex_shader.vert");
|
auto vertexShaderSource = gl_resources.open("opengl_vertex_shader.vert");
|
||||||
auto fragmentShaderSource = gl_resources.open("opengl_fragment_shader.frag");
|
auto fragmentShaderSource = gl_resources.open("opengl_fragment_shader.frag");
|
||||||
|
|
||||||
|
@ -70,16 +66,7 @@ void RendererGL::initGraphicsContextInternal() {
|
||||||
triangleProgram.create({vert, frag});
|
triangleProgram.create({vert, frag});
|
||||||
initUbershader(triangleProgram);
|
initUbershader(triangleProgram);
|
||||||
|
|
||||||
auto displayVertexShaderSource = gl_resources.open("opengl_display.vert");
|
compileDisplayShader();
|
||||||
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
|
|
||||||
|
|
||||||
// Create stream buffers for vertex, index and uniform buffers
|
// Create stream buffers for vertex, index and uniform buffers
|
||||||
static constexpr usize hwIndexBufferSize = 2_MB;
|
static constexpr usize hwIndexBufferSize = 2_MB;
|
||||||
static constexpr usize hwVertexBufferSize = 16_MB;
|
static constexpr usize hwVertexBufferSize = 16_MB;
|
||||||
|
@ -191,6 +178,7 @@ void RendererGL::initGraphicsContextInternal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
fragShaderGen.setTarget(driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL);
|
||||||
|
|
||||||
// Populate our driver info structure
|
// Populate our driver info structure
|
||||||
driverInfo.supportsExtFbFetch = (GLAD_GL_EXT_shader_framebuffer_fetch != 0);
|
driverInfo.supportsExtFbFetch = (GLAD_GL_EXT_shader_framebuffer_fetch != 0);
|
||||||
|
@ -372,17 +360,29 @@ void RendererGL::bindTexturesToSlots() {
|
||||||
const u32 width = getBits<16, 11>(dim);
|
const u32 width = getBits<16, 11>(dim);
|
||||||
const u32 addr = (regs[ioBase + 4] & 0x0FFFFFFF) << 3;
|
const u32 addr = (regs[ioBase + 4] & 0x0FFFFFFF) << 3;
|
||||||
u32 format = regs[ioBase + (i == 0 ? 13 : 5)] & 0xF;
|
u32 format = regs[ioBase + (i == 0 ? 13 : 5)] & 0xF;
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + i);
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
|
||||||
if (addr != 0) [[likely]] {
|
if (addr != 0) [[likely]] {
|
||||||
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
||||||
|
|
||||||
|
if (hashTextures) {
|
||||||
|
const u8* startPointer = gpu.getPointerPhys<u8>(targetTex.location);
|
||||||
|
const usize sizeInBytes = targetTex.sizeInBytes();
|
||||||
|
|
||||||
|
if (startPointer == nullptr || (sizeInBytes > 0 && gpu.getPointerPhys<u8>(targetTex.location + sizeInBytes - 1) == nullptr))
|
||||||
|
[[unlikely]] {
|
||||||
|
Helpers::warn("Out-of-bounds texture fetch");
|
||||||
|
} else {
|
||||||
|
targetTex.hash = PICAHash::computeHash((const char*)startPointer, sizeInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OpenGL::Texture tex = getTexture(targetTex);
|
OpenGL::Texture tex = getTexture(targetTex);
|
||||||
tex.bind();
|
tex.bind();
|
||||||
} else {
|
} else {
|
||||||
// Mapping a texture from NULL. PICA seems to read the last sampled colour, but for now we will display a black texture instead since it is far easier.
|
// Mapping a texture from NULL. PICA seems to read the last sampled colour, but for now we will display a black texture instead since it
|
||||||
// Games that do this don't really care what it does, they just expect the PICA to not crash, since it doesn't have a PU/MMU and can do all sorts of
|
// is far easier. Games that do this don't really care what it does, they just expect the PICA to not crash, since it doesn't have a
|
||||||
// Weird invalid memory accesses without crashing
|
// PU/MMU and can do all sorts of Weird invalid memory accesses without crashing
|
||||||
blankTexture.bind();
|
blankTexture.bind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -805,6 +805,8 @@ void RendererGL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
|
||||||
shutUpCounter++;
|
shutUpCounter++;
|
||||||
printf("RendererGL::TextureCopy failed to locate src framebuffer!\n");
|
printf("RendererGL::TextureCopy failed to locate src framebuffer!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doSoftwareTextureCopy(inputAddr, outputAddr, copySize, inputWidth, inputGap, outputWidth, outputGap);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -850,9 +852,9 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
|
||||||
|
|
||||||
PICA::FragmentConfig fsConfig(regs);
|
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
|
// 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
|
if (!driverInfo.usingGLES) {
|
||||||
fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0);
|
fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
OpenGL::Shader& fragShader = shaderCache.fragmentShaderCache[fsConfig];
|
OpenGL::Shader& fragShader = shaderCache.fragmentShaderCache[fsConfig];
|
||||||
if (!fragShader.exists()) {
|
if (!fragShader.exists()) {
|
||||||
|
@ -1010,7 +1012,7 @@ bool RendererGL::prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration*
|
||||||
|
|
||||||
std::string picaShaderSource = PICA::ShaderGen::decompileShader(
|
std::string picaShaderSource = PICA::ShaderGen::decompileShader(
|
||||||
shaderUnit.vs, *emulatorConfig, shaderUnit.vs.entrypoint,
|
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
|
// 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 +1158,19 @@ void RendererGL::initUbershader(OpenGL::Program& program) {
|
||||||
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
|
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) {
|
void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) {
|
||||||
u32 buffer = 0; // Vertex buffer index for non-fixed attributes
|
u32 buffer = 0; // Vertex buffer index for non-fixed attributes
|
||||||
u32 attrCount = 0;
|
u32 attrCount = 0;
|
||||||
|
@ -1251,3 +1266,19 @@ 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) {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -1,16 +1,18 @@
|
||||||
#include "renderer_mtl/mtl_texture.hpp"
|
#include "renderer_mtl/mtl_texture.hpp"
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "colour.hpp"
|
#include "colour.hpp"
|
||||||
#include "renderer_mtl/objc_helper.hpp"
|
#include "renderer_mtl/objc_helper.hpp"
|
||||||
|
|
||||||
|
|
||||||
using namespace Helpers;
|
using namespace Helpers;
|
||||||
|
|
||||||
namespace Metal {
|
namespace Metal {
|
||||||
void Texture::allocate() {
|
void Texture::allocate() {
|
||||||
formatInfo = PICA::getPixelFormatInfo(format);
|
formatInfo = PICA::getMTLPixelFormatInfo(format);
|
||||||
|
|
||||||
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
|
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
|
||||||
descriptor->setTextureType(MTL::TextureType2D);
|
descriptor->setTextureType(MTL::TextureType2D);
|
||||||
|
@ -20,11 +22,14 @@ namespace Metal {
|
||||||
descriptor->setUsage(MTL::TextureUsageShaderRead);
|
descriptor->setUsage(MTL::TextureUsageShaderRead);
|
||||||
descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers?
|
descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers?
|
||||||
texture = device->newTexture(descriptor);
|
texture = device->newTexture(descriptor);
|
||||||
texture->setLabel(toNSString(
|
texture->setLabel(toNSString(fmt::format("Base texture {} {}x{}", std::string(PICA::textureFormatToString(format)), size.u(), size.v())));
|
||||||
"Texture " + std::string(PICA::textureFormatToString(format)) + " " + std::to_string(size.u()) + "x" + std::to_string(size.v())
|
|
||||||
));
|
|
||||||
descriptor->release();
|
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);
|
setNewConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +63,11 @@ namespace Metal {
|
||||||
if (texture) {
|
if (texture) {
|
||||||
texture->release();
|
texture->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (base) {
|
||||||
|
base->release();
|
||||||
|
}
|
||||||
|
|
||||||
if (sampler) {
|
if (sampler) {
|
||||||
sampler->release();
|
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) {
|
void Texture::decodeTexture(std::span<const u8> data) {
|
||||||
std::vector<u8> decoded;
|
std::unique_ptr<u8[]> decodedData(new u8[u64(size.u()) * u64(size.v()) * formatInfo.bytesPerTexel]);
|
||||||
decoded.reserve(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
|
// Decode texels line by line
|
||||||
for (u32 v = 0; v < size.v(); v++) {
|
for (u32 v = 0; v < size.v(); v++) {
|
||||||
for (u32 u = 0; u < size.u(); u++) {
|
for (u32 u = 0; u < size.u(); u++) {
|
||||||
if (formatInfo.bytesPerTexel == 1) {
|
formatInfo.decoder(size, u, v, data, decodePtr);
|
||||||
u8 texel = decodeTexelU8(u, v, format, data);
|
decodePtr += formatInfo.bytesPerTexel;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
} // namespace Metal
|
||||||
|
|
62
src/core/renderer_mtl/pica_to_mtl.cpp
Normal file
62
src/core/renderer_mtl/pica_to_mtl.cpp
Normal 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
|
|
@ -9,6 +9,7 @@
|
||||||
#undef NO
|
#undef NO
|
||||||
|
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
|
#include "PICA/pica_hash.hpp"
|
||||||
#include "SDL_metal.h"
|
#include "SDL_metal.h"
|
||||||
|
|
||||||
using namespace PICA;
|
using namespace PICA;
|
||||||
|
@ -30,7 +31,6 @@ PICA::ColorFmt ToColorFormat(u32 format) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MTL::Library* loadLibrary(MTL::Device* device, const cmrc::file& shaderSource) {
|
MTL::Library* loadLibrary(MTL::Device* device, const cmrc::file& shaderSource) {
|
||||||
// MTL::CompileOptions* compileOptions = MTL::CompileOptions::alloc()->init();
|
|
||||||
NS::Error* error = nullptr;
|
NS::Error* error = nullptr;
|
||||||
MTL::Library* library = device->newLibrary(Metal::createDispatchData(shaderSource.begin(), shaderSource.size()), &error);
|
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);
|
// MTL::Library* library = device->newLibrary(NS::String::string(source.c_str(), NS::ASCIIStringEncoding), compileOptions, &error);
|
||||||
|
@ -56,12 +56,18 @@ void RendererMTL::reset() {
|
||||||
colorRenderTargetCache.reset();
|
colorRenderTargetCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererMTL::setMTKLayer(void* layer) {
|
||||||
|
metalLayer = (CA::MetalLayer*)layer;
|
||||||
|
}
|
||||||
|
|
||||||
void RendererMTL::display() {
|
void RendererMTL::display() {
|
||||||
CA::MetalDrawable* drawable = metalLayer->nextDrawable();
|
CA::MetalDrawable* drawable = metalLayer->nextDrawable();
|
||||||
if (!drawable) {
|
if (!drawable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MTL::Texture* texture = drawable->texture();
|
||||||
|
|
||||||
using namespace PICA::ExternalRegs;
|
using namespace PICA::ExternalRegs;
|
||||||
|
|
||||||
// Top screen
|
// Top screen
|
||||||
|
@ -87,13 +93,13 @@ void RendererMTL::display() {
|
||||||
|
|
||||||
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
|
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
|
||||||
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
|
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
|
||||||
colorAttachment->setTexture(drawable->texture());
|
colorAttachment->setTexture(texture);
|
||||||
colorAttachment->setLoadAction(MTL::LoadActionClear);
|
colorAttachment->setLoadAction(MTL::LoadActionClear);
|
||||||
colorAttachment->setClearColor(MTL::ClearColor{0.0f, 0.0f, 0.0f, 1.0f});
|
colorAttachment->setClearColor(MTL::ClearColor{0.0f, 0.0f, 0.0f, 1.0f});
|
||||||
colorAttachment->setStoreAction(MTL::StoreActionStore);
|
colorAttachment->setStoreAction(MTL::StoreActionStore);
|
||||||
|
|
||||||
nextRenderPassName = "Display";
|
nextRenderPassName = "Display";
|
||||||
beginRenderPassIfNeeded(renderPassDescriptor, false, drawable->texture());
|
beginRenderPassIfNeeded(renderPassDescriptor, false, texture);
|
||||||
renderCommandEncoder->setRenderPipelineState(displayPipeline);
|
renderCommandEncoder->setRenderPipelineState(displayPipeline);
|
||||||
renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0);
|
renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0);
|
||||||
|
|
||||||
|
@ -119,17 +125,22 @@ void RendererMTL::display() {
|
||||||
|
|
||||||
// Inform the vertex buffer cache that the frame ended
|
// Inform the vertex buffer cache that the frame ended
|
||||||
vertexBufferCache.endFrame();
|
vertexBufferCache.endFrame();
|
||||||
|
|
||||||
// Release
|
|
||||||
drawable->release();
|
drawable->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererMTL::initGraphicsContext(SDL_Window* window) {
|
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?
|
// TODO: what should be the type of the view?
|
||||||
void* view = SDL_Metal_CreateView(window);
|
void* view = SDL_Metal_CreateView(window);
|
||||||
metalLayer = (CA::MetalLayer*)SDL_Metal_GetLayer(view);
|
metalLayer = (CA::MetalLayer*)SDL_Metal_GetLayer(view);
|
||||||
device = MTL::CreateSystemDefaultDevice();
|
device = MTL::CreateSystemDefaultDevice();
|
||||||
metalLayer->setDevice(device);
|
metalLayer->setDevice(device);
|
||||||
|
#endif
|
||||||
|
checkForMTLPixelFormatSupport(device);
|
||||||
|
|
||||||
commandQueue = device->newCommandQueue();
|
commandQueue = device->newCommandQueue();
|
||||||
|
|
||||||
// Textures
|
// Textures
|
||||||
|
@ -426,7 +437,7 @@ void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
|
||||||
// Find the source surface.
|
// Find the source surface.
|
||||||
auto srcFramebuffer = getColorRenderTarget(inputAddr, PICA::ColorFmt::RGBA8, copyStride, copyHeight, false);
|
auto srcFramebuffer = getColorRenderTarget(inputAddr, PICA::ColorFmt::RGBA8, copyStride, copyHeight, false);
|
||||||
if (!srcFramebuffer) {
|
if (!srcFramebuffer) {
|
||||||
Helpers::warn("RendererMTL::TextureCopy failed to locate src framebuffer!\n");
|
doSoftwareTextureCopy(inputAddr, outputAddr, copySize, inputWidth, inputGap, outputWidth, outputGap);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nextRenderPassName = "Clear before texture copy";
|
nextRenderPassName = "Clear before texture copy";
|
||||||
|
@ -719,6 +730,19 @@ void RendererMTL::bindTexturesToSlots() {
|
||||||
|
|
||||||
if (addr != 0) [[likely]] {
|
if (addr != 0) [[likely]] {
|
||||||
Metal::Texture targetTex(device, addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
Metal::Texture targetTex(device, addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
||||||
|
|
||||||
|
if (hashTextures) {
|
||||||
|
const u8* startPointer = gpu.getPointerPhys<u8>(targetTex.location);
|
||||||
|
const usize sizeInBytes = targetTex.sizeInBytes();
|
||||||
|
|
||||||
|
if (startPointer == nullptr || (sizeInBytes > 0 && gpu.getPointerPhys<u8>(targetTex.location + sizeInBytes - 1) == nullptr))
|
||||||
|
[[unlikely]] {
|
||||||
|
Helpers::warn("Out-of-bounds texture fetch");
|
||||||
|
} else {
|
||||||
|
targetTex.hash = PICAHash::computeHash((const char*)startPointer, sizeInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto tex = getTexture(targetTex);
|
auto tex = getTexture(targetTex);
|
||||||
commandEncoder.setFragmentTexture(tex.texture, i);
|
commandEncoder.setFragmentTexture(tex.texture, i);
|
||||||
commandEncoder.setFragmentSamplerState(tex.sampler ? tex.sampler : nearestSampler, i);
|
commandEncoder.setFragmentSamplerState(tex.sampler ? tex.sampler : nearestSampler, i);
|
||||||
|
|
334
src/core/renderer_mtl/texture_decoder.cpp
Normal file
334
src/core/renderer_mtl/texture_decoder.cpp
Normal 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);
|
||||||
|
}
|
|
@ -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) {
|
void RendererVK::initGraphicsContext(SDL_Window* window) {
|
||||||
targetWindow = window;
|
targetWindow = window;
|
||||||
// Resolve all instance function pointers
|
// Resolve all instance function pointers
|
||||||
static vk::DynamicLoader dl;
|
static VulkanDynamicLoader dl;
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
|
||||||
|
|
||||||
// Create Instance
|
// Create Instance
|
||||||
|
|
|
@ -61,8 +61,8 @@ namespace Vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
|
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
|
||||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
|
vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType,
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
|
const vk::DebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
|
||||||
) {
|
) {
|
||||||
debugMessageCallback(
|
debugMessageCallback(
|
||||||
vk::DebugUtilsMessageSeverityFlagBitsEXT(messageSeverity), vk::DebugUtilsMessageTypeFlagsEXT(messageType), *callbackData
|
vk::DebugUtilsMessageSeverityFlagBitsEXT(messageSeverity), vk::DebugUtilsMessageTypeFlagsEXT(messageType), *callbackData
|
||||||
|
@ -70,7 +70,7 @@ namespace Vulkan {
|
||||||
return VK_FALSE;
|
return VK_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GPU_DEBUG_INFO
|
#ifdef GPU_DEBUG_INFO
|
||||||
void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...) {
|
void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/ac.hpp"
|
#include "services/ac.hpp"
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
|
||||||
namespace ACCommands {
|
namespace ACCommands {
|
||||||
|
@ -36,6 +37,7 @@ void ACService::handleSyncRequest(u32 messagePointer) {
|
||||||
case ACCommands::IsConnected: isConnected(messagePointer); break;
|
case ACCommands::IsConnected: isConnected(messagePointer); break;
|
||||||
case ACCommands::RegisterDisconnectEvent: registerDisconnectEvent(messagePointer); break;
|
case ACCommands::RegisterDisconnectEvent: registerDisconnectEvent(messagePointer); break;
|
||||||
case ACCommands::SetClientVersion: setClientVersion(messagePointer); break;
|
case ACCommands::SetClientVersion: setClientVersion(messagePointer); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
Helpers::warn("AC service requested. Command: %08X\n", command);
|
Helpers::warn("AC service requested. Command: %08X\n", command);
|
||||||
|
|
|
@ -397,7 +397,7 @@ void APTService::setScreencapPostPermission(u32 messagePointer) {
|
||||||
void APTService::getSharedFont(u32 messagePointer) {
|
void APTService::getSharedFont(u32 messagePointer) {
|
||||||
log("APT::GetSharedFont\n");
|
log("APT::GetSharedFont\n");
|
||||||
|
|
||||||
constexpr u32 fontVaddr = 0x18000000;
|
const u32 fontVaddr = kernel.getSharedFontVaddr();
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x44, 2, 2));
|
mem.write32(messagePointer, IPC::responseHeader(0x44, 2, 2));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, fontVaddr);
|
mem.write32(messagePointer + 8, fontVaddr);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/boss.hpp"
|
#include "services/boss.hpp"
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
|
||||||
namespace BOSSCommands {
|
namespace BOSSCommands {
|
||||||
|
@ -39,9 +40,7 @@ namespace BOSSCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void BOSSService::reset() {
|
void BOSSService::reset() { optoutFlag = 0; }
|
||||||
optoutFlag = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOSSService::handleSyncRequest(u32 messagePointer) {
|
void BOSSService::handleSyncRequest(u32 messagePointer) {
|
||||||
const u32 command = mem.read32(messagePointer);
|
const u32 command = mem.read32(messagePointer);
|
||||||
|
@ -200,7 +199,6 @@ void BOSSService::sendProperty(u32 messagePointer) {
|
||||||
// TODO: Should this do anything else?
|
// TODO: Should this do anything else?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BOSSService::receiveProperty(u32 messagePointer) {
|
void BOSSService::receiveProperty(u32 messagePointer) {
|
||||||
const u32 id = mem.read32(messagePointer + 4);
|
const u32 id = mem.read32(messagePointer + 4);
|
||||||
const u32 size = mem.read32(messagePointer + 8);
|
const u32 size = mem.read32(messagePointer + 8);
|
||||||
|
|
|
@ -21,29 +21,52 @@ namespace CFGCommands {
|
||||||
SetConfigInfoBlk4 = 0x04020082,
|
SetConfigInfoBlk4 = 0x04020082,
|
||||||
UpdateConfigNANDSavegame = 0x04030000,
|
UpdateConfigNANDSavegame = 0x04030000,
|
||||||
|
|
||||||
|
GetCountryCodeString = 0x00090040,
|
||||||
|
GetCountryCodeID = 0x000A0040,
|
||||||
|
IsFangateSupported = 0x000B0000,
|
||||||
|
SetConfigInfoBlk4 = 0x04020082,
|
||||||
|
UpdateConfigNANDSavegame = 0x04030000,
|
||||||
GetLocalFriendCodeSeed = 0x04050000,
|
GetLocalFriendCodeSeed = 0x04050000,
|
||||||
SecureInfoGetByte101 = 0x04070000,
|
SecureInfoGetByte101 = 0x04070000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cfg:i commands
|
||||||
|
namespace CFGICommands {
|
||||||
|
enum : u32 {
|
||||||
|
GetConfigInfoBlk8 = 0x08010082,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// cfg:nor commands
|
||||||
|
namespace NORCommands {
|
||||||
|
enum : u32 {
|
||||||
|
Initialize = 0x00010040,
|
||||||
|
ReadData = 0x00050082,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void CFGService::reset() {}
|
void CFGService::reset() {}
|
||||||
|
|
||||||
void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) {
|
void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) {
|
||||||
const u32 command = mem.read32(messagePointer);
|
const u32 command = mem.read32(messagePointer);
|
||||||
|
if (type != Type::NOR) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break;
|
case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break;
|
||||||
|
case CFGCommands::GetCountryCodeString: getCountryCodeString(messagePointer); break;
|
||||||
case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break;
|
case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break;
|
||||||
case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break;
|
case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break;
|
||||||
case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break;
|
case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break;
|
||||||
case CFGCommands::GenHashConsoleUnique: genUniqueConsoleHash(messagePointer); break;
|
case CFGCommands::GenHashConsoleUnique: genUniqueConsoleHash(messagePointer); break;
|
||||||
|
case CFGCommands::IsFangateSupported: isFangateSupported(messagePointer); break;
|
||||||
case CFGCommands::SecureInfoGetRegion: secureInfoGetRegion(messagePointer); break;
|
case CFGCommands::SecureInfoGetRegion: secureInfoGetRegion(messagePointer); break;
|
||||||
case CFGCommands::TranslateCountryInfo: translateCountryInfo(messagePointer); break;
|
case CFGCommands::TranslateCountryInfo: translateCountryInfo(messagePointer); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (type == Type::S) {
|
if (type == Type::S) {
|
||||||
// cfg:s-only functions
|
// cfg:s (and cfg:i) functions only functions
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CFGCommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer); break;
|
case CFGCommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer, command); break;
|
||||||
case CFGCommands::GetLocalFriendCodeSeed: getLocalFriendCodeSeed(messagePointer); break;
|
case CFGCommands::GetLocalFriendCodeSeed: getLocalFriendCodeSeed(messagePointer); break;
|
||||||
case CFGCommands::SecureInfoGetByte101: secureInfoGetByte101(messagePointer); break;
|
case CFGCommands::SecureInfoGetByte101: secureInfoGetByte101(messagePointer); break;
|
||||||
case CFGCommands::SetConfigInfoBlk4: setConfigInfoBlk4(messagePointer); break;
|
case CFGCommands::SetConfigInfoBlk4: setConfigInfoBlk4(messagePointer); break;
|
||||||
|
@ -51,12 +74,28 @@ void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) {
|
||||||
|
|
||||||
default: Helpers::panic("CFG:S service requested. Command: %08X\n", command);
|
default: Helpers::panic("CFG:S service requested. Command: %08X\n", command);
|
||||||
}
|
}
|
||||||
|
} else if (type == Type::I) {
|
||||||
|
switch (command) {
|
||||||
|
case CFGCommands::GetConfigInfoBlk8:
|
||||||
|
case CFGICommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer, command); break;
|
||||||
|
|
||||||
|
default: Helpers::panic("CFG:I service requested. Command: %08X\n", command);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Helpers::panic("CFG service requested. Command: %08X\n", command);
|
Helpers::panic("CFG service requested. Command: %08X\n", command);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// cfg:nor functions
|
||||||
|
switch (command) {
|
||||||
|
case NORCommands::Initialize: norInitialize(messagePointer); break;
|
||||||
|
case NORCommands::ReadData: norReadData(messagePointer); break;
|
||||||
|
|
||||||
|
default: Helpers::panic("CFG:NOR service requested. Command: %08X\n", command);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFGService::getSystemModel(u32 messagePointer) {
|
void CFGService::getSystemModel(u32 messagePointer) {
|
||||||
|
@ -88,14 +127,14 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFGService::getConfigInfoBlk8(u32 messagePointer) {
|
void CFGService::getConfigInfoBlk8(u32 messagePointer, u32 commandWord) {
|
||||||
u32 size = mem.read32(messagePointer + 4);
|
u32 size = mem.read32(messagePointer + 4);
|
||||||
u32 blockID = mem.read32(messagePointer + 8);
|
u32 blockID = mem.read32(messagePointer + 8);
|
||||||
u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to
|
u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to
|
||||||
log("CFG::GetConfigInfoBlk8 (size = %X, block ID = %X, output pointer = %08X)\n", size, blockID, output);
|
log("CFG::GetConfigInfoBlk8 (size = %X, block ID = %X, output pointer = %08X)\n", size, blockID, output);
|
||||||
|
|
||||||
getConfigInfo(output, blockID, size, 0x8);
|
getConfigInfo(output, blockID, size, 0x8);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x401, 1, 2));
|
mem.write32(messagePointer, IPC::responseHeader(commandWord >> 16, 1, 2));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +143,7 @@ void CFGService::getConfigInfo(u32 output, u32 blockID, u32 size, u32 permission
|
||||||
if (size == 1 && blockID == 0x70001) { // Sound output mode
|
if (size == 1 && blockID == 0x70001) { // Sound output mode
|
||||||
mem.write8(output, static_cast<u8>(DSPService::SoundOutputMode::Stereo));
|
mem.write8(output, static_cast<u8>(DSPService::SoundOutputMode::Stereo));
|
||||||
} else if (size == 1 && blockID == 0xA0002) { // System language
|
} else if (size == 1 && blockID == 0xA0002) { // System language
|
||||||
mem.write8(output, static_cast<u8>(LanguageCodes::English));
|
mem.write8(output, static_cast<u8>(settings.systemLanguage));
|
||||||
} else if (size == 4 && blockID == 0xB0000) { // Country info
|
} else if (size == 4 && blockID == 0xB0000) { // Country info
|
||||||
mem.write8(output, 0); // Unknown
|
mem.write8(output, 0); // Unknown
|
||||||
mem.write8(output + 1, 0); // Unknown
|
mem.write8(output + 1, 0); // Unknown
|
||||||
|
@ -178,6 +217,23 @@ void CFGService::getConfigInfo(u32 output, u32 blockID, u32 size, u32 permission
|
||||||
mem.write32(output, 0);
|
mem.write32(output, 0);
|
||||||
} else if (size == 1 && blockID == 0xE0000) {
|
} else if (size == 1 && blockID == 0xE0000) {
|
||||||
mem.write8(output, 0);
|
mem.write8(output, 0);
|
||||||
|
} else if ((size == 512 && blockID == 0xC0002) || (size == 148 && blockID == 0x100001)) {
|
||||||
|
// CTR parental controls block (0xC0002) and TWL parental controls block (0x100001)
|
||||||
|
for (u32 i = 0; i < size; i++) {
|
||||||
|
mem.write8(output + i, 0);
|
||||||
|
}
|
||||||
|
} else if (size == 2 && blockID == 0x100000) {
|
||||||
|
// EULA agreed
|
||||||
|
mem.write8(output, 1); // We have agreed to the EULA
|
||||||
|
mem.write8(output + 1, 1); // EULA version = 1
|
||||||
|
} else if (size == 1 && blockID == 0x100002) {
|
||||||
|
Helpers::warn("Unimplemented TWL country code access");
|
||||||
|
mem.write8(output, 0);
|
||||||
|
} else if (size == 24 && blockID == 0x180001) {
|
||||||
|
// QTM calibration data
|
||||||
|
for (u32 i = 0; i < size; i++) {
|
||||||
|
mem.write8(output + i, 0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID);
|
Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID);
|
||||||
}
|
}
|
||||||
|
@ -262,6 +318,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) {
|
void CFGService::secureInfoGetByte101(u32 messagePointer) {
|
||||||
log("CFG::SecureInfoGetByte101\n");
|
log("CFG::SecureInfoGetByte101\n");
|
||||||
|
|
||||||
|
@ -330,3 +404,27 @@ void CFGService::translateCountryInfo(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, result);
|
mem.write32(messagePointer + 8, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CFGService::isFangateSupported(u32 messagePointer) {
|
||||||
|
log("CFG::IsFangateSupported\n");
|
||||||
|
|
||||||
|
// TODO: What even is fangate?
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
|
||||||
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
mem.write32(messagePointer + 8, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFGService::norInitialize(u32 messagePointer) {
|
||||||
|
log("CFG::NOR::Initialize\n");
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
|
||||||
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFGService::norReadData(u32 messagePointer) {
|
||||||
|
log("CFG::NOR::ReadData\n");
|
||||||
|
Helpers::warn("Unimplemented CFG::NOR::ReadData");
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||||
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/gsp_gpu.hpp"
|
#include "services/gsp_gpu.hpp"
|
||||||
|
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
#include "kernel.hpp"
|
#include "kernel.hpp"
|
||||||
|
@ -166,7 +167,6 @@ void GPUService::readHwRegs(u32 messagePointer) {
|
||||||
u32 dataPointer = initialDataPointer;
|
u32 dataPointer = initialDataPointer;
|
||||||
log("GSP::GPU::ReadHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
|
log("GSP::GPU::ReadHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
|
||||||
|
|
||||||
|
|
||||||
// Check for alignment
|
// Check for alignment
|
||||||
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) {
|
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) {
|
||||||
Helpers::panic("GSP::GPU::ReadHwRegs misalignment");
|
Helpers::panic("GSP::GPU::ReadHwRegs misalignment");
|
||||||
|
@ -236,8 +236,8 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
||||||
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
||||||
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
||||||
|
|
||||||
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
|
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n", ioAddr, size, dataPointer,
|
||||||
ioAddr, size, dataPointer, maskPointer);
|
maskPointer);
|
||||||
|
|
||||||
// Check for alignment
|
// Check for alignment
|
||||||
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3) || (maskPointer & 3)) {
|
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3) || (maskPointer & 3)) {
|
||||||
|
@ -453,22 +453,14 @@ void GPUService::triggerDMARequest(u32* cmd) {
|
||||||
requestInterrupt(GPUInterrupt::DMA);
|
requestInterrupt(GPUInterrupt::DMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUService::flushCacheRegions(u32* cmd) {
|
void GPUService::flushCacheRegions(u32* cmd) { log("GSP::GPU::FlushCacheRegions (Stubbed)\n"); }
|
||||||
log("GSP::GPU::FlushCacheRegions (Stubbed)\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) {
|
void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) {
|
||||||
using namespace PICA::ExternalRegs;
|
using namespace PICA::ExternalRegs;
|
||||||
|
|
||||||
static constexpr std::array<u32, 8> fbAddresses = {
|
static constexpr std::array<u32, 8> fbAddresses = {
|
||||||
Framebuffer0AFirstAddr,
|
Framebuffer0AFirstAddr, Framebuffer0BFirstAddr, Framebuffer1AFirstAddr, Framebuffer1BFirstAddr,
|
||||||
Framebuffer0BFirstAddr,
|
Framebuffer0ASecondAddr, Framebuffer0BSecondAddr, Framebuffer1ASecondAddr, Framebuffer1BSecondAddr,
|
||||||
Framebuffer1AFirstAddr,
|
|
||||||
Framebuffer1BFirstAddr,
|
|
||||||
Framebuffer0ASecondAddr,
|
|
||||||
Framebuffer0BSecondAddr,
|
|
||||||
Framebuffer1ASecondAddr,
|
|
||||||
Framebuffer1BSecondAddr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& regs = gpu.getExtRegisters();
|
auto& regs = gpu.getExtRegisters();
|
||||||
|
@ -478,12 +470,7 @@ void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) {
|
||||||
regs[fbAddresses[fbIndex + 1]] = VaddrToPaddr(info.rightFramebufferVaddr);
|
regs[fbAddresses[fbIndex + 1]] = VaddrToPaddr(info.rightFramebufferVaddr);
|
||||||
|
|
||||||
static constexpr std::array<u32, 6> configAddresses = {
|
static constexpr std::array<u32, 6> configAddresses = {
|
||||||
Framebuffer0Config,
|
Framebuffer0Config, Framebuffer0Select, Framebuffer0Stride, Framebuffer1Config, Framebuffer1Select, Framebuffer1Stride,
|
||||||
Framebuffer0Select,
|
|
||||||
Framebuffer0Stride,
|
|
||||||
Framebuffer1Config,
|
|
||||||
Framebuffer1Select,
|
|
||||||
Framebuffer1Stride,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const u32 configIndex = screenId * 3;
|
const u32 configIndex = screenId * 3;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/gsp_lcd.hpp"
|
#include "services/gsp_lcd.hpp"
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
|
||||||
namespace LCDCommands {
|
namespace LCDCommands {
|
||||||
|
|
|
@ -19,7 +19,11 @@ void HTTPService::handleSyncRequest(u32 messagePointer) {
|
||||||
case HTTPCommands::CreateRootCertChain: createRootCertChain(messagePointer); break;
|
case HTTPCommands::CreateRootCertChain: createRootCertChain(messagePointer); break;
|
||||||
case HTTPCommands::Initialize: initialize(messagePointer); break;
|
case HTTPCommands::Initialize: initialize(messagePointer); break;
|
||||||
case HTTPCommands::RootCertChainAddDefaultCert: rootCertChainAddDefaultCert(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace CROHeader {
|
||||||
NameOffset = 0x084,
|
NameOffset = 0x084,
|
||||||
NextCRO = 0x088,
|
NextCRO = 0x088,
|
||||||
PrevCRO = 0x08C,
|
PrevCRO = 0x08C,
|
||||||
|
FixedSize = 0x98,
|
||||||
OnUnresolved = 0x0AC,
|
OnUnresolved = 0x0AC,
|
||||||
CodeOffset = 0x0B0,
|
CodeOffset = 0x0B0,
|
||||||
DataOffset = 0x0B8,
|
DataOffset = 0x0B8,
|
||||||
|
@ -167,6 +168,10 @@ public:
|
||||||
return mem.read32(croPointer + CROHeader::PrevCRO);
|
return mem.read32(croPointer + CROHeader::PrevCRO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 getFixedSize() {
|
||||||
|
return mem.read32(croPointer + CROHeader::FixedSize);
|
||||||
|
}
|
||||||
|
|
||||||
void setNextCRO(u32 nextCRO) {
|
void setNextCRO(u32 nextCRO) {
|
||||||
mem.write32(croPointer + CROHeader::NextCRO, nextCRO);
|
mem.write32(croPointer + CROHeader::NextCRO, nextCRO);
|
||||||
}
|
}
|
||||||
|
@ -1248,8 +1253,7 @@ void LDRService::initialize(u32 messagePointer) {
|
||||||
Helpers::panic("Failed to rebase CRS");
|
Helpers::panic("Failed to rebase CRS");
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel.clearInstructionCache();
|
kernel.clearInstructionCacheRange(mapVaddr, size);
|
||||||
|
|
||||||
loadedCRS = mapVaddr;
|
loadedCRS = mapVaddr;
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
|
||||||
|
@ -1278,8 +1282,6 @@ void LDRService::linkCRO(u32 messagePointer) {
|
||||||
Helpers::panic("Failed to link CRO");
|
Helpers::panic("Failed to link CRO");
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel.clearInstructionCache();
|
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
@ -1346,8 +1348,7 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
|
||||||
|
|
||||||
// TODO: add fixing
|
// TODO: add fixing
|
||||||
cro.fix(fixLevel);
|
cro.fix(fixLevel);
|
||||||
|
kernel.clearInstructionCacheRange(mapVaddr, size);
|
||||||
kernel.clearInstructionCache();
|
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0));
|
||||||
|
@ -1377,7 +1378,6 @@ void LDRService::unloadCRO(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CRO cro(mem, mapVaddr, true);
|
CRO cro(mem, mapVaddr, true);
|
||||||
|
|
||||||
cro.unregisterCRO(loadedCRS);
|
cro.unregisterCRO(loadedCRS);
|
||||||
|
|
||||||
if (!cro.unlink(loadedCRS)) {
|
if (!cro.unlink(loadedCRS)) {
|
||||||
|
@ -1388,8 +1388,7 @@ void LDRService::unloadCRO(u32 messagePointer) {
|
||||||
Helpers::panic("Failed to unrebase CRO");
|
Helpers::panic("Failed to unrebase CRO");
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel.clearInstructionCache();
|
kernel.clearInstructionCacheRange(mapVaddr, cro.getFixedSize());
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/ptm.hpp"
|
#include "services/ptm.hpp"
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
|
||||||
namespace PTMCommands {
|
namespace PTMCommands {
|
||||||
|
|
|
@ -114,6 +114,7 @@ static std::map<std::string, HorizonHandle> serviceMap = {
|
||||||
{ "cfg:u", KernelHandles::CFG_U },
|
{ "cfg:u", KernelHandles::CFG_U },
|
||||||
{ "cfg:i", KernelHandles::CFG_I },
|
{ "cfg:i", KernelHandles::CFG_I },
|
||||||
{ "cfg:s", KernelHandles::CFG_S },
|
{ "cfg:s", KernelHandles::CFG_S },
|
||||||
|
{ "cfg:nor", KernelHandles::CFG_NOR },
|
||||||
{ "csnd:SND", KernelHandles::CSND },
|
{ "csnd:SND", KernelHandles::CSND },
|
||||||
{ "dlp:SRVR", KernelHandles::DLP_SRVR },
|
{ "dlp:SRVR", KernelHandles::DLP_SRVR },
|
||||||
{ "dsp::DSP", KernelHandles::DSP },
|
{ "dsp::DSP", KernelHandles::DSP },
|
||||||
|
@ -134,6 +135,7 @@ static std::map<std::string, HorizonHandle> serviceMap = {
|
||||||
{ "news:u", KernelHandles::NEWS_U },
|
{ "news:u", KernelHandles::NEWS_U },
|
||||||
{ "nfc:u", KernelHandles::NFC },
|
{ "nfc:u", KernelHandles::NFC },
|
||||||
{ "ns:s", KernelHandles::NS_S },
|
{ "ns:s", KernelHandles::NS_S },
|
||||||
|
{ "nwm::EXT", KernelHandles::NWM_EXT },
|
||||||
{ "nwm::UDS", KernelHandles::NWM_UDS },
|
{ "nwm::UDS", KernelHandles::NWM_UDS },
|
||||||
{ "nim:aoc", KernelHandles::NIM_AOC },
|
{ "nim:aoc", KernelHandles::NIM_AOC },
|
||||||
{ "nim:u", KernelHandles::NIM_U },
|
{ "nim:u", KernelHandles::NIM_U },
|
||||||
|
@ -223,6 +225,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
||||||
case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break;
|
case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break;
|
||||||
case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break;
|
case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break;
|
||||||
case KernelHandles::CFG_S: cfg.handleSyncRequest(messagePointer, CFGService::Type::S); break;
|
case KernelHandles::CFG_S: cfg.handleSyncRequest(messagePointer, CFGService::Type::S); break;
|
||||||
|
case KernelHandles::CFG_NOR: cfg.handleSyncRequest(messagePointer, CFGService::Type::NOR); break;
|
||||||
case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break;
|
case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break;
|
||||||
case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break;
|
case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break;
|
||||||
case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
|
case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
|
||||||
|
@ -246,6 +249,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
||||||
case KernelHandles::PTM_PLAY: ptm.handleSyncRequest(messagePointer, PTMService::Type::PLAY); break;
|
case KernelHandles::PTM_PLAY: ptm.handleSyncRequest(messagePointer, PTMService::Type::PLAY); break;
|
||||||
case KernelHandles::PTM_SYSM: ptm.handleSyncRequest(messagePointer, PTMService::Type::SYSM); break;
|
case KernelHandles::PTM_SYSM: ptm.handleSyncRequest(messagePointer, PTMService::Type::SYSM); break;
|
||||||
case KernelHandles::PTM_U: ptm.handleSyncRequest(messagePointer, PTMService::Type::U); break;
|
case KernelHandles::PTM_U: ptm.handleSyncRequest(messagePointer, PTMService::Type::U); break;
|
||||||
|
case KernelHandles::PTM_GETS: ptm.handleSyncRequest(messagePointer, PTMService::Type::GETS); break;
|
||||||
case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break;
|
case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break;
|
||||||
case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break;
|
case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break;
|
||||||
case KernelHandles::Y2R: y2r.handleSyncRequest(messagePointer); break;
|
case KernelHandles::Y2R: y2r.handleSyncRequest(messagePointer); break;
|
||||||
|
|
|
@ -32,18 +32,14 @@ Emulator::Emulator()
|
||||||
dspService.setDSPCore(dsp.get());
|
dspService.setDSPCore(dsp.get());
|
||||||
|
|
||||||
audioDevice.init(dsp->getSamples());
|
audioDevice.init(dsp->getSamples());
|
||||||
setAudioEnabled(config.audioEnabled);
|
|
||||||
|
|
||||||
if (Renderdoc::isSupported() && config.enableRenderdoc) {
|
|
||||||
loadRenderdoc();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
||||||
if (config.discordRpcEnabled) {
|
if (config.discordRpcEnabled) {
|
||||||
discordRpc.init();
|
discordRpc.init();
|
||||||
updateDiscord();
|
updateDiscord();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
reloadSettings();
|
||||||
reset(ReloadOption::NoReload);
|
reset(ReloadOption::NoReload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,13 +101,18 @@ std::filesystem::path Emulator::getConfigPath() {
|
||||||
if constexpr (Helpers::isAndroid()) {
|
if constexpr (Helpers::isAndroid()) {
|
||||||
return getAndroidAppPath() / "config.toml";
|
return getAndroidAppPath() / "config.toml";
|
||||||
} else {
|
} 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
|
#endif
|
||||||
|
|
||||||
void Emulator::step() {}
|
void Emulator::step() {}
|
||||||
void Emulator::render() {}
|
|
||||||
|
|
||||||
// Only resume if a ROM is properly loaded
|
// Only resume if a ROM is properly loaded
|
||||||
void Emulator::resume() {
|
void Emulator::resume() {
|
||||||
|
@ -456,6 +457,8 @@ void Emulator::reloadSettings() {
|
||||||
loadRenderdoc();
|
loadRenderdoc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu.getRenderer()->setHashTextures(config.hashTextures);
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
||||||
// Reload RPC setting if we're compiling with RPC support
|
// Reload RPC setting if we're compiling with RPC support
|
||||||
|
|
||||||
|
|
|
@ -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); });
|
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
static const std::unordered_map<std::string, Theme> map = {
|
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()) {
|
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::Light: return "light";
|
||||||
case Theme::GreetingsCat: return "greetingscat";
|
case Theme::GreetingsCat: return "greetingscat";
|
||||||
case Theme::Cream: return "cream";
|
case Theme::Cream: return "cream";
|
||||||
|
case Theme::Oled: return "oled";
|
||||||
|
|
||||||
case Theme::Dark:
|
case Theme::Dark:
|
||||||
default: return "dark";
|
default: return "dark";
|
||||||
|
@ -39,7 +40,7 @@ FrontendSettings::WindowIcon FrontendSettings::iconFromString(std::string inStri
|
||||||
|
|
||||||
static const std::unordered_map<std::string, WindowIcon> map = {
|
static const std::unordered_map<std::string, WindowIcon> map = {
|
||||||
{"rpog", WindowIcon::Rpog}, {"rsyn", WindowIcon::Rsyn}, {"rcow", WindowIcon::Rcow},
|
{"rpog", WindowIcon::Rpog}, {"rsyn", WindowIcon::Rsyn}, {"rcow", WindowIcon::Rcow},
|
||||||
{"rnap", WindowIcon::Rnap}, {"skyemu", WindowIcon::SkyEmu},
|
{"rnap", WindowIcon::Rnap}, {"skyemu", WindowIcon::SkyEmu}, {"runpog", WindowIcon::Runpog},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (auto search = map.find(inString); search != map.end()) {
|
if (auto search = map.find(inString); search != map.end()) {
|
||||||
|
@ -56,6 +57,7 @@ const char* FrontendSettings::iconToString(WindowIcon icon) {
|
||||||
case WindowIcon::Rcow: return "rcow";
|
case WindowIcon::Rcow: return "rcow";
|
||||||
case WindowIcon::Rnap: return "rnap";
|
case WindowIcon::Rnap: return "rnap";
|
||||||
case WindowIcon::SkyEmu: return "skyemu";
|
case WindowIcon::SkyEmu: return "skyemu";
|
||||||
|
case WindowIcon::Runpog: return "runpog";
|
||||||
|
|
||||||
case WindowIcon::Rpog:
|
case WindowIcon::Rpog:
|
||||||
default: return "rpog";
|
default: return "rpog";
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include <metal_stdlib>
|
#include <metal_stdlib>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
||||||
struct BasicVertexOut {
|
struct BasicVertexOut {
|
||||||
|
@ -219,12 +221,6 @@ struct Globals {
|
||||||
uint GPUREG_LIGHTING_LUTINPUT_SELECT;
|
uint GPUREG_LIGHTING_LUTINPUT_SELECT;
|
||||||
uint GPUREG_LIGHTi_CONFIG;
|
uint GPUREG_LIGHTi_CONFIG;
|
||||||
|
|
||||||
// HACK
|
|
||||||
//bool lightingEnabled;
|
|
||||||
//uint8_t lightingNumLights;
|
|
||||||
//uint32_t lightingConfig1;
|
|
||||||
//uint16_t alphaControl;
|
|
||||||
|
|
||||||
float3 normal;
|
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)));
|
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)]]) {
|
// iOS simulator doesn't support fb fetch, so don't enable it
|
||||||
Globals globals;
|
#ifndef TARGET_OS_SIMULATOR
|
||||||
|
#define PREVIOUS_COLOR_DECL float4 prevColor [[color(0)]],
|
||||||
|
#else
|
||||||
|
#define PREVIOUS_COLOR_DECL
|
||||||
|
#endif
|
||||||
|
|
||||||
// HACK
|
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.lightingEnabled = picaRegs.read(0x008Fu) != 0u;
|
Globals globals;
|
||||||
//globals.lightingNumLights = picaRegs.read(0x01C2u);
|
|
||||||
//globals.lightingConfig1 = picaRegs.read(0x01C4u);
|
|
||||||
//globals.alphaControl = picaRegs.read(0x104);
|
|
||||||
|
|
||||||
globals.tevSources[0] = in.color;
|
globals.tevSources[0] = in.color;
|
||||||
if (lightingEnabled) {
|
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);
|
return performLogicOp(logicOp, color, prevColor);
|
||||||
|
#else
|
||||||
|
return performLogicOp(logicOp, color, float4(0.0));
|
||||||
|
#endif
|
||||||
}
|
}
|
10
src/host_shaders/opengl_es_display.frag
Normal file
10
src/host_shaders/opengl_es_display.frag
Normal 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);
|
||||||
|
}
|
25
src/host_shaders/opengl_es_display.vert
Normal file
25
src/host_shaders/opengl_es_display.vert
Normal 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];
|
||||||
|
}
|
|
@ -118,6 +118,7 @@ void HydraCore::resetContext() {
|
||||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
||||||
Helpers::panic("OpenGL ES init failed");
|
Helpers::panic("OpenGL ES init failed");
|
||||||
}
|
}
|
||||||
|
emulator->getRenderer()->setupGLES();
|
||||||
#else
|
#else
|
||||||
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
||||||
Helpers::panic("OpenGL init failed");
|
Helpers::panic("OpenGL init failed");
|
||||||
|
|
38
src/ios_driver.mm
Normal file
38
src/ios_driver.mm
Normal 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);
|
||||||
|
}
|
|
@ -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);
|
__android_log_print(ANDROID_LOG_INFO, "AlberDriver", "OpenGL ES %d.%d", GLVersion.major, GLVersion.minor);
|
||||||
|
emulator->getRenderer()->setupGLES();
|
||||||
emulator->initGraphicsContext(nullptr);
|
emulator->initGraphicsContext(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +154,6 @@ int AndroidUtils::openDocument(const char* path, const char* perms) {
|
||||||
|
|
||||||
jstring uri = env->NewStringUTF(path);
|
jstring uri = env->NewStringUTF(path);
|
||||||
jstring jmode = env->NewStringUTF(perms);
|
jstring jmode = env->NewStringUTF(perms);
|
||||||
|
|
||||||
jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode);
|
jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode);
|
||||||
|
|
||||||
env->DeleteLocalRef(uri);
|
env->DeleteLocalRef(uri);
|
||||||
|
|
|
@ -17,7 +17,8 @@ static retro_input_state_t inputStateCallback;
|
||||||
static retro_hw_render_callback hwRender;
|
static retro_hw_render_callback hwRender;
|
||||||
static std::filesystem::path savePath;
|
static std::filesystem::path savePath;
|
||||||
|
|
||||||
static bool screenTouched;
|
static bool screenTouched = false;
|
||||||
|
static bool usingGLES = false;
|
||||||
|
|
||||||
std::unique_ptr<Emulator> emulator;
|
std::unique_ptr<Emulator> emulator;
|
||||||
RendererGL* renderer;
|
RendererGL* renderer;
|
||||||
|
@ -35,15 +36,19 @@ static void* getGLProcAddress(const char* name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void videoResetContext() {
|
static void videoResetContext() {
|
||||||
#ifdef USING_GLES
|
if (usingGLES) {
|
||||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
||||||
Helpers::panic("OpenGL ES init failed");
|
Helpers::panic("OpenGL ES init failed");
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
|
emulator->getRenderer()->setupGLES();
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
||||||
Helpers::panic("OpenGL init failed");
|
Helpers::panic("OpenGL init failed");
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
|
|
||||||
emulator->initGraphicsContext(nullptr);
|
emulator->initGraphicsContext(nullptr);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +78,7 @@ static bool setHWRender(retro_hw_context_type type) {
|
||||||
hwRender.version_minor = 1;
|
hwRender.version_minor = 1;
|
||||||
|
|
||||||
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
|
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
|
||||||
|
usingGLES = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -170,8 +176,12 @@ static void configInit() {
|
||||||
{"panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault ? "Use ubershaders (No stutter, maybe slower); enabled|disabled"
|
{"panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault ? "Use ubershaders (No stutter, maybe slower); enabled|disabled"
|
||||||
: "Use ubershaders (No stutter, maybe slower); disabled|enabled"},
|
: "Use ubershaders (No stutter, maybe slower); disabled|enabled"},
|
||||||
{"panda3ds_use_vsync", "Enable VSync; enabled|disabled"},
|
{"panda3ds_use_vsync", "Enable VSync; enabled|disabled"},
|
||||||
|
{"panda3ds_hash_textures", EmulatorConfig::hashTexturesDefault ? "Hash textures (Better graphics, maybe slower); enabled|disabled"
|
||||||
|
: "Hash textures (Better graphics, maybe slower); disabled|enabled"},
|
||||||
|
|
||||||
|
{"panda3ds_system_language", "System language; En|Fr|Es|De|It|Pt|Nl|Ru|Ja|Zh|Ko|Tw"},
|
||||||
{"panda3ds_dsp_emulation", "DSP emulation; HLE|LLE|Null"},
|
{"panda3ds_dsp_emulation", "DSP emulation; HLE|LLE|Null"},
|
||||||
{"panda3ds_use_audio", "Enable audio; disabled|enabled"},
|
{"panda3ds_use_audio", EmulatorConfig::audioEnabledDefault ? "Enable audio; enabled|disabled" : "Enable audio; disabled|enabled"},
|
||||||
{"panda3ds_audio_volume", "Audio volume; 100|0|10|20|40|60|80|90|100|120|140|150|180|200"},
|
{"panda3ds_audio_volume", "Audio volume; 100|0|10|20|40|60|80|90|100|120|140|150|180|200"},
|
||||||
{"panda3ds_mute_audio", "Mute audio; disabled|enabled"},
|
{"panda3ds_mute_audio", "Mute audio; disabled|enabled"},
|
||||||
{"panda3ds_enable_aac", "Enable AAC audio; enabled|disabled"},
|
{"panda3ds_enable_aac", "Enable AAC audio; enabled|disabled"},
|
||||||
|
@ -196,6 +206,8 @@ static void configUpdate() {
|
||||||
config.shaderJitEnabled = fetchVariableBool("panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault);
|
config.shaderJitEnabled = fetchVariableBool("panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault);
|
||||||
config.chargerPlugged = fetchVariableBool("panda3ds_use_charger", true);
|
config.chargerPlugged = fetchVariableBool("panda3ds_use_charger", true);
|
||||||
config.batteryPercentage = fetchVariableRange("panda3ds_battery_level", 5, 100);
|
config.batteryPercentage = fetchVariableRange("panda3ds_battery_level", 5, 100);
|
||||||
|
config.systemLanguage = EmulatorConfig::languageCodeFromString(fetchVariable("panda3ds_system_language", "en"));
|
||||||
|
|
||||||
config.dspType = Audio::DSPCore::typeFromString(fetchVariable("panda3ds_dsp_emulation", "null"));
|
config.dspType = Audio::DSPCore::typeFromString(fetchVariable("panda3ds_dsp_emulation", "null"));
|
||||||
config.audioEnabled = fetchVariableBool("panda3ds_use_audio", false);
|
config.audioEnabled = fetchVariableBool("panda3ds_use_audio", false);
|
||||||
config.aacEnabled = fetchVariableBool("panda3ds_enable_aac", true);
|
config.aacEnabled = fetchVariableBool("panda3ds_enable_aac", true);
|
||||||
|
@ -207,6 +219,7 @@ static void configUpdate() {
|
||||||
config.accurateShaderMul = fetchVariableBool("panda3ds_accurate_shader_mul", false);
|
config.accurateShaderMul = fetchVariableBool("panda3ds_accurate_shader_mul", false);
|
||||||
config.useUbershaders = fetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
|
config.useUbershaders = fetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
|
||||||
config.accelerateShaders = fetchVariableBool("panda3ds_accelerate_shaders", EmulatorConfig::accelerateShadersDefault);
|
config.accelerateShaders = fetchVariableBool("panda3ds_accelerate_shaders", EmulatorConfig::accelerateShadersDefault);
|
||||||
|
config.hashTextures = fetchVariableBool("panda3ds_hash_textures", EmulatorConfig::hashTexturesDefault);
|
||||||
|
|
||||||
config.forceShadergenForLights = fetchVariableBool("panda3ds_ubershader_lighting_override", true);
|
config.forceShadergenForLights = fetchVariableBool("panda3ds_ubershader_lighting_override", true);
|
||||||
config.lightShadergenThreshold = fetchVariableRange("panda3ds_ubershader_lighting_override_threshold", 1, 8);
|
config.lightShadergenThreshold = fetchVariableRange("panda3ds_ubershader_lighting_override_threshold", 1, 8);
|
||||||
|
@ -380,6 +393,8 @@ void retro_run() {
|
||||||
emulator->runFrame();
|
emulator->runFrame();
|
||||||
|
|
||||||
videoCallback(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0);
|
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) {}
|
void retro_set_controller_port_device(uint port, uint device) {}
|
||||||
|
|
|
@ -4,4 +4,6 @@
|
||||||
#define MA_NO_ENCODING
|
#define MA_NO_ENCODING
|
||||||
#define MINIAUDIO_IMPLEMENTATION
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifndef PANDA3DS_IOS
|
||||||
#include "miniaudio.h"
|
#include "miniaudio.h"
|
||||||
|
#endif
|
8
src/miniaudio/miniaudio.m
Normal file
8
src/miniaudio/miniaudio.m
Normal 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"
|
|
@ -71,6 +71,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
themeSelect->addItem(tr("Dark"));
|
themeSelect->addItem(tr("Dark"));
|
||||||
themeSelect->addItem(tr("Greetings Cat"));
|
themeSelect->addItem(tr("Greetings Cat"));
|
||||||
themeSelect->addItem(tr("Cream"));
|
themeSelect->addItem(tr("Cream"));
|
||||||
|
themeSelect->addItem(tr("OLED"));
|
||||||
themeSelect->setCurrentIndex(static_cast<int>(config.frontendSettings.theme));
|
themeSelect->setCurrentIndex(static_cast<int>(config.frontendSettings.theme));
|
||||||
connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) {
|
connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) {
|
||||||
config.frontendSettings.theme = static_cast<Theme>(index);
|
config.frontendSettings.theme = static_cast<Theme>(index);
|
||||||
|
@ -86,6 +87,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
iconSelect->addItem(tr("Sleepy panda"));
|
iconSelect->addItem(tr("Sleepy panda"));
|
||||||
iconSelect->addItem(tr("Cow panda"));
|
iconSelect->addItem(tr("Cow panda"));
|
||||||
iconSelect->addItem(tr("The penguin from SkyEmu"));
|
iconSelect->addItem(tr("The penguin from SkyEmu"));
|
||||||
|
iconSelect->addItem(tr("Unpog"));
|
||||||
iconSelect->setCurrentIndex(static_cast<int>(config.frontendSettings.icon));
|
iconSelect->setCurrentIndex(static_cast<int>(config.frontendSettings.icon));
|
||||||
|
|
||||||
connect(iconSelect, &QComboBox::currentIndexChanged, this, [&](int index) {
|
connect(iconSelect, &QComboBox::currentIndexChanged, this, [&](int index) {
|
||||||
|
@ -145,6 +147,27 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
romLayout->addWidget(browseRomPath);
|
romLayout->addWidget(browseRomPath);
|
||||||
genLayout->addRow(tr("Default ROMs path"), romLayout);
|
genLayout->addRow(tr("Default ROMs path"), romLayout);
|
||||||
|
|
||||||
|
QComboBox* systemLanguage = new QComboBox();
|
||||||
|
systemLanguage->addItem(tr("Japanese"));
|
||||||
|
systemLanguage->addItem(tr("English"));
|
||||||
|
systemLanguage->addItem(tr("French"));
|
||||||
|
systemLanguage->addItem(tr("German"));
|
||||||
|
systemLanguage->addItem(tr("Italian"));
|
||||||
|
systemLanguage->addItem(tr("Spanish"));
|
||||||
|
systemLanguage->addItem(tr("Chinese"));
|
||||||
|
systemLanguage->addItem(tr("Korean"));
|
||||||
|
systemLanguage->addItem(tr("Dutch"));
|
||||||
|
systemLanguage->addItem(tr("Portuguese"));
|
||||||
|
systemLanguage->addItem(tr("Russian"));
|
||||||
|
systemLanguage->addItem(tr("Taiwanese"));
|
||||||
|
|
||||||
|
systemLanguage->setCurrentIndex(static_cast<int>(config.systemLanguage));
|
||||||
|
connect(systemLanguage, &QComboBox::currentIndexChanged, this, [&](int index) {
|
||||||
|
config.systemLanguage = static_cast<LanguageCodes>(index);
|
||||||
|
updateConfig();
|
||||||
|
});
|
||||||
|
genLayout->addRow(tr("System language"), systemLanguage);
|
||||||
|
|
||||||
QCheckBox* discordRpcEnabled = new QCheckBox(tr("Enable Discord RPC"));
|
QCheckBox* discordRpcEnabled = new QCheckBox(tr("Enable Discord RPC"));
|
||||||
connectCheckbox(discordRpcEnabled, config.discordRpcEnabled);
|
connectCheckbox(discordRpcEnabled, config.discordRpcEnabled);
|
||||||
genLayout->addRow(discordRpcEnabled);
|
genLayout->addRow(discordRpcEnabled);
|
||||||
|
@ -163,14 +186,24 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
gpuLayout->setHorizontalSpacing(20);
|
gpuLayout->setHorizontalSpacing(20);
|
||||||
gpuLayout->setVerticalSpacing(10);
|
gpuLayout->setVerticalSpacing(10);
|
||||||
|
|
||||||
QComboBox* rendererType = new QComboBox;
|
QComboBox* rendererType = new QComboBox();
|
||||||
rendererType->addItem(tr("Null"));
|
rendererType->addItem(tr("Null"));
|
||||||
rendererType->addItem(tr("OpenGL"));
|
rendererType->addItem(tr("OpenGL"));
|
||||||
rendererType->addItem(tr("Vulkan"));
|
rendererType->addItem(tr("Vulkan"));
|
||||||
rendererType->setCurrentIndex(static_cast<int>(config.rendererType));
|
rendererType->setCurrentIndex(static_cast<int>(config.rendererType));
|
||||||
connect(rendererType, &QComboBox::currentIndexChanged, this, [&](int index) {
|
connect(rendererType, &QComboBox::currentIndexChanged, this, [&](int index) {
|
||||||
config.rendererType = static_cast<RendererType>(index);
|
auto type = static_cast<RendererType>(index);
|
||||||
|
|
||||||
|
if (type == RendererType::Vulkan) {
|
||||||
|
QMessageBox messageBox(
|
||||||
|
QMessageBox::Icon::Critical, tr("Vulkan renderer unavailable"),
|
||||||
|
tr("Qt UI doesn't currently support Vulkan, try again at a later time")
|
||||||
|
);
|
||||||
|
messageBox.exec();
|
||||||
|
} else {
|
||||||
|
config.rendererType = type;
|
||||||
updateConfig();
|
updateConfig();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
gpuLayout->addRow(tr("GPU renderer"), rendererType);
|
gpuLayout->addRow(tr("GPU renderer"), rendererType);
|
||||||
|
|
||||||
|
@ -198,6 +231,11 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
connectCheckbox(accelerateShaders, config.accelerateShaders);
|
connectCheckbox(accelerateShaders, config.accelerateShaders);
|
||||||
gpuLayout->addRow(accelerateShaders);
|
gpuLayout->addRow(accelerateShaders);
|
||||||
|
|
||||||
|
QCheckBox* hashTextures = new QCheckBox(tr("Hash textures"));
|
||||||
|
hashTextures->setToolTip(tr("Enable this to reduce texture mismatches at the cost of slightly lower performance"));
|
||||||
|
connectCheckbox(hashTextures, config.hashTextures);
|
||||||
|
gpuLayout->addRow(hashTextures);
|
||||||
|
|
||||||
QCheckBox* forceShadergenForLights = new QCheckBox(tr("Force shadergen when rendering lights"));
|
QCheckBox* forceShadergenForLights = new QCheckBox(tr("Force shadergen when rendering lights"));
|
||||||
connectCheckbox(forceShadergenForLights, config.forceShadergenForLights);
|
connectCheckbox(forceShadergenForLights, config.forceShadergenForLights);
|
||||||
gpuLayout->addRow(forceShadergenForLights);
|
gpuLayout->addRow(forceShadergenForLights);
|
||||||
|
@ -217,7 +255,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
audioLayout->setHorizontalSpacing(20);
|
audioLayout->setHorizontalSpacing(20);
|
||||||
audioLayout->setVerticalSpacing(10);
|
audioLayout->setVerticalSpacing(10);
|
||||||
|
|
||||||
QComboBox* dspType = new QComboBox;
|
QComboBox* dspType = new QComboBox();
|
||||||
dspType->addItem(tr("Null"));
|
dspType->addItem(tr("Null"));
|
||||||
dspType->addItem(tr("LLE"));
|
dspType->addItem(tr("LLE"));
|
||||||
dspType->addItem(tr("HLE"));
|
dspType->addItem(tr("HLE"));
|
||||||
|
@ -244,7 +282,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
|
||||||
connectCheckbox(muteAudio, config.audioDeviceConfig.muteAudio);
|
connectCheckbox(muteAudio, config.audioDeviceConfig.muteAudio);
|
||||||
audioLayout->addRow(muteAudio);
|
audioLayout->addRow(muteAudio);
|
||||||
|
|
||||||
QComboBox* volumeCurveType = new QComboBox;
|
QComboBox* volumeCurveType = new QComboBox();
|
||||||
volumeCurveType->addItem(tr("Cubic"));
|
volumeCurveType->addItem(tr("Cubic"));
|
||||||
volumeCurveType->addItem(tr("Linear"));
|
volumeCurveType->addItem(tr("Linear"));
|
||||||
volumeCurveType->setCurrentIndex(static_cast<int>(config.audioDeviceConfig.volumeCurve));
|
volumeCurveType->setCurrentIndex(static_cast<int>(config.audioDeviceConfig.volumeCurve));
|
||||||
|
@ -406,6 +444,34 @@ void ConfigWindow::setTheme(Theme theme) {
|
||||||
break;
|
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: {
|
case Theme::System: {
|
||||||
qApp->setPalette(this->style()->standardPalette());
|
qApp->setPalette(this->style()->standardPalette());
|
||||||
qApp->setStyle(QStyleFactory::create("WindowsVista"));
|
qApp->setStyle(QStyleFactory::create("WindowsVista"));
|
||||||
|
@ -423,6 +489,7 @@ void ConfigWindow::setIcon(WindowIcon icon) {
|
||||||
case WindowIcon::Rnap: updateIcon(":/docs/img/rnap_icon.png"); break;
|
case WindowIcon::Rnap: updateIcon(":/docs/img/rnap_icon.png"); break;
|
||||||
case WindowIcon::Rcow: updateIcon(":/docs/img/rcow_icon.png"); break;
|
case WindowIcon::Rcow: updateIcon(":/docs/img/rcow_icon.png"); break;
|
||||||
case WindowIcon::SkyEmu: updateIcon(":/docs/img/skyemu_icon.png"); break;
|
case WindowIcon::SkyEmu: updateIcon(":/docs/img/skyemu_icon.png"); break;
|
||||||
|
case WindowIcon::Runpog: updateIcon(":/docs/img/runpog_icon.png"); break;
|
||||||
|
|
||||||
case WindowIcon::Rpog:
|
case WindowIcon::Rpog:
|
||||||
default: updateIcon(":/docs/img/rpog_icon.png"); break;
|
default: updateIcon(":/docs/img/rpog_icon.png"); break;
|
||||||
|
|
|
@ -7,6 +7,5 @@ int main(int argc, char *argv[]) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
MainWindow window(&app);
|
MainWindow window(&app);
|
||||||
|
|
||||||
window.show();
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,13 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
||||||
// Enable drop events for loading ROMs
|
// Enable drop events for loading ROMs
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
resize(800, 240 * 4);
|
resize(800, 240 * 4);
|
||||||
|
show();
|
||||||
|
|
||||||
// We pass a callback to the screen widget that will be triggered every time we resize the screen
|
// 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);
|
screen = new ScreenWidget([this](u32 width, u32 height) { handleScreenResize(width, height); }, this);
|
||||||
setCentralWidget(screen);
|
setCentralWidget(screen);
|
||||||
|
|
||||||
screen->show();
|
|
||||||
appRunning = true;
|
appRunning = true;
|
||||||
|
|
||||||
// Set our menu bar up
|
// Set our menu bar up
|
||||||
menuBar = new QMenuBar(nullptr);
|
menuBar = new QMenuBar(nullptr);
|
||||||
|
|
||||||
|
@ -140,6 +139,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
||||||
glContext->MakeCurrent();
|
glContext->MakeCurrent();
|
||||||
glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);
|
glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);
|
||||||
|
|
||||||
|
if (glContext->IsGLES()) {
|
||||||
|
emu->getRenderer()->setupGLES();
|
||||||
|
}
|
||||||
|
|
||||||
emu->initGraphicsContext(glContext);
|
emu->initGraphicsContext(glContext);
|
||||||
} else if (usingVk) {
|
} else if (usingVk) {
|
||||||
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
|
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
|
||||||
|
|
|
@ -29,6 +29,7 @@ ScreenWidget::ScreenWidget(ResizeCallback resizeCallback, QWidget* parent) : QWi
|
||||||
setAttribute(Qt::WA_KeyCompression, false);
|
setAttribute(Qt::WA_KeyCompression, false);
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
show();
|
||||||
|
|
||||||
if (!createGLContext()) {
|
if (!createGLContext()) {
|
||||||
Helpers::panic("Failed to create GL context for display");
|
Helpers::panic("Failed to create GL context for display");
|
||||||
|
@ -60,11 +61,12 @@ void ScreenWidget::resizeSurface(u32 width, u32 height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenWidget::createGLContext() {
|
bool ScreenWidget::createGLContext() {
|
||||||
// List of GL context versions we will try. Anything 4.1+ is good
|
// 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, 6> versionsToTry = {
|
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, 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, 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::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();
|
std::optional<WindowInfo> windowInfo = getWindowInfo();
|
||||||
|
@ -72,6 +74,10 @@ bool ScreenWidget::createGLContext() {
|
||||||
this->windowInfo = *windowInfo;
|
this->windowInfo = *windowInfo;
|
||||||
|
|
||||||
glContext = GL::Context::Create(*getWindowInfo(), versionsToTry);
|
glContext = GL::Context::Create(*getWindowInfo(), versionsToTry);
|
||||||
|
if (glContext == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
glContext->DoneCurrent();
|
glContext->DoneCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +85,7 @@ bool ScreenWidget::createGLContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal ScreenWidget::devicePixelRatioFromScreen() const {
|
qreal ScreenWidget::devicePixelRatioFromScreen() const {
|
||||||
const QScreen* screenForRatio = window()->windowHandle()->screen();
|
const QScreen* screenForRatio = windowHandle()->screen();
|
||||||
if (!screenForRatio) {
|
if (!screenForRatio) {
|
||||||
screenForRatio = QGuiApplication::primaryScreen();
|
screenForRatio = QGuiApplication::primaryScreen();
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,12 +46,13 @@ struct LanguageInfo {
|
||||||
// Please keep this list mostly in alphabetical order.
|
// Please keep this list mostly in alphabetical order.
|
||||||
// Also, for Unicode characters in language names, use Unicode keycodes instead of writing out the name,
|
// 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.
|
// 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"English"), "en"), // English
|
||||||
LanguageInfo(QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"), // Greek
|
LanguageInfo(QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"), // Greek
|
||||||
LanguageInfo(QStringLiteral(u"Espa\u00F1ol"), "es"), // Spanish
|
LanguageInfo(QStringLiteral(u"Espa\u00F1ol"), "es"), // Spanish
|
||||||
LanguageInfo(QStringLiteral(u"Nederlands"), "nl"), // Dutch
|
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() {
|
QComboBox* ConfigWindow::createLanguageSelect() {
|
||||||
|
|
|
@ -69,14 +69,30 @@ FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMapp
|
||||||
Helpers::panic("Window creation failed: %s", SDL_GetError());
|
Helpers::panic("Window creation failed: %s", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glContext = SDL_GL_CreateContext(window);
|
||||||
|
if (glContext == nullptr) {
|
||||||
|
Helpers::warn("OpenGL context creation failed: %s\nTrying again with OpenGL ES.", SDL_GetError());
|
||||||
|
|
||||||
|
// 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);
|
glContext = SDL_GL_CreateContext(window);
|
||||||
if (glContext == nullptr) {
|
if (glContext == nullptr) {
|
||||||
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
|
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))) {
|
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
|
||||||
Helpers::panic("OpenGL init failed");
|
Helpers::panic("OpenGL init failed");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0);
|
SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
2
src/pandios/.gitignore
vendored
Normal file
2
src/pandios/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
libAlber.dylib
|
||||||
|
Alber/Headers/ios_driver.h
|
7
src/pandios/Alber/Headers/ios_driver.h
Normal file
7
src/pandios/Alber/Headers/ios_driver.h
Normal 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);
|
5
src/pandios/Alber/module.map
Normal file
5
src/pandios/Alber/module.map
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module AlberDriver {
|
||||||
|
umbrella "Headers" // for multiple files
|
||||||
|
link "libAlber"
|
||||||
|
export *
|
||||||
|
}
|
587
src/pandios/Pandios.xcodeproj/project.pbxproj
Normal file
587
src/pandios/Pandios.xcodeproj/project.pbxproj
Normal 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 */;
|
||||||
|
}
|
7
src/pandios/Pandios.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
src/pandios/Pandios.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
Binary file not shown.
|
@ -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>
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
6
src/pandios/Pandios/Assets.xcassets/Contents.json
Normal file
6
src/pandios/Pandios/Assets.xcassets/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
95
src/pandios/Pandios/ContentView.swift
Normal file
95
src/pandios/Pandios/ContentView.swift
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
75
src/pandios/Pandios/DocumentPicker.swift
Normal file
75
src/pandios/Pandios/DocumentPicker.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/pandios/Pandios/PandiosApp.swift
Normal file
11
src/pandios/Pandios/PandiosApp.swift
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct PandiosApp: App {
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
ContentView()
|
||||||
|
DocumentView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
37
src/pandios/build.sh
Executable file
37
src/pandios/build.sh
Executable 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
|
|
@ -38,6 +38,7 @@ public class AppDataDocumentProvider extends DocumentsProvider {
|
||||||
Document.COLUMN_DISPLAY_NAME,
|
Document.COLUMN_DISPLAY_NAME,
|
||||||
Document.COLUMN_MIME_TYPE,
|
Document.COLUMN_MIME_TYPE,
|
||||||
Document.COLUMN_LAST_MODIFIED,
|
Document.COLUMN_LAST_MODIFIED,
|
||||||
|
Document.COLUMN_FLAGS,
|
||||||
Document.COLUMN_SIZE
|
Document.COLUMN_SIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ public class AppDataDocumentProvider extends DocumentsProvider {
|
||||||
}
|
}
|
||||||
cursor.newRow()
|
cursor.newRow()
|
||||||
.add(Document.COLUMN_DOCUMENT_ID, obtainDocumentId(file))
|
.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_FLAGS, flags)
|
||||||
.add(Document.COLUMN_LAST_MODIFIED, file.lastModified())
|
.add(Document.COLUMN_LAST_MODIFIED, file.lastModified())
|
||||||
.add(Document.COLUMN_DISPLAY_NAME, file.getName())
|
.add(Document.COLUMN_DISPLAY_NAME, file.getName())
|
||||||
|
@ -157,6 +158,11 @@ public class AppDataDocumentProvider extends DocumentsProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeDocument(String documentId, String parentDocumentId) throws FileNotFoundException {
|
||||||
|
deleteDocument(documentId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor openDocument(String documentId, String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {
|
public ParcelFileDescriptor openDocument(String documentId, String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {
|
||||||
return ParcelFileDescriptor.open(obtainFile(documentId), ParcelFileDescriptor.parseMode(mode));
|
return ParcelFileDescriptor.open(obtainFile(documentId), ParcelFileDescriptor.parseMode(mode));
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "PICA/gpu.hpp"
|
||||||
|
|
||||||
Renderer::Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
|
Renderer::Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
|
||||||
: gpu(gpu), regs(internalRegs), externalRegs(externalRegs) {}
|
: gpu(gpu), regs(internalRegs), externalRegs(externalRegs) {}
|
||||||
Renderer::~Renderer() {}
|
Renderer::~Renderer() {}
|
||||||
|
@ -39,3 +41,39 @@ const char* Renderer::typeToString(RendererType rendererType) {
|
||||||
default: return "Invalid";
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue