Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
liushuyu | 69e758d738 | |
Steveice10 | f4768cd26c | |
Théo B | e0d2c1308e | |
Steveice10 | 4f9fc88bb3 | |
GPUCode | d857743075 | |
kylon | b5042a5257 | |
Wunk | e524542a40 | |
Steveice10 | 3a4ebb1413 | |
Steveice10 | cbe8987036 | |
Charles Lombardo | da5aa70fc9 | |
Castor215 | 749a721aa2 | |
SachinVin | bb003c2bd4 | |
Tobias | 7638f87f74 | |
Steveice10 | aa6809e2a8 | |
Steveice10 | 5e02be75a3 | |
Tobias | b9c9beeee5 | |
GPUCode | de993dcfbd | |
oltolm | 3c9157b1ec | |
Ishan09811 | 0c40c10022 |
|
@ -12,13 +12,13 @@ jobs:
|
||||||
if: ${{ !github.head_ref }}
|
if: ${{ !github.head_ref }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/source.sh
|
run: ./.ci/source.sh
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: source
|
name: source
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
@ -37,11 +37,11 @@ jobs:
|
||||||
OS: linux
|
OS: linux
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
|
@ -53,7 +53,7 @@ jobs:
|
||||||
run: ./.ci/pack.sh
|
run: ./.ci/pack.sh
|
||||||
if: ${{ matrix.target == 'appimage' }}
|
if: ${{ matrix.target == 'appimage' }}
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ matrix.target == 'appimage' }}
|
if: ${{ matrix.target == 'appimage' }}
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
|
@ -70,24 +70,24 @@ jobs:
|
||||||
OS: macos
|
OS: macos
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-${{ matrix.target }}-
|
${{ runner.os }}-${{ matrix.target }}-
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: brew install ccache glslang ninja
|
run: brew install ccache ninja
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./.ci/macos.sh
|
run: ./.ci/macos.sh
|
||||||
- name: Prepare outputs for caching
|
- name: Prepare outputs for caching
|
||||||
run: mv build/bundle $OS-$TARGET
|
run: mv build/bundle $OS-$TARGET
|
||||||
- name: Cache outputs for universal build
|
- name: Cache outputs for universal build
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.OS }}-${{ env.TARGET }}
|
path: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
@ -98,15 +98,15 @@ jobs:
|
||||||
OS: macos
|
OS: macos
|
||||||
TARGET: universal
|
TARGET: universal
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Download x86_64 build from cache
|
- name: Download x86_64 build from cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.OS }}-x86_64
|
path: ${{ env.OS }}-x86_64
|
||||||
key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
fail-on-cache-miss: true
|
fail-on-cache-miss: true
|
||||||
- name: Download ARM64 build from cache
|
- name: Download ARM64 build from cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.OS }}-arm64
|
path: ${{ env.OS }}-arm64
|
||||||
key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
@ -118,7 +118,7 @@ jobs:
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/pack.sh
|
run: ./.ci/pack.sh
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
@ -137,11 +137,11 @@ jobs:
|
||||||
OS: windows
|
OS: windows
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
|
@ -153,13 +153,6 @@ jobs:
|
||||||
- name: Install extra tools (MSVC)
|
- name: Install extra tools (MSVC)
|
||||||
run: choco install ccache ninja wget
|
run: choco install ccache ninja wget
|
||||||
if: ${{ matrix.target == 'msvc' }}
|
if: ${{ matrix.target == 'msvc' }}
|
||||||
- name: Set up Vulkan SDK (MSVC)
|
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
|
||||||
if: ${{ matrix.target == 'msvc' }}
|
|
||||||
with:
|
|
||||||
vulkan-query-version: latest
|
|
||||||
vulkan-components: SPIRV-Tools, Glslang
|
|
||||||
vulkan-use-cache: true
|
|
||||||
- name: Set up MSYS2
|
- name: Set up MSYS2
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
if: ${{ matrix.target == 'msys2' }}
|
if: ${{ matrix.target == 'msys2' }}
|
||||||
|
@ -168,10 +161,8 @@ jobs:
|
||||||
update: true
|
update: true
|
||||||
install: git make p7zip
|
install: git make p7zip
|
||||||
pacboy: >-
|
pacboy: >-
|
||||||
toolchain:p ccache:p cmake:p ninja:p glslang:p
|
toolchain:p ccache:p cmake:p ninja:p
|
||||||
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
|
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
|
||||||
- name: Test glslang
|
|
||||||
run: glslang --version || glslangValidator --version
|
|
||||||
- name: Disable line ending translation
|
- name: Disable line ending translation
|
||||||
run: git config --global core.autocrlf input
|
run: git config --global core.autocrlf input
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -179,7 +170,7 @@ jobs:
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/pack.sh
|
run: ./.ci/pack.sh
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
@ -192,11 +183,11 @@ jobs:
|
||||||
OS: android
|
OS: android
|
||||||
TARGET: universal
|
TARGET: universal
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
@ -215,7 +206,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository -y ppa:theofficialgman/gpu-tools
|
sudo add-apt-repository -y ppa:theofficialgman/gpu-tools
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install ccache glslang-dev glslang-tools apksigner -y
|
sudo apt-get install ccache apksigner -y
|
||||||
- name: Build
|
- name: Build
|
||||||
run: JAVA_HOME=$JAVA_HOME_17_X64 ./.ci/android.sh
|
run: JAVA_HOME=$JAVA_HOME_17_X64 ./.ci/android.sh
|
||||||
env:
|
env:
|
||||||
|
@ -228,7 +219,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
UNPACKED: 1
|
UNPACKED: 1
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: src/android/app/artifacts/
|
path: src/android/app/artifacts/
|
||||||
|
@ -242,18 +233,18 @@ jobs:
|
||||||
OS: ios
|
OS: ios
|
||||||
TARGET: arm64
|
TARGET: arm64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ runner.os }}-ios-${{ github.sha }}
|
key: ${{ runner.os }}-ios-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-ios-
|
${{ runner.os }}-ios-
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: brew install ccache glslang ninja
|
run: brew install ccache ninja
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./.ci/ios.sh
|
run: ./.ci/ios.sh
|
||||||
release:
|
release:
|
||||||
|
@ -261,7 +252,7 @@ jobs:
|
||||||
needs: [windows, linux, macos-universal, android, source]
|
needs: [windows, linux, macos-universal, android, source]
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v4
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
image: citraemu/build-environments:linux-fresh
|
image: citraemu/build-environments:linux-fresh
|
||||||
options: -u 1001
|
options: -u 1001
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|
|
@ -20,11 +20,11 @@ jobs:
|
||||||
if: ${{ github.event.inputs.nightly != 'false' && github.repository == 'citra-emu/citra' }}
|
if: ${{ github.event.inputs.nightly != 'false' && github.repository == 'citra-emu/citra' }}
|
||||||
steps:
|
steps:
|
||||||
# this checkout is required to make sure the GitHub Actions scripts are available
|
# this checkout is required to make sure the GitHub Actions scripts are available
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
name: Pre-checkout
|
name: Pre-checkout
|
||||||
with:
|
with:
|
||||||
submodules: false
|
submodules: false
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v7
|
||||||
id: check-changes
|
id: check-changes
|
||||||
name: 'Check for new changes'
|
name: 'Check for new changes'
|
||||||
env:
|
env:
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
||||||
return checkBaseChanges(github, context);
|
return checkBaseChanges(github, context);
|
||||||
- run: npm install execa@5
|
- run: npm install execa@5
|
||||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
name: Checkout
|
name: Checkout
|
||||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||||
with:
|
with:
|
||||||
|
@ -46,7 +46,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
token: ${{ secrets.ALT_GITHUB_TOKEN }}
|
token: ${{ secrets.ALT_GITHUB_TOKEN }}
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v7
|
||||||
name: 'Update and tag new commits'
|
name: 'Update and tag new commits'
|
||||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||||
env:
|
env:
|
||||||
|
@ -62,11 +62,11 @@ jobs:
|
||||||
if: ${{ github.event.inputs.canary != 'false' && github.repository == 'citra-emu/citra' }}
|
if: ${{ github.event.inputs.canary != 'false' && github.repository == 'citra-emu/citra' }}
|
||||||
steps:
|
steps:
|
||||||
# this checkout is required to make sure the GitHub Actions scripts are available
|
# this checkout is required to make sure the GitHub Actions scripts are available
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
name: Pre-checkout
|
name: Pre-checkout
|
||||||
with:
|
with:
|
||||||
submodules: false
|
submodules: false
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v7
|
||||||
id: check-changes
|
id: check-changes
|
||||||
name: 'Check for new changes'
|
name: 'Check for new changes'
|
||||||
env:
|
env:
|
||||||
|
@ -79,7 +79,7 @@ jobs:
|
||||||
return checkCanaryChanges(github, context);
|
return checkCanaryChanges(github, context);
|
||||||
- run: npm install execa@5
|
- run: npm install execa@5
|
||||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
name: Checkout
|
name: Checkout
|
||||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||||
with:
|
with:
|
||||||
|
@ -87,7 +87,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
token: ${{ secrets.ALT_GITHUB_TOKEN }}
|
token: ${{ secrets.ALT_GITHUB_TOKEN }}
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v7
|
||||||
name: 'Check and merge canary changes'
|
name: 'Check and merge canary changes'
|
||||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
container: citraemu/build-environments:linux-fresh
|
container: citraemu/build-environments:linux-fresh
|
||||||
if: ${{ github.repository == 'citra-emu/citra' }}
|
if: ${{ github.repository == 'citra-emu/citra' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
|
@ -294,12 +294,21 @@ endif()
|
||||||
add_library(httplib INTERFACE)
|
add_library(httplib INTERFACE)
|
||||||
if(USE_SYSTEM_CPP_HTTPLIB)
|
if(USE_SYSTEM_CPP_HTTPLIB)
|
||||||
find_package(CppHttp 0.14.1)
|
find_package(CppHttp 0.14.1)
|
||||||
|
# Detect if system cpphttplib is a shared library
|
||||||
|
# this breaks building as Citra relies on functions that are moved
|
||||||
|
# into the shared object.
|
||||||
|
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
|
||||||
|
if(HTTP_LIBS)
|
||||||
|
message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...")
|
||||||
|
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
||||||
|
else()
|
||||||
if(CppHttp_FOUND)
|
if(CppHttp_FOUND)
|
||||||
target_link_libraries(httplib INTERFACE httplib::httplib)
|
target_link_libraries(httplib INTERFACE httplib::httplib)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
|
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
|
||||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -10,7 +10,7 @@ plugins {
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
id("de.undercouch.download") version "5.5.0"
|
id("de.undercouch.download") version "5.5.0"
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
kotlin("plugin.serialization") version "1.8.21"
|
kotlin("plugin.serialization") version "1.9.22"
|
||||||
id("androidx.navigation.safeargs.kotlin")
|
id("androidx.navigation.safeargs.kotlin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,23 +173,23 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||||
implementation("androidx.activity:activity-ktx:1.8.0")
|
implementation("androidx.activity:activity-ktx:1.8.2")
|
||||||
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
|
||||||
implementation("androidx.slidingpanelayout:slidingpanelayout:1.2.0")
|
implementation("androidx.slidingpanelayout:slidingpanelayout:1.2.0")
|
||||||
implementation("com.google.android.material:material:1.9.0")
|
implementation("com.google.android.material:material:1.9.0")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.work:work-runtime:2.8.1")
|
implementation("androidx.work:work-runtime:2.9.0")
|
||||||
implementation("org.ini4j:ini4j:0.5.4")
|
implementation("org.ini4j:ini4j:0.5.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
|
||||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||||
implementation("io.coil-kt:coil:2.2.2")
|
implementation("io.coil-kt:coil:2.5.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
android:banner="@mipmap/ic_launcher"
|
android:banner="@mipmap/ic_launcher"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true">
|
||||||
|
|
||||||
|
<meta-data android:name="android.game_mode_config"
|
||||||
|
android:resource="@xml/game_mode_config" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.citra.citra_emu.ui.main.MainActivity"
|
android:name="org.citra.citra_emu.ui.main.MainActivity"
|
||||||
android:theme="@style/Theme.Citra.Splash.Main"
|
android:theme="@style/Theme.Citra.Splash.Main"
|
||||||
|
|
|
@ -9,10 +9,13 @@ import android.app.Application
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||||
import org.citra.citra_emu.utils.DocumentsTree
|
import org.citra.citra_emu.utils.DocumentsTree
|
||||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||||
import org.citra.citra_emu.utils.PermissionsHandler
|
import org.citra.citra_emu.utils.PermissionsHandler
|
||||||
|
import org.citra.citra_emu.utils.Log
|
||||||
|
import org.citra.citra_emu.utils.MemoryUtil
|
||||||
|
|
||||||
class CitraApplication : Application() {
|
class CitraApplication : Application() {
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
|
@ -53,9 +56,20 @@ class CitraApplication : Application() {
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.logDeviceInfo()
|
NativeLibrary.logDeviceInfo()
|
||||||
|
logDeviceInfo()
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun logDeviceInfo() {
|
||||||
|
Log.info("Device Manufacturer - ${Build.MANUFACTURER}")
|
||||||
|
Log.info("Device Model - ${Build.MODEL}")
|
||||||
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
|
||||||
|
Log.info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
|
||||||
|
Log.info("SoC Model - ${Build.SOC_MODEL}")
|
||||||
|
}
|
||||||
|
Log.info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var application: CitraApplication? = null
|
private var application: CitraApplication? = null
|
||||||
|
|
||||||
|
|
|
@ -413,12 +413,12 @@ object NativeLibrary {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
|
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
|
||||||
Log.verbose("[NativeLibrary] Registering EmulationActivity.")
|
Log.debug("[NativeLibrary] Registering EmulationActivity.")
|
||||||
sEmulationActivity = WeakReference(emulationActivity)
|
sEmulationActivity = WeakReference(emulationActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearEmulationActivity() {
|
fun clearEmulationActivity() {
|
||||||
Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
|
Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
|
||||||
sEmulationActivity.clear()
|
sEmulationActivity.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,14 +94,14 @@ object DirectoryInitialization {
|
||||||
val dataPath = PermissionsHandler.citraDirectory
|
val dataPath = PermissionsHandler.citraDirectory
|
||||||
if (dataPath.toString().isNotEmpty()) {
|
if (dataPath.toString().isNotEmpty()) {
|
||||||
userPath = dataPath.toString()
|
userPath = dataPath.toString()
|
||||||
Log.debug("[DirectoryInitialization] User Dir: $userPath")
|
android.util.Log.d("[Citra Frontend]", "[DirectoryInitialization] User Dir: $userPath")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyAsset(asset: String, output: File, overwrite: Boolean, context: Context) {
|
private fun copyAsset(asset: String, output: File, overwrite: Boolean, context: Context) {
|
||||||
Log.verbose("[DirectoryInitialization] Copying File $asset to $output")
|
Log.debug("[DirectoryInitialization] Copying File $asset to $output")
|
||||||
try {
|
try {
|
||||||
if (!output.exists() || overwrite) {
|
if (!output.exists() || overwrite) {
|
||||||
val inputStream = context.assets.open(asset)
|
val inputStream = context.assets.open(asset)
|
||||||
|
@ -121,7 +121,7 @@ object DirectoryInitialization {
|
||||||
overwrite: Boolean,
|
overwrite: Boolean,
|
||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
Log.verbose("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
|
Log.debug("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
|
||||||
try {
|
try {
|
||||||
var createdFolder = false
|
var createdFolder = false
|
||||||
for (file in context.assets.list(assetFolder)!!) {
|
for (file in context.assets.list(assetFolder)!!) {
|
||||||
|
|
|
@ -4,34 +4,17 @@
|
||||||
|
|
||||||
package org.citra.citra_emu.utils
|
package org.citra.citra_emu.utils
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import org.citra.citra_emu.BuildConfig
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains methods that call through to [android.util.Log], but
|
|
||||||
* with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log
|
|
||||||
* levels in release builds.
|
|
||||||
*/
|
|
||||||
object Log {
|
object Log {
|
||||||
// Tracks whether we should share the old log or the current log
|
// Tracks whether we should share the old log or the current log
|
||||||
var gameLaunched = false
|
var gameLaunched = false
|
||||||
private const val TAG = "Citra Frontend"
|
|
||||||
|
|
||||||
fun verbose(message: String?) {
|
external fun debug(message: String)
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.v(TAG, message!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun debug(message: String?) {
|
external fun warning(message: String)
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(TAG, message!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun info(message: String?) = Log.i(TAG, message!!)
|
external fun info(message: String)
|
||||||
|
|
||||||
fun warning(message: String?) = Log.w(TAG, message!!)
|
external fun error(message: String)
|
||||||
|
|
||||||
fun error(message: String?) = Log.e(TAG, message!!)
|
external fun critical(message: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.citra.citra_emu.utils
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import org.citra.citra_emu.CitraApplication
|
||||||
|
import org.citra.citra_emu.R
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
object MemoryUtil {
|
||||||
|
private val context get() = CitraApplication.appContext
|
||||||
|
|
||||||
|
private val Float.hundredths: String
|
||||||
|
get() = String.format(Locale.ROOT, "%.2f", this)
|
||||||
|
|
||||||
|
const val Kb: Float = 1024F
|
||||||
|
const val Mb = Kb * 1024
|
||||||
|
const val Gb = Mb * 1024
|
||||||
|
const val Tb = Gb * 1024
|
||||||
|
const val Pb = Tb * 1024
|
||||||
|
const val Eb = Pb * 1024
|
||||||
|
|
||||||
|
fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
|
||||||
|
when {
|
||||||
|
size < Kb -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
size.hundredths,
|
||||||
|
context.getString(R.string.memory_byte_shorthand)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size < Mb -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
|
||||||
|
context.getString(R.string.memory_kilobyte)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size < Gb -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
|
||||||
|
context.getString(R.string.memory_megabyte)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size < Tb -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
|
||||||
|
context.getString(R.string.memory_gigabyte)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size < Pb -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
|
||||||
|
context.getString(R.string.memory_terabyte)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size < Eb -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
|
||||||
|
context.getString(R.string.memory_petabyte)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
context.getString(
|
||||||
|
R.string.memory_formatted,
|
||||||
|
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
|
||||||
|
context.getString(R.string.memory_exabyte)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val totalMemory: Float
|
||||||
|
get() {
|
||||||
|
val memInfo = ActivityManager.MemoryInfo()
|
||||||
|
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
|
||||||
|
getMemoryInfo(memInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
memInfo.advertisedMem.toFloat()
|
||||||
|
} else {
|
||||||
|
memInfo.totalMem.toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isLessThan(minimum: Int, size: Float): Boolean =
|
||||||
|
when (size) {
|
||||||
|
Kb -> totalMemory < Mb && totalMemory < minimum
|
||||||
|
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
|
||||||
|
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
|
||||||
|
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
|
||||||
|
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
|
||||||
|
Eb -> totalMemory / Eb < minimum
|
||||||
|
else -> totalMemory < Kb && totalMemory < minimum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
|
||||||
|
// the potential error created by memInfo.totalMem
|
||||||
|
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ add_library(citra-android SHARED
|
||||||
ndk_motion.cpp
|
ndk_motion.cpp
|
||||||
ndk_motion.h
|
ndk_motion.h
|
||||||
system_save_game.cpp
|
system_save_game.cpp
|
||||||
|
native_log.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network)
|
target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network)
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <common/logging/log.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include "android_common/android_common.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||||
|
LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||||
|
LOG_WARNING(Frontend, "{}", GetJString(env, jmessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||||
|
LOG_INFO(Frontend, "{}", GetJString(env, jmessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||||
|
LOG_ERROR(Frontend, "{}", GetJString(env, jmessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||||
|
LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
|
@ -442,6 +442,17 @@
|
||||||
<string name="cia_install_error_encrypted">\"%s\" must be decrypted before being used with Citra.\n A real 3DS is required</string>
|
<string name="cia_install_error_encrypted">\"%s\" must be decrypted before being used with Citra.\n A real 3DS is required</string>
|
||||||
<string name="cia_install_error_unknown">An unknown error occurred while installing \"%s\".\n Please see the log for more details</string>
|
<string name="cia_install_error_unknown">An unknown error occurred while installing \"%s\".\n Please see the log for more details</string>
|
||||||
|
|
||||||
|
<!-- Memory Sizes -->
|
||||||
|
<string name="memory_formatted">%1$s %2$s</string>
|
||||||
|
<string name="memory_byte">Byte</string>
|
||||||
|
<string name="memory_byte_shorthand">B</string>
|
||||||
|
<string name="memory_kilobyte">KB</string>
|
||||||
|
<string name="memory_megabyte">MB</string>
|
||||||
|
<string name="memory_gigabyte">GB</string>
|
||||||
|
<string name="memory_terabyte">TB</string>
|
||||||
|
<string name="memory_petabyte">PB</string>
|
||||||
|
<string name="memory_exabyte">EB</string>
|
||||||
|
|
||||||
<!-- Theme Modes -->
|
<!-- Theme Modes -->
|
||||||
<string name="change_theme_mode">Change Theme Mode</string>
|
<string name="change_theme_mode">Change Theme Mode</string>
|
||||||
<string name="theme_mode_follow_system">Follow System</string>
|
<string name="theme_mode_follow_system">Follow System</string>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<game-mode-config
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:supportsBatteryGameMode="true"
|
||||||
|
android:supportsPerformanceGameMode="true"
|
||||||
|
android:allowGameDownscaling="false"
|
||||||
|
android:allowGameFpsOverride="false"/>
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.1.2" apply false
|
id("com.android.application") version "8.2.1" apply false
|
||||||
id("com.android.library") version "8.1.2" apply false
|
id("com.android.library") version "8.2.1" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.21"
|
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean").configure {
|
tasks.register("clean").configure {
|
||||||
|
@ -19,6 +19,6 @@ buildscript {
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.7.5")
|
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.7.6")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||||
|
|
|
@ -298,9 +298,9 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||||
b.buffer_id,
|
b.buffer_id,
|
||||||
state.mono_or_stereo,
|
state.mono_or_stereo,
|
||||||
state.format,
|
state.format,
|
||||||
true,
|
true, // from_queue
|
||||||
{}, // 0 in u32_dsp
|
0, // play_position
|
||||||
false,
|
false, // has_played
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i,
|
LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i,
|
||||||
|
@ -321,7 +321,11 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||||
void Source::GenerateFrame() {
|
void Source::GenerateFrame() {
|
||||||
current_frame.fill({});
|
current_frame.fill({});
|
||||||
|
|
||||||
if (state.current_buffer.empty() && !DequeueBuffer()) {
|
if (state.current_buffer.empty()) {
|
||||||
|
// TODO(SachinV): Should dequeue happen at the end of the frame generation?
|
||||||
|
if (DequeueBuffer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
state.enabled = false;
|
state.enabled = false;
|
||||||
state.buffer_update = true;
|
state.buffer_update = true;
|
||||||
state.last_buffer_id = state.current_buffer_id;
|
state.last_buffer_id = state.current_buffer_id;
|
||||||
|
@ -330,8 +334,6 @@ void Source::GenerateFrame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t frame_position = 0;
|
std::size_t frame_position = 0;
|
||||||
|
|
||||||
state.current_sample_number = state.next_sample_number;
|
|
||||||
while (frame_position < current_frame.size()) {
|
while (frame_position < current_frame.size()) {
|
||||||
if (state.current_buffer.empty() && !DequeueBuffer()) {
|
if (state.current_buffer.empty() && !DequeueBuffer()) {
|
||||||
break;
|
break;
|
||||||
|
@ -358,7 +360,7 @@ void Source::GenerateFrame() {
|
||||||
}
|
}
|
||||||
// TODO(jroweboy): Keep track of frame_position independently so that it doesn't lose precision
|
// TODO(jroweboy): Keep track of frame_position independently so that it doesn't lose precision
|
||||||
// over time
|
// over time
|
||||||
state.next_sample_number += static_cast<u32>(frame_position * state.rate_multiplier);
|
state.current_sample_number += static_cast<u32>(frame_position * state.rate_multiplier);
|
||||||
|
|
||||||
state.filters.ProcessFrame(current_frame);
|
state.filters.ProcessFrame(current_frame);
|
||||||
}
|
}
|
||||||
|
@ -409,7 +411,6 @@ bool Source::DequeueBuffer() {
|
||||||
|
|
||||||
// the first playthrough starts at play_position, loops start at the beginning of the buffer
|
// the first playthrough starts at play_position, loops start at the beginning of the buffer
|
||||||
state.current_sample_number = (!buf.has_played) ? buf.play_position : 0;
|
state.current_sample_number = (!buf.has_played) ? buf.play_position : 0;
|
||||||
state.next_sample_number = state.current_sample_number;
|
|
||||||
state.current_buffer_physical_address = buf.physical_address;
|
state.current_buffer_physical_address = buf.physical_address;
|
||||||
state.current_buffer_id = buf.buffer_id;
|
state.current_buffer_id = buf.buffer_id;
|
||||||
state.last_buffer_id = 0;
|
state.last_buffer_id = 0;
|
||||||
|
@ -420,8 +421,17 @@ bool Source::DequeueBuffer() {
|
||||||
state.input_queue.push(buf);
|
state.input_queue.push(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TRACE(Audio_DSP, "source_id={} buffer_id={} from_queue={} current_buffer.size()={}",
|
// Because our interpolation consumes samples instead of using an index,
|
||||||
source_id, buf.buffer_id, buf.from_queue, state.current_buffer.size());
|
// let's just consume the samples up to the current sample number.
|
||||||
|
state.current_buffer.erase(
|
||||||
|
state.current_buffer.begin(),
|
||||||
|
std::next(state.current_buffer.begin(), state.current_sample_number));
|
||||||
|
|
||||||
|
LOG_TRACE(Audio_DSP,
|
||||||
|
"source_id={} buffer_id={} from_queue={} current_buffer.size()={}, "
|
||||||
|
"buf.has_played={}, buf.play_position={}",
|
||||||
|
source_id, buf.buffer_id, buf.from_queue, state.current_buffer.size(), buf.has_played,
|
||||||
|
buf.play_position);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ private:
|
||||||
Format format;
|
Format format;
|
||||||
|
|
||||||
bool from_queue;
|
bool from_queue;
|
||||||
u32_dsp play_position; // = 0;
|
u32 play_position; // = 0;
|
||||||
bool has_played; // = false;
|
bool has_played; // = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -136,7 +136,6 @@ private:
|
||||||
// Current buffer
|
// Current buffer
|
||||||
|
|
||||||
u32 current_sample_number = 0;
|
u32 current_sample_number = 0;
|
||||||
u32 next_sample_number = 0;
|
|
||||||
PAddr current_buffer_physical_address = 0;
|
PAddr current_buffer_physical_address = 0;
|
||||||
AudioInterp::StereoBuffer16 current_buffer = {};
|
AudioInterp::StereoBuffer16 current_buffer = {};
|
||||||
|
|
||||||
|
@ -171,7 +170,6 @@ private:
|
||||||
ar& mono_or_stereo;
|
ar& mono_or_stereo;
|
||||||
ar& format;
|
ar& format;
|
||||||
ar& current_sample_number;
|
ar& current_sample_number;
|
||||||
ar& next_sample_number;
|
|
||||||
ar& current_buffer_physical_address;
|
ar& current_buffer_physical_address;
|
||||||
ar& current_buffer;
|
ar& current_buffer;
|
||||||
ar& buffer_update;
|
ar& buffer_update;
|
||||||
|
|
|
@ -54,7 +54,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||||
// This must be in alphabetical order according to action name as it must have the same order as
|
// This must be in alphabetical order according to action name as it must have the same order as
|
||||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::array<UISettings::Shortcut, 30> Config::default_hotkeys {{
|
const std::array<UISettings::Shortcut, 35> Config::default_hotkeys {{
|
||||||
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
|
@ -71,6 +71,11 @@ const std::array<UISettings::Shortcut, 30> Config::default_hotkeys {{
|
||||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
||||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
||||||
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
||||||
|
{QStringLiteral("Multiplayer Browse Public Game Lobby"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), Qt::ApplicationShortcut}},
|
||||||
|
{QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), Qt::ApplicationShortcut}},
|
||||||
|
{QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), Qt::ApplicationShortcut}},
|
||||||
|
{QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), Qt::ApplicationShortcut}},
|
||||||
|
{QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
||||||
|
@ -557,6 +562,15 @@ void Config::ReadMultiplayerValues() {
|
||||||
UISettings::values.game_id = ReadSetting(QStringLiteral("game_id"), 0).toULongLong();
|
UISettings::values.game_id = ReadSetting(QStringLiteral("game_id"), 0).toULongLong();
|
||||||
UISettings::values.room_description =
|
UISettings::values.room_description =
|
||||||
ReadSetting(QStringLiteral("room_description"), QString{}).toString();
|
ReadSetting(QStringLiteral("room_description"), QString{}).toString();
|
||||||
|
UISettings::values.multiplayer_filter_text =
|
||||||
|
ReadSetting(QStringLiteral("multiplayer_filter_text"), QString{}).toString();
|
||||||
|
UISettings::values.multiplayer_filter_games_owned =
|
||||||
|
ReadSetting(QStringLiteral("multiplayer_filter_games_owned"), false).toBool();
|
||||||
|
UISettings::values.multiplayer_filter_hide_empty =
|
||||||
|
ReadSetting(QStringLiteral("multiplayer_filter_hide_empty"), false).toBool();
|
||||||
|
UISettings::values.multiplayer_filter_hide_full =
|
||||||
|
ReadSetting(QStringLiteral("multiplayer_filter_hide_full"), false).toBool();
|
||||||
|
|
||||||
// Read ban list back
|
// Read ban list back
|
||||||
int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
|
int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
|
||||||
UISettings::values.ban_list.first.resize(size);
|
UISettings::values.ban_list.first.resize(size);
|
||||||
|
@ -1074,6 +1088,15 @@ void Config::SaveMultiplayerValues() {
|
||||||
WriteSetting(QStringLiteral("game_id"), UISettings::values.game_id, 0);
|
WriteSetting(QStringLiteral("game_id"), UISettings::values.game_id, 0);
|
||||||
WriteSetting(QStringLiteral("room_description"), UISettings::values.room_description,
|
WriteSetting(QStringLiteral("room_description"), UISettings::values.room_description,
|
||||||
QString{});
|
QString{});
|
||||||
|
WriteSetting(QStringLiteral("multiplayer_filter_text"),
|
||||||
|
UISettings::values.multiplayer_filter_text, QString{});
|
||||||
|
WriteSetting(QStringLiteral("multiplayer_filter_games_owned"),
|
||||||
|
UISettings::values.multiplayer_filter_games_owned, false);
|
||||||
|
WriteSetting(QStringLiteral("multiplayer_filter_hide_empty"),
|
||||||
|
UISettings::values.multiplayer_filter_hide_empty, false);
|
||||||
|
WriteSetting(QStringLiteral("multiplayer_filter_hide_full"),
|
||||||
|
UISettings::values.multiplayer_filter_hide_full, false);
|
||||||
|
|
||||||
// Write ban list
|
// Write ban list
|
||||||
qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
|
qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
|
||||||
for (std::size_t i = 0; i < UISettings::values.ban_list.first.size(); ++i) {
|
for (std::size_t i = 0; i < UISettings::values.ban_list.first.size(); ++i) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
|
|
||||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||||
static const std::array<UISettings::Shortcut, 30> default_hotkeys;
|
static const std::array<UISettings::Shortcut, 35> default_hotkeys;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Initialize(const std::string& config_name);
|
void Initialize(const std::string& config_name);
|
||||||
|
|
|
@ -647,6 +647,13 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
link_action_shortcut(ui->action_Advance_Frame, QStringLiteral("Advance Frame"));
|
link_action_shortcut(ui->action_Advance_Frame, QStringLiteral("Advance Frame"));
|
||||||
link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot"));
|
link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot"));
|
||||||
link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot"));
|
link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot"));
|
||||||
|
link_action_shortcut(ui->action_View_Lobby,
|
||||||
|
QStringLiteral("Multiplayer Browse Public Game Lobby"));
|
||||||
|
link_action_shortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
|
||||||
|
link_action_shortcut(ui->action_Connect_To_Room,
|
||||||
|
QStringLiteral("Multiplayer Direct Connect to Room"));
|
||||||
|
link_action_shortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
|
||||||
|
link_action_shortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
|
||||||
|
|
||||||
const auto add_secondary_window_hotkey = [this](QKeySequence hotkey, const char* slot) {
|
const auto add_secondary_window_hotkey = [this](QKeySequence hotkey, const char* slot) {
|
||||||
// This action will fire specifically when secondary_window is in focus
|
// This action will fire specifically when secondary_window is in focus
|
||||||
|
@ -3190,8 +3197,10 @@ int main(int argc, char* argv[]) {
|
||||||
QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_policy);
|
QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_policy);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
|
auto bundle_dir = FileUtil::GetBundleDirectory();
|
||||||
chdir(bin_path.c_str());
|
if (bundle_dir) {
|
||||||
|
FileUtil::SetCurrentDir(bundle_dir.value() + "..");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL
|
#ifdef ENABLE_OPENGL
|
||||||
|
|
|
@ -80,9 +80,8 @@ void DirectConnectWindow::Connect() {
|
||||||
// Store settings
|
// Store settings
|
||||||
UISettings::values.nickname = ui->nickname->text();
|
UISettings::values.nickname = ui->nickname->text();
|
||||||
UISettings::values.ip = ui->ip->text();
|
UISettings::values.ip = ui->ip->text();
|
||||||
UISettings::values.port = (ui->port->isModified() && !ui->port->text().isEmpty())
|
UISettings::values.port =
|
||||||
? ui->port->text()
|
!ui->port->text().isEmpty() ? ui->port->text() : UISettings::values.port;
|
||||||
: UISettings::values.port;
|
|
||||||
|
|
||||||
// attempt to connect in a different thread
|
// attempt to connect in a different thread
|
||||||
QFuture<void> f = QtConcurrent::run([&] {
|
QFuture<void> f = QtConcurrent::run([&] {
|
||||||
|
|
|
@ -63,10 +63,10 @@ Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list,
|
||||||
|
|
||||||
// UI Buttons
|
// UI Buttons
|
||||||
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
|
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
|
||||||
|
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
|
||||||
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
|
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
|
||||||
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
|
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
|
||||||
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
|
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
|
||||||
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
|
|
||||||
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
|
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
|
||||||
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
|
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
|
||||||
|
|
||||||
|
@ -74,6 +74,12 @@ Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list,
|
||||||
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
|
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
|
||||||
&Lobby::OnRefreshLobby);
|
&Lobby::OnRefreshLobby);
|
||||||
|
|
||||||
|
// Load persistent filters after events are connected to make sure they apply
|
||||||
|
ui->search->setText(UISettings::values.multiplayer_filter_text);
|
||||||
|
ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned);
|
||||||
|
ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty);
|
||||||
|
ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full);
|
||||||
|
|
||||||
// manually start a refresh when the window is opening
|
// manually start a refresh when the window is opening
|
||||||
// TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as
|
// TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as
|
||||||
// part of the constructor, but offload the refresh until after the window shown. perhaps emit a
|
// part of the constructor, but offload the refresh until after the window shown. perhaps emit a
|
||||||
|
@ -180,6 +186,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
||||||
UISettings::values.nickname = ui->nickname->text();
|
UISettings::values.nickname = ui->nickname->text();
|
||||||
UISettings::values.ip = proxy->data(connection_index, LobbyItemHost::HostIPRole).toString();
|
UISettings::values.ip = proxy->data(connection_index, LobbyItemHost::HostIPRole).toString();
|
||||||
UISettings::values.port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toString();
|
UISettings::values.port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toString();
|
||||||
|
UISettings::values.multiplayer_filter_text = ui->search->text();
|
||||||
|
UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked();
|
||||||
|
UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked();
|
||||||
|
UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lobby::ResetModel() {
|
void Lobby::ResetModel() {
|
||||||
|
|
|
@ -188,13 +188,38 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant data(int role) const override {
|
QVariant data(int role) const override {
|
||||||
if (role != Qt::DisplayRole) {
|
switch (role) {
|
||||||
return LobbyItem::data(role);
|
case Qt::DisplayRole: {
|
||||||
}
|
|
||||||
auto members = data(MemberListRole).toList();
|
auto members = data(MemberListRole).toList();
|
||||||
return QStringLiteral("%1 / %2").arg(QString::number(members.size()),
|
return QStringLiteral("%1 / %2").arg(QString::number(members.size()),
|
||||||
data(MaxPlayerRole).toString());
|
data(MaxPlayerRole).toString());
|
||||||
}
|
}
|
||||||
|
case Qt::ForegroundRole: {
|
||||||
|
auto members = data(MemberListRole).toList();
|
||||||
|
auto max_players = data(MaxPlayerRole).toInt();
|
||||||
|
const QColor room_full_color(255, 48, 32);
|
||||||
|
const QColor room_almost_full_color(255, 140, 32);
|
||||||
|
const QColor room_has_players_color(32, 160, 32);
|
||||||
|
const QColor room_empty_color(128, 128, 128);
|
||||||
|
|
||||||
|
if (members.size() >= max_players) {
|
||||||
|
return QBrush(room_full_color);
|
||||||
|
} else if (members.size() == (max_players - 1)) {
|
||||||
|
return QBrush(room_almost_full_color);
|
||||||
|
} else if (members.size() == 0) {
|
||||||
|
return QBrush(room_empty_color);
|
||||||
|
} else if (members.size() > 0 && members.size() < (max_players - 1)) {
|
||||||
|
return QBrush(room_has_players_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: How to return a value that tells Qt not to modify the
|
||||||
|
// text color from the default (as if Qt::ForegroundRole wasn't overridden)?
|
||||||
|
return QBrush(nullptr);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return LobbyItem::data(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool operator<(const QStandardItem& other) const override {
|
bool operator<(const QStandardItem& other) const override {
|
||||||
// sort by rooms that have the most players
|
// sort by rooms that have the most players
|
||||||
|
|
|
@ -138,6 +138,11 @@ struct Values {
|
||||||
QString room_description;
|
QString room_description;
|
||||||
std::pair<std::vector<std::string>, std::vector<std::string>> ban_list;
|
std::pair<std::vector<std::string>, std::vector<std::string>> ban_list;
|
||||||
|
|
||||||
|
QString multiplayer_filter_text;
|
||||||
|
bool multiplayer_filter_games_owned;
|
||||||
|
bool multiplayer_filter_hide_empty;
|
||||||
|
bool multiplayer_filter_hide_full;
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
Settings::Setting<bool> show_console{false, "showConsole"};
|
Settings::Setting<bool> show_console{false, "showConsole"};
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The user data dir
|
// The user data dir
|
||||||
#define ROOT_DIR "."
|
|
||||||
#define USERDATA_DIR "user"
|
#define USERDATA_DIR "user"
|
||||||
#ifdef USER_DIR
|
#ifdef USER_DIR
|
||||||
#define EMU_DATA_DIR USER_DIR
|
#define EMU_DATA_DIR USER_DIR
|
||||||
|
|
|
@ -634,6 +634,10 @@ std::optional<std::string> GetCurrentDir() {
|
||||||
std::string strDir = dir;
|
std::string strDir = dir;
|
||||||
#endif
|
#endif
|
||||||
free(dir);
|
free(dir);
|
||||||
|
|
||||||
|
if (!strDir.ends_with(DIR_SEP)) {
|
||||||
|
strDir += DIR_SEP;
|
||||||
|
}
|
||||||
return strDir;
|
return strDir;
|
||||||
} // namespace FileUtil
|
} // namespace FileUtil
|
||||||
|
|
||||||
|
@ -646,17 +650,36 @@ bool SetCurrentDir(const std::string& directory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
std::string GetBundleDirectory() {
|
std::optional<std::string> GetBundleDirectory() {
|
||||||
CFURLRef BundleRef;
|
|
||||||
char AppBundlePath[MAXPATHLEN];
|
|
||||||
// Get the main bundle for the app
|
// Get the main bundle for the app
|
||||||
BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
CFBundleRef bundle_ref = CFBundleGetMainBundle();
|
||||||
CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle);
|
if (!bundle_ref) {
|
||||||
CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath));
|
return {};
|
||||||
CFRelease(BundleRef);
|
}
|
||||||
CFRelease(BundlePath);
|
|
||||||
|
|
||||||
return AppBundlePath;
|
CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref);
|
||||||
|
if (!bundle_url_ref) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
SCOPE_EXIT({ CFRelease(bundle_url_ref); });
|
||||||
|
|
||||||
|
CFStringRef bundle_path_ref = CFURLCopyFileSystemPath(bundle_url_ref, kCFURLPOSIXPathStyle);
|
||||||
|
if (!bundle_path_ref) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
SCOPE_EXIT({ CFRelease(bundle_path_ref); });
|
||||||
|
|
||||||
|
char app_bundle_path[MAXPATHLEN];
|
||||||
|
if (!CFStringGetFileSystemRepresentation(bundle_path_ref, app_bundle_path,
|
||||||
|
sizeof(app_bundle_path))) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path_str(app_bundle_path);
|
||||||
|
if (!path_str.ends_with(DIR_SEP)) {
|
||||||
|
path_str += DIR_SEP;
|
||||||
|
}
|
||||||
|
return path_str;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -732,22 +755,6 @@ static const std::string& GetHomeDirectory() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string GetSysDirectory() {
|
|
||||||
std::string sysDir;
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
sysDir = GetBundleDirectory();
|
|
||||||
sysDir += DIR_SEP;
|
|
||||||
sysDir += SYSDATA_DIR;
|
|
||||||
#else
|
|
||||||
sysDir = SYSDATA_DIR;
|
|
||||||
#endif
|
|
||||||
sysDir += DIR_SEP;
|
|
||||||
|
|
||||||
LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir);
|
|
||||||
return sysDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::unordered_map<UserPath, std::string> g_paths;
|
std::unordered_map<UserPath, std::string> g_paths;
|
||||||
std::unordered_map<UserPath, std::string> g_default_paths;
|
std::unordered_map<UserPath, std::string> g_default_paths;
|
||||||
|
@ -777,8 +784,10 @@ void SetUserPath(const std::string& path) {
|
||||||
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
||||||
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
||||||
#else
|
#else
|
||||||
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
|
auto current_dir = FileUtil::GetCurrentDir();
|
||||||
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
|
if (current_dir.has_value() &&
|
||||||
|
FileUtil::Exists(current_dir.value() + USERDATA_DIR DIR_SEP)) {
|
||||||
|
user_path = current_dir.value() + USERDATA_DIR DIR_SEP;
|
||||||
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
||||||
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -193,11 +193,8 @@ void SetCurrentRomPath(const std::string& path);
|
||||||
// Update the Global Path with the new value
|
// Update the Global Path with the new value
|
||||||
void UpdateUserPath(UserPath path, const std::string& filename);
|
void UpdateUserPath(UserPath path, const std::string& filename);
|
||||||
|
|
||||||
// Returns the path to where the sys file are
|
|
||||||
[[nodiscard]] std::string GetSysDirectory();
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
[[nodiscard]] std::string GetBundleDirectory();
|
[[nodiscard]] std::optional<std::string> GetBundleDirectory();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
@ -451,7 +451,7 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||||
unsigned int line_num, const char* function, const char* format,
|
unsigned int line_num, const char* function, fmt::string_view format,
|
||||||
const fmt::format_args& args) {
|
const fmt::format_args& args) {
|
||||||
if (!initialization_in_progress_suppress_logging) {
|
if (!initialization_in_progress_suppress_logging) {
|
||||||
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
|
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
|
||||||
|
|
|
@ -24,12 +24,12 @@ constexpr const char* TrimSourcePath(std::string_view source) {
|
||||||
|
|
||||||
/// Logs a message to the global logger, using fmt
|
/// Logs a message to the global logger, using fmt
|
||||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||||
unsigned int line_num, const char* function, const char* format,
|
unsigned int line_num, const char* function, fmt::string_view format,
|
||||||
const fmt::format_args& args);
|
const fmt::format_args& args);
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||||
const char* function, const char* format, const Args&... args) {
|
const char* function, fmt::format_string<Args...> format, const Args&... args) {
|
||||||
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format,
|
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format,
|
||||||
fmt::make_format_args(args...));
|
fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
|
@ -327,6 +327,10 @@ add_library(citra_core STATIC
|
||||||
hle/service/ldr_ro/cro_helper.h
|
hle/service/ldr_ro/cro_helper.h
|
||||||
hle/service/ldr_ro/ldr_ro.cpp
|
hle/service/ldr_ro/ldr_ro.cpp
|
||||||
hle/service/ldr_ro/ldr_ro.h
|
hle/service/ldr_ro/ldr_ro.h
|
||||||
|
hle/service/mcu/mcu_hwc.cpp
|
||||||
|
hle/service/mcu/mcu_hwc.h
|
||||||
|
hle/service/mcu/mcu.cpp
|
||||||
|
hle/service/mcu/mcu.h
|
||||||
hle/service/mic/mic_u.cpp
|
hle/service/mic/mic_u.cpp
|
||||||
hle/service/mic/mic_u.h
|
hle/service/mic/mic_u.h
|
||||||
hle/service/mvd/mvd.cpp
|
hle/service/mvd/mvd.cpp
|
||||||
|
|
|
@ -14,18 +14,18 @@ namespace ConfigMem {
|
||||||
Handler::Handler() {
|
Handler::Handler() {
|
||||||
std::memset(&config_mem, 0, sizeof(config_mem));
|
std::memset(&config_mem, 0, sizeof(config_mem));
|
||||||
|
|
||||||
// Values extracted from firmware 11.2.0-35E
|
// Values extracted from firmware 11.17.0-50E
|
||||||
config_mem.kernel_version_min = 0x34;
|
config_mem.kernel_version_min = 0x3a;
|
||||||
config_mem.kernel_version_maj = 0x2;
|
config_mem.kernel_version_maj = 0x2;
|
||||||
config_mem.ns_tid = 0x0004013000008002;
|
config_mem.ns_tid = 0x0004013000008002;
|
||||||
config_mem.sys_core_ver = 0x2;
|
config_mem.sys_core_ver = 0x2;
|
||||||
config_mem.unit_info = 0x1; // Bit 0 set for Retail
|
config_mem.unit_info = 0x1; // Bit 0 set for Retail
|
||||||
config_mem.prev_firm = 0x1;
|
config_mem.prev_firm = 0x1;
|
||||||
config_mem.ctr_sdk_ver = 0x0000F297;
|
config_mem.ctr_sdk_ver = 0x0000F450;
|
||||||
config_mem.firm_version_min = 0x34;
|
config_mem.firm_version_min = 0x3a;
|
||||||
config_mem.firm_version_maj = 0x2;
|
config_mem.firm_version_maj = 0x2;
|
||||||
config_mem.firm_sys_core_ver = 0x2;
|
config_mem.firm_sys_core_ver = 0x2;
|
||||||
config_mem.firm_ctr_sdk_ver = 0x0000F297;
|
config_mem.firm_ctr_sdk_ver = 0x0000F450;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigMemDef& Handler::GetConfigMem() {
|
ConfigMemDef& Handler::GetConfigMem() {
|
||||||
|
|
|
@ -210,10 +210,10 @@ void Process::Set3dsxKernelCaps() {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Similar to Rosalina, we set kernel version to a recent one.
|
// Similar to Rosalina, we set kernel version to a recent one.
|
||||||
// This is 11.2.0, to be consistent with core/hle/kernel/config_mem.cpp
|
// This is 11.17.0, to be consistent with core/hle/kernel/config_mem.cpp
|
||||||
// TODO: refactor kernel version out so it is configurable and consistent
|
// TODO: refactor kernel version out so it is configurable and consistent
|
||||||
// among all relevant places.
|
// among all relevant places.
|
||||||
kernel_version = 0x234;
|
kernel_version = 0x23a;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
||||||
|
|
|
@ -373,7 +373,10 @@ ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId ap
|
||||||
if (active_slot == AppletSlot::Error) {
|
if (active_slot == AppletSlot::Error) {
|
||||||
active_slot = slot;
|
active_slot = slot;
|
||||||
|
|
||||||
// Wake up the application.
|
// APT automatically calls enable on the first registered applet.
|
||||||
|
Enable(attributes);
|
||||||
|
|
||||||
|
// Wake up the applet.
|
||||||
SendParameter({
|
SendParameter({
|
||||||
.sender_id = AppletId::None,
|
.sender_id = AppletId::None,
|
||||||
.destination_id = app_id,
|
.destination_id = app_id,
|
||||||
|
@ -398,7 +401,8 @@ Result AppletManager::Enable(AppletAttributes attributes) {
|
||||||
auto slot_data = GetAppletSlot(slot);
|
auto slot_data = GetAppletSlot(slot);
|
||||||
slot_data->registered = true;
|
slot_data->registered = true;
|
||||||
|
|
||||||
if (slot_data->attributes.applet_pos == AppletPos::System &&
|
if (slot_data->applet_id != AppletId::None &&
|
||||||
|
slot_data->attributes.applet_pos == AppletPos::System &&
|
||||||
slot_data->attributes.is_home_menu) {
|
slot_data->attributes.is_home_menu) {
|
||||||
slot_data->attributes.raw |= attributes.raw;
|
slot_data->attributes.raw |= attributes.raw;
|
||||||
LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.",
|
LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.",
|
||||||
|
@ -786,16 +790,23 @@ Result AppletManager::PrepareToStartSystemApplet(AppletId applet_id) {
|
||||||
|
|
||||||
Result AppletManager::StartSystemApplet(AppletId applet_id, std::shared_ptr<Kernel::Object> object,
|
Result AppletManager::StartSystemApplet(AppletId applet_id, std::shared_ptr<Kernel::Object> object,
|
||||||
const std::vector<u8>& buffer) {
|
const std::vector<u8>& buffer) {
|
||||||
auto source_applet_id = AppletId::None;
|
auto source_applet_id = AppletId::Application;
|
||||||
if (last_system_launcher_slot != AppletSlot::Error) {
|
if (last_system_launcher_slot != AppletSlot::Error) {
|
||||||
const auto slot_data = GetAppletSlot(last_system_launcher_slot);
|
const auto launcher_slot_data = GetAppletSlot(last_system_launcher_slot);
|
||||||
source_applet_id = slot_data->applet_id;
|
source_applet_id = launcher_slot_data->applet_id;
|
||||||
|
|
||||||
// If a system applet is launching another system applet, reset the slot to avoid conflicts.
|
// APT generally clears and terminates the caller of StartSystemApplet. This helps in
|
||||||
// This is needed because system applets won't necessarily call CloseSystemApplet before
|
// situations such as a system applet launching another system applet, which would
|
||||||
// exiting.
|
// otherwise deadlock.
|
||||||
if (last_system_launcher_slot == AppletSlot::SystemApplet) {
|
// TODO: In real APT, the check for AppletSlot::Application does not exist; there is
|
||||||
slot_data->Reset();
|
// TODO: something wrong with our implementation somewhere that makes this necessary.
|
||||||
|
// TODO: Otherwise, games that attempt to launch system applets will be cleared and
|
||||||
|
// TODO: emulation will crash.
|
||||||
|
if (!launcher_slot_data->registered ||
|
||||||
|
(last_system_launcher_slot != AppletSlot::Application &&
|
||||||
|
!launcher_slot_data->attributes.no_exit_on_system_applet)) {
|
||||||
|
launcher_slot_data->Reset();
|
||||||
|
// TODO: Implement launcher process termination.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ union AppletAttributes {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
|
|
||||||
BitField<0, 3, AppletPos> applet_pos;
|
BitField<0, 3, AppletPos> applet_pos;
|
||||||
|
BitField<28, 1, u32> no_exit_on_system_applet;
|
||||||
BitField<29, 1, u32> is_home_menu;
|
BitField<29, 1, u32> is_home_menu;
|
||||||
|
|
||||||
AppletAttributes() : raw(0) {}
|
AppletAttributes() : raw(0) {}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2024 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/mcu/mcu.h"
|
||||||
|
#include "core/hle/service/mcu/mcu_hwc.h"
|
||||||
|
|
||||||
|
namespace Service::MCU {
|
||||||
|
|
||||||
|
void InstallInterfaces(Core::System& system) {
|
||||||
|
auto& service_manager = system.ServiceManager();
|
||||||
|
std::make_shared<HWC>()->InstallAsService(service_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::MCU
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2024 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::MCU {
|
||||||
|
|
||||||
|
void InstallInterfaces(Core::System& system);
|
||||||
|
|
||||||
|
} // namespace Service::MCU
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2024 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/archives.h"
|
||||||
|
#include "core/hle/service/mcu/mcu_hwc.h"
|
||||||
|
|
||||||
|
SERIALIZE_EXPORT_IMPL(Service::MCU::HWC)
|
||||||
|
|
||||||
|
namespace Service::MCU {
|
||||||
|
|
||||||
|
HWC::HWC() : ServiceFramework("mcu::HWC", 1) {
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
// clang-format off
|
||||||
|
{0x0001, nullptr, "ReadRegister"},
|
||||||
|
{0x0002, nullptr, "WriteRegister"},
|
||||||
|
{0x0003, nullptr, "GetInfoRegisters"},
|
||||||
|
{0x0004, nullptr, "GetBatteryVoltage"},
|
||||||
|
{0x0005, nullptr, "GetBatteryLevel"},
|
||||||
|
{0x0006, nullptr, "SetPowerLEDPattern"},
|
||||||
|
{0x0007, nullptr, "SetWifiLEDState"},
|
||||||
|
{0x0008, nullptr, "SetCameraLEDPattern"},
|
||||||
|
{0x0009, nullptr, "Set3DLEDState"},
|
||||||
|
{0x000A, nullptr, "SetInfoLEDPattern"},
|
||||||
|
{0x000B, nullptr, "GetSoundVolume"},
|
||||||
|
{0x000C, nullptr, "SetTopScreenFlicker"},
|
||||||
|
{0x000D, nullptr, "SetBottomScreenFlicker"},
|
||||||
|
{0x000F, nullptr, "GetRtcTime"},
|
||||||
|
{0x0010, nullptr, "GetMcuFwVerHigh"},
|
||||||
|
{0x0011, nullptr, "GetMcuFwVerLow"},
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::MCU
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2024 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Service::MCU {
|
||||||
|
|
||||||
|
class HWC final : public ServiceFramework<HWC> {
|
||||||
|
public:
|
||||||
|
explicit HWC();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SERVICE_SERIALIZATION_SIMPLE
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::MCU
|
||||||
|
|
||||||
|
BOOST_CLASS_EXPORT_KEY(Service::MCU::HWC)
|
|
@ -35,6 +35,7 @@
|
||||||
#include "core/hle/service/http/http_c.h"
|
#include "core/hle/service/http/http_c.h"
|
||||||
#include "core/hle/service/ir/ir.h"
|
#include "core/hle/service/ir/ir.h"
|
||||||
#include "core/hle/service/ldr_ro/ldr_ro.h"
|
#include "core/hle/service/ldr_ro/ldr_ro.h"
|
||||||
|
#include "core/hle/service/mcu/mcu.h"
|
||||||
#include "core/hle/service/mic/mic_u.h"
|
#include "core/hle/service/mic/mic_u.h"
|
||||||
#include "core/hle/service/mvd/mvd.h"
|
#include "core/hle/service/mvd/mvd.h"
|
||||||
#include "core/hle/service/ndm/ndm_u.h"
|
#include "core/hle/service/ndm/ndm_u.h"
|
||||||
|
@ -101,7 +102,7 @@ const std::array<ServiceModuleInfo, 41> service_module_map{
|
||||||
{"CDC", 0x00040130'00001802, nullptr},
|
{"CDC", 0x00040130'00001802, nullptr},
|
||||||
{"GPIO", 0x00040130'00001B02, nullptr},
|
{"GPIO", 0x00040130'00001B02, nullptr},
|
||||||
{"I2C", 0x00040130'00001E02, nullptr},
|
{"I2C", 0x00040130'00001E02, nullptr},
|
||||||
{"MCU", 0x00040130'00001F02, nullptr},
|
{"MCU", 0x00040130'00001F02, MCU::InstallInterfaces},
|
||||||
{"MP", 0x00040130'00002A02, nullptr},
|
{"MP", 0x00040130'00002A02, nullptr},
|
||||||
{"PDN", 0x00040130'00002102, nullptr},
|
{"PDN", 0x00040130'00002102, nullptr},
|
||||||
{"SPI", 0x00040130'00002302, nullptr}}};
|
{"SPI", 0x00040130'00002302, nullptr}}};
|
||||||
|
|
|
@ -1014,8 +1014,8 @@ void SOC_U::Accept(Kernel::HLERequestContext& ctx) {
|
||||||
ctr_addr_buf.resize(async_data->max_addr_len);
|
ctr_addr_buf.resize(async_data->max_addr_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", async_data->socket_handle,
|
LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", async_data->pid,
|
||||||
static_cast<s32>(async_data->ret));
|
async_data->socket_handle, static_cast<s32>(async_data->ret));
|
||||||
|
|
||||||
IPC::RequestBuilder rb(ctx, 0x04, 2, 2);
|
IPC::RequestBuilder rb(ctx, 0x04, 2, 2);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
|
@ -152,6 +152,7 @@ static void SaveBanList(const Network::Room::BanList& ban_list, const std::strin
|
||||||
static void InitializeLogging(const std::string& log_file) {
|
static void InitializeLogging(const std::string& log_file) {
|
||||||
Common::Log::Initialize(log_file);
|
Common::Log::Initialize(log_file);
|
||||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||||
|
Common::Log::Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Application entry point
|
/// Application entry point
|
||||||
|
|
|
@ -429,6 +429,7 @@ Common::ParamPackage SDLState::GetSDLControllerButtonBindByGUID(
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
||||||
{
|
{
|
||||||
|
if (mapped_button != SDL_CONTROLLER_BUTTON_INVALID) {
|
||||||
const SDL_ExtendedGameControllerBind extended_bind =
|
const SDL_ExtendedGameControllerBind extended_bind =
|
||||||
controller->bindings[mapped_button];
|
controller->bindings[mapped_button];
|
||||||
if (extended_bind.input.axis.axis_max < extended_bind.input.axis.axis_min) {
|
if (extended_bind.input.axis.axis_max < extended_bind.input.axis.axis_min) {
|
||||||
|
@ -436,12 +437,13 @@ Common::ParamPackage SDLState::GetSDLControllerButtonBindByGUID(
|
||||||
} else {
|
} else {
|
||||||
params.Set("direction", "+");
|
params.Set("direction", "+");
|
||||||
}
|
}
|
||||||
params.Set(
|
params.Set("threshold", (extended_bind.input.axis.axis_min +
|
||||||
"threshold",
|
(extended_bind.input.axis.axis_max -
|
||||||
(extended_bind.input.axis.axis_min +
|
extended_bind.input.axis.axis_min) /
|
||||||
(extended_bind.input.axis.axis_max - extended_bind.input.axis.axis_min) / 2.0f) /
|
2.0f) /
|
||||||
SDL_JOYSTICK_AXIS_MAX);
|
SDL_JOYSTICK_AXIS_MAX);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
params.Set("direction", "+"); // lacks extended_bind, so just a guess
|
params.Set("direction", "+"); // lacks extended_bind, so just a guess
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,17 +26,6 @@ set(SHADER_FILES
|
||||||
vulkan_blit_depth_stencil.frag
|
vulkan_blit_depth_stencil.frag
|
||||||
)
|
)
|
||||||
|
|
||||||
find_program(GLSLANG "glslang")
|
|
||||||
if ("${GLSLANG}" STREQUAL "GLSLANG-NOTFOUND")
|
|
||||||
find_program(GLSLANG "glslangValidator")
|
|
||||||
if ("${GLSLANG}" STREQUAL "GLSLANG-NOTFOUND")
|
|
||||||
message(FATAL_ERROR "Required program `glslang` (or `glslangValidator`) not found.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(MACROS "-Dgl_VertexID=gl_VertexIndex")
|
|
||||||
set(QUIET_FLAG "--quiet")
|
|
||||||
|
|
||||||
set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
|
set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||||
set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
|
set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
|
||||||
set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
|
set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
|
||||||
|
@ -44,28 +33,9 @@ set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
|
||||||
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
|
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
|
||||||
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
|
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
|
||||||
|
|
||||||
# Check if `--quiet` is available on host's glslang version
|
|
||||||
# glslang prints to STDERR iff an unrecognized flag is passed to it
|
|
||||||
execute_process(
|
|
||||||
COMMAND
|
|
||||||
${GLSLANG} ${QUIET_FLAG}
|
|
||||||
ERROR_VARIABLE
|
|
||||||
GLSLANG_ERROR
|
|
||||||
# STDOUT variable defined to silence unnecessary output during CMake configuration
|
|
||||||
OUTPUT_VARIABLE
|
|
||||||
GLSLANG_OUTPUT
|
|
||||||
)
|
|
||||||
|
|
||||||
if (NOT GLSLANG_ERROR STREQUAL "")
|
|
||||||
message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANG}`")
|
|
||||||
set(QUIET_FLAG "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach(FILENAME IN ITEMS ${SHADER_FILES})
|
foreach(FILENAME IN ITEMS ${SHADER_FILES})
|
||||||
string(REPLACE "." "_" SHADER_NAME ${FILENAME})
|
string(REPLACE "." "_" SHADER_NAME ${FILENAME})
|
||||||
set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
|
set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
|
||||||
# Skip generating source headers on Vulkan exclusive files
|
|
||||||
if (NOT ${FILENAME} MATCHES "vulkan.*")
|
|
||||||
set(SOURCE_HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
|
set(SOURCE_HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT
|
OUTPUT
|
||||||
|
@ -79,22 +49,6 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
|
||||||
# HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
|
# HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
|
||||||
)
|
)
|
||||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
|
set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
|
||||||
endif()
|
|
||||||
# Skip compiling to SPIR-V OpenGL exclusive files
|
|
||||||
if (NOT ${FILENAME} MATCHES "opengl.*")
|
|
||||||
get_filename_component(FILE_NAME ${SHADER_NAME} NAME)
|
|
||||||
string(TOUPPER ${FILE_NAME}_SPV SPIRV_VARIABLE_NAME)
|
|
||||||
set(SPIRV_HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}_spv.h)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT
|
|
||||||
${SPIRV_HEADER_FILE}
|
|
||||||
COMMAND
|
|
||||||
${GLSLANG} --target-env vulkan1.1 --glsl-version 450 ${QUIET_FLAG} ${MACROS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
|
|
||||||
MAIN_DEPENDENCY
|
|
||||||
${SOURCE_FILE}
|
|
||||||
)
|
|
||||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${SPIRV_HEADER_FILE})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
set(SHADER_SOURCES ${SHADER_FILES})
|
set(SHADER_SOURCES ${SHADER_FILES})
|
||||||
|
|
|
@ -8,6 +8,10 @@ layout(location = 0) out vec2 dst_coord;
|
||||||
|
|
||||||
layout(location = 0) uniform mediump ivec2 dst_size;
|
layout(location = 0) uniform mediump ivec2 dst_size;
|
||||||
|
|
||||||
|
#ifdef VULKAN
|
||||||
|
#define gl_VertexID gl_VertexIndex
|
||||||
|
#endif
|
||||||
|
|
||||||
const vec2 vertices[4] =
|
const vec2 vertices[4] =
|
||||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ out gl_PerVertex {
|
||||||
layout(location = 0) out vec2 texcoord;
|
layout(location = 0) out vec2 texcoord;
|
||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
#define gl_VertexID gl_VertexIndex
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
//? #version 430 core
|
//? #version 430 core
|
||||||
layout(location = 0) out vec2 tex_coord;
|
layout(location = 0) out vec2 tex_coord;
|
||||||
|
|
||||||
|
#ifdef VULKAN
|
||||||
|
#define gl_VertexID gl_VertexIndex
|
||||||
|
#endif
|
||||||
|
|
||||||
const vec2 vertices[4] =
|
const vec2 vertices[4] =
|
||||||
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
|
||||||
|
|
||||||
|
|
|
@ -148,8 +148,6 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
|
||||||
// Range check table for input
|
// Range check table for input
|
||||||
if (index >= blend_func_table.size()) {
|
if (index >= blend_func_table.size()) {
|
||||||
LOG_CRITICAL(Render_OpenGL, "Unknown blend factor {}", index);
|
LOG_CRITICAL(Render_OpenGL, "Unknown blend factor {}", index);
|
||||||
UNREACHABLE();
|
|
||||||
|
|
||||||
return GL_ONE;
|
return GL_ONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,10 @@ inline vk::BlendFactor BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const auto index = static_cast<std::size_t>(factor);
|
const auto index = static_cast<std::size_t>(factor);
|
||||||
ASSERT_MSG(index < blend_func_table.size(), "Unknown blend factor {}", index);
|
if (index >= blend_func_table.size()) {
|
||||||
|
LOG_CRITICAL(Render_Vulkan, "Unknown blend factor {}", index);
|
||||||
|
return vk::BlendFactor::eOne;
|
||||||
|
}
|
||||||
return blend_func_table[index];
|
return blend_func_table[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
#include "video_core/renderer_vulkan/vk_memory_util.h"
|
#include "video_core/renderer_vulkan/vk_memory_util.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
|
|
||||||
#include "video_core/host_shaders/vulkan_present_anaglyph_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_present_anaglyph_frag.h"
|
||||||
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_present_frag.h"
|
||||||
#include "video_core/host_shaders/vulkan_present_interlaced_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_present_interlaced_frag.h"
|
||||||
#include "video_core/host_shaders/vulkan_present_vert_spv.h"
|
#include "video_core/host_shaders/vulkan_present_vert.h"
|
||||||
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
|
@ -225,10 +225,14 @@ void RendererVulkan::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuff
|
||||||
|
|
||||||
void RendererVulkan::CompileShaders() {
|
void RendererVulkan::CompileShaders() {
|
||||||
vk::Device device = instance.GetDevice();
|
vk::Device device = instance.GetDevice();
|
||||||
present_vertex_shader = CompileSPV(VULKAN_PRESENT_VERT_SPV, device);
|
present_vertex_shader =
|
||||||
present_shaders[0] = CompileSPV(VULKAN_PRESENT_FRAG_SPV, device);
|
Compile(HostShaders::VULKAN_PRESENT_VERT, vk::ShaderStageFlagBits::eVertex, device);
|
||||||
present_shaders[1] = CompileSPV(VULKAN_PRESENT_ANAGLYPH_FRAG_SPV, device);
|
present_shaders[0] =
|
||||||
present_shaders[2] = CompileSPV(VULKAN_PRESENT_INTERLACED_FRAG_SPV, device);
|
Compile(HostShaders::VULKAN_PRESENT_FRAG, vk::ShaderStageFlagBits::eFragment, device);
|
||||||
|
present_shaders[1] = Compile(HostShaders::VULKAN_PRESENT_ANAGLYPH_FRAG,
|
||||||
|
vk::ShaderStageFlagBits::eFragment, device);
|
||||||
|
present_shaders[2] = Compile(HostShaders::VULKAN_PRESENT_INTERLACED_FRAG,
|
||||||
|
vk::ShaderStageFlagBits::eFragment, device);
|
||||||
|
|
||||||
auto properties = instance.GetPhysicalDevice().getProperties();
|
auto properties = instance.GetPhysicalDevice().getProperties();
|
||||||
for (std::size_t i = 0; i < present_samplers.size(); i++) {
|
for (std::size_t i = 0; i < present_samplers.size(); i++) {
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
||||||
|
|
||||||
#include "video_core/host_shaders/format_reinterpreter/vulkan_d24s8_to_rgba8_comp_spv.h"
|
#include "video_core/host_shaders/format_reinterpreter/vulkan_d24s8_to_rgba8_comp.h"
|
||||||
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
|
#include "video_core/host_shaders/full_screen_triangle_vert.h"
|
||||||
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag.h"
|
||||||
#include "video_core/host_shaders/vulkan_depth_to_buffer_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_depth_to_buffer_comp.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
@ -189,10 +189,14 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, Descrip
|
||||||
PipelineLayoutCreateInfo(&compute_buffer_provider.Layout(), true))},
|
PipelineLayoutCreateInfo(&compute_buffer_provider.Layout(), true))},
|
||||||
two_textures_pipeline_layout{
|
two_textures_pipeline_layout{
|
||||||
device.createPipelineLayout(PipelineLayoutCreateInfo(&two_textures_provider.Layout()))},
|
device.createPipelineLayout(PipelineLayoutCreateInfo(&two_textures_provider.Layout()))},
|
||||||
full_screen_vert{CompileSPV(FULL_SCREEN_TRIANGLE_VERT_SPV, device)},
|
full_screen_vert{Compile(HostShaders::FULL_SCREEN_TRIANGLE_VERT,
|
||||||
d24s8_to_rgba8_comp{CompileSPV(VULKAN_D24S8_TO_RGBA8_COMP_SPV, device)},
|
vk::ShaderStageFlagBits::eVertex, device)},
|
||||||
depth_to_buffer_comp{CompileSPV(VULKAN_DEPTH_TO_BUFFER_COMP_SPV, device)},
|
d24s8_to_rgba8_comp{Compile(HostShaders::VULKAN_D24S8_TO_RGBA8_COMP,
|
||||||
blit_depth_stencil_frag{CompileSPV(VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV, device)},
|
vk::ShaderStageFlagBits::eCompute, device)},
|
||||||
|
depth_to_buffer_comp{Compile(HostShaders::VULKAN_DEPTH_TO_BUFFER_COMP,
|
||||||
|
vk::ShaderStageFlagBits::eCompute, device)},
|
||||||
|
blit_depth_stencil_frag{Compile(HostShaders::VULKAN_BLIT_DEPTH_STENCIL_FRAG,
|
||||||
|
vk::ShaderStageFlagBits::eFragment, device)},
|
||||||
d24s8_to_rgba8_pipeline{MakeComputePipeline(d24s8_to_rgba8_comp, compute_pipeline_layout)},
|
d24s8_to_rgba8_pipeline{MakeComputePipeline(d24s8_to_rgba8_comp, compute_pipeline_layout)},
|
||||||
depth_to_buffer_pipeline{
|
depth_to_buffer_pipeline{
|
||||||
MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)},
|
MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)},
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
@ -153,6 +154,12 @@ Instance::Instance(Core::TelemetrySession& telemetry, Frontend::EmuWindow& windo
|
||||||
physical_device = physical_devices[physical_device_index];
|
physical_device = physical_devices[physical_device_index];
|
||||||
available_extensions = GetSupportedExtensions(physical_device);
|
available_extensions = GetSupportedExtensions(physical_device);
|
||||||
properties = physical_device.getProperties();
|
properties = physical_device.getProperties();
|
||||||
|
if (properties.apiVersion < TargetVulkanApiVersion) {
|
||||||
|
throw std::runtime_error(fmt::format(
|
||||||
|
"Vulkan {}.{} is required, but only {}.{} is supported by device!",
|
||||||
|
VK_VERSION_MAJOR(TargetVulkanApiVersion), VK_VERSION_MINOR(TargetVulkanApiVersion),
|
||||||
|
VK_VERSION_MAJOR(properties.apiVersion), VK_VERSION_MINOR(properties.apiVersion)));
|
||||||
|
}
|
||||||
|
|
||||||
CollectTelemetryParameters(telemetry);
|
CollectTelemetryParameters(telemetry);
|
||||||
CreateDevice();
|
CreateDevice();
|
||||||
|
@ -629,7 +636,7 @@ void Instance::CreateAllocator() {
|
||||||
.device = *device,
|
.device = *device,
|
||||||
.pVulkanFunctions = &functions,
|
.pVulkanFunctions = &functions,
|
||||||
.instance = *instance,
|
.instance = *instance,
|
||||||
.vulkanApiVersion = properties.apiVersion,
|
.vulkanApiVersion = TargetVulkanApiVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
const VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
|
const VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
|
||||||
|
@ -670,7 +677,7 @@ void Instance::CollectToolingInfo() {
|
||||||
if (!tooling_info) {
|
if (!tooling_info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto tools = physical_device.getToolProperties();
|
const auto tools = physical_device.getToolPropertiesEXT();
|
||||||
for (const vk::PhysicalDeviceToolProperties& tool : tools) {
|
for (const vk::PhysicalDeviceToolProperties& tool : tools) {
|
||||||
const std::string_view name = tool.name;
|
const std::string_view name = tool.name;
|
||||||
LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name);
|
LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
@ -290,13 +291,14 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library,
|
||||||
}
|
}
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||||
|
|
||||||
if (!VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion) {
|
const u32 available_version = VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion
|
||||||
throw std::runtime_error("Vulkan 1.0 is not supported, 1.1 is required!");
|
? vk::enumerateInstanceVersion()
|
||||||
}
|
: VK_API_VERSION_1_0;
|
||||||
|
if (available_version < TargetVulkanApiVersion) {
|
||||||
const u32 available_version = vk::enumerateInstanceVersion();
|
throw std::runtime_error(fmt::format(
|
||||||
if (available_version < VK_API_VERSION_1_1) {
|
"Vulkan {}.{} is required, but only {}.{} is supported by instance!",
|
||||||
throw std::runtime_error("Vulkan 1.0 is not supported, 1.1 is required!");
|
VK_VERSION_MAJOR(TargetVulkanApiVersion), VK_VERSION_MINOR(TargetVulkanApiVersion),
|
||||||
|
VK_VERSION_MAJOR(available_version), VK_VERSION_MINOR(available_version)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto extensions = GetInstanceExtensions(window_type, enable_validation);
|
const auto extensions = GetInstanceExtensions(window_type, enable_validation);
|
||||||
|
@ -306,7 +308,7 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library,
|
||||||
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||||
.pEngineName = "Citra Vulkan",
|
.pEngineName = "Citra Vulkan",
|
||||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||||
.apiVersion = VK_API_VERSION_1_3,
|
.apiVersion = TargetVulkanApiVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::container::static_vector<const char*, 2> layers;
|
boost::container::static_vector<const char*, 2> layers;
|
||||||
|
|
|
@ -19,6 +19,8 @@ enum class WindowSystemType : u8;
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
constexpr u32 TargetVulkanApiVersion = VK_API_VERSION_1_1;
|
||||||
|
|
||||||
using DebugCallback =
|
using DebugCallback =
|
||||||
std::variant<vk::UniqueDebugUtilsMessengerEXT, vk::UniqueDebugReportCallbackEXT>;
|
std::variant<vk::UniqueDebugUtilsMessengerEXT, vk::UniqueDebugReportCallbackEXT>;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
|
@ -119,9 +120,9 @@ u32 UnpackDepthStencil(const VideoCore::StagingData& data, vk::Format dest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::container::small_vector<vk::ImageMemoryBarrier, 3> MakeInitBarriers(
|
boost::container::small_vector<vk::ImageMemoryBarrier, 3> MakeInitBarriers(
|
||||||
vk::ImageAspectFlags aspect, std::span<const vk::Image> images, std::size_t num_images) {
|
vk::ImageAspectFlags aspect, std::span<const vk::Image> images) {
|
||||||
boost::container::small_vector<vk::ImageMemoryBarrier, 3> barriers;
|
boost::container::small_vector<vk::ImageMemoryBarrier, 3> barriers;
|
||||||
for (std::size_t i = 0; i < num_images; i++) {
|
for (const vk::Image& image : images) {
|
||||||
barriers.push_back(vk::ImageMemoryBarrier{
|
barriers.push_back(vk::ImageMemoryBarrier{
|
||||||
.srcAccessMask = vk::AccessFlagBits::eNone,
|
.srcAccessMask = vk::AccessFlagBits::eNone,
|
||||||
.dstAccessMask = vk::AccessFlagBits::eNone,
|
.dstAccessMask = vk::AccessFlagBits::eNone,
|
||||||
|
@ -129,7 +130,7 @@ boost::container::small_vector<vk::ImageMemoryBarrier, 3> MakeInitBarriers(
|
||||||
.newLayout = vk::ImageLayout::eGeneral,
|
.newLayout = vk::ImageLayout::eGeneral,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.image = images[i],
|
.image = image,
|
||||||
.subresourceRange{
|
.subresourceRange{
|
||||||
.aspectMask = aspect,
|
.aspectMask = aspect,
|
||||||
.baseMipLevel = 0,
|
.baseMipLevel = 0,
|
||||||
|
@ -219,11 +220,10 @@ Handle MakeHandle(const Instance* instance, u32 width, u32 height, u32 levels, T
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::UniqueFramebuffer MakeFramebuffer(vk::Device device, vk::RenderPass render_pass, u32 width,
|
vk::UniqueFramebuffer MakeFramebuffer(vk::Device device, vk::RenderPass render_pass, u32 width,
|
||||||
u32 height, std::span<const vk::ImageView> attachments,
|
u32 height, std::span<const vk::ImageView> attachments) {
|
||||||
u32 num_attachments) {
|
|
||||||
const vk::FramebufferCreateInfo framebuffer_info = {
|
const vk::FramebufferCreateInfo framebuffer_info = {
|
||||||
.renderPass = render_pass,
|
.renderPass = render_pass,
|
||||||
.attachmentCount = num_attachments,
|
.attachmentCount = static_cast<u32>(attachments.size()),
|
||||||
.pAttachments = attachments.data(),
|
.pAttachments = attachments.data(),
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height,
|
.height = height,
|
||||||
|
@ -715,8 +715,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
|
||||||
ASSERT_MSG(format != vk::Format::eUndefined && levels >= 1,
|
ASSERT_MSG(format != vk::Format::eUndefined && levels >= 1,
|
||||||
"Image allocation parameters are invalid");
|
"Image allocation parameters are invalid");
|
||||||
|
|
||||||
u32 num_images = 0;
|
boost::container::static_vector<vk::Image, 3> raw_images;
|
||||||
std::array<vk::Image, 3> raw_images;
|
|
||||||
|
|
||||||
vk::ImageCreateFlags flags{};
|
vk::ImageCreateFlags flags{};
|
||||||
if (texture_type == VideoCore::TextureType::CubeMap) {
|
if (texture_type == VideoCore::TextureType::CubeMap) {
|
||||||
|
@ -729,18 +728,18 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
|
||||||
const bool need_format_list = is_mutable && instance->IsImageFormatListSupported();
|
const bool need_format_list = is_mutable && instance->IsImageFormatListSupported();
|
||||||
handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage,
|
handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage,
|
||||||
flags, traits.aspect, need_format_list, DebugName(false));
|
flags, traits.aspect, need_format_list, DebugName(false));
|
||||||
raw_images[num_images++] = handles[0].image;
|
raw_images.emplace_back(handles[0].image);
|
||||||
|
|
||||||
if (res_scale != 1) {
|
if (res_scale != 1) {
|
||||||
handles[1] =
|
handles[1] =
|
||||||
MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format,
|
MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format,
|
||||||
traits.usage, flags, traits.aspect, need_format_list, DebugName(true));
|
traits.usage, flags, traits.aspect, need_format_list, DebugName(true));
|
||||||
raw_images[num_images++] = handles[1].image;
|
raw_images.emplace_back(handles[1].image);
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime->renderpass_cache.EndRendering();
|
runtime->renderpass_cache.EndRendering();
|
||||||
scheduler->Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
||||||
const auto barriers = MakeInitBarriers(aspect, raw_images, num_images);
|
const auto barriers = MakeInitBarriers(aspect, raw_images);
|
||||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::PipelineStageFlagBits::eTopOfPipe,
|
vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
||||||
|
@ -758,8 +757,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface
|
||||||
const bool has_normal = mat && mat->Map(MapType::Normal);
|
const bool has_normal = mat && mat->Map(MapType::Normal);
|
||||||
const vk::Format format = traits.native;
|
const vk::Format format = traits.native;
|
||||||
|
|
||||||
u32 num_images = 0;
|
boost::container::static_vector<vk::Image, 2> raw_images;
|
||||||
std::array<vk::Image, 2> raw_images;
|
|
||||||
|
|
||||||
vk::ImageCreateFlags flags{};
|
vk::ImageCreateFlags flags{};
|
||||||
if (texture_type == VideoCore::TextureType::CubeMap) {
|
if (texture_type == VideoCore::TextureType::CubeMap) {
|
||||||
|
@ -769,23 +767,23 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface
|
||||||
const std::string debug_name = DebugName(false, true);
|
const std::string debug_name = DebugName(false, true);
|
||||||
handles[0] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format,
|
handles[0] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format,
|
||||||
traits.usage, flags, traits.aspect, false, debug_name);
|
traits.usage, flags, traits.aspect, false, debug_name);
|
||||||
raw_images[num_images++] = handles[0].image;
|
raw_images.emplace_back(handles[0].image);
|
||||||
|
|
||||||
if (res_scale != 1) {
|
if (res_scale != 1) {
|
||||||
handles[1] = MakeHandle(instance, mat->width, mat->height, levels, texture_type,
|
handles[1] = MakeHandle(instance, mat->width, mat->height, levels, texture_type,
|
||||||
vk::Format::eR8G8B8A8Unorm, traits.usage, flags, traits.aspect,
|
vk::Format::eR8G8B8A8Unorm, traits.usage, flags, traits.aspect,
|
||||||
false, debug_name);
|
false, debug_name);
|
||||||
raw_images[num_images++] = handles[1].image;
|
raw_images.emplace_back(handles[1].image);
|
||||||
}
|
}
|
||||||
if (has_normal) {
|
if (has_normal) {
|
||||||
handles[2] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format,
|
handles[2] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format,
|
||||||
traits.usage, flags, traits.aspect, false, debug_name);
|
traits.usage, flags, traits.aspect, false, debug_name);
|
||||||
raw_images[num_images++] = handles[2].image;
|
raw_images.emplace_back(handles[2].image);
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime->renderpass_cache.EndRendering();
|
runtime->renderpass_cache.EndRendering();
|
||||||
scheduler->Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
||||||
const auto barriers = MakeInitBarriers(aspect, raw_images, num_images);
|
const auto barriers = MakeInitBarriers(aspect, raw_images);
|
||||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::PipelineStageFlagBits::eTopOfPipe,
|
vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
||||||
|
@ -825,11 +823,10 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
|
||||||
|
|
||||||
scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = traits.native, params,
|
scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = traits.native, params,
|
||||||
staging, upload](vk::CommandBuffer cmdbuf) {
|
staging, upload](vk::CommandBuffer cmdbuf) {
|
||||||
u32 num_copies = 1;
|
boost::container::static_vector<vk::BufferImageCopy, 2> buffer_image_copies;
|
||||||
std::array<vk::BufferImageCopy, 2> buffer_image_copies;
|
|
||||||
|
|
||||||
const auto rect = upload.texture_rect;
|
const auto rect = upload.texture_rect;
|
||||||
buffer_image_copies[0] = vk::BufferImageCopy{
|
buffer_image_copies.emplace_back(vk::BufferImageCopy{
|
||||||
.bufferOffset = upload.buffer_offset,
|
.bufferOffset = upload.buffer_offset,
|
||||||
.bufferRowLength = rect.GetWidth(),
|
.bufferRowLength = rect.GetWidth(),
|
||||||
.bufferImageHeight = rect.GetHeight(),
|
.bufferImageHeight = rect.GetHeight(),
|
||||||
|
@ -841,15 +838,16 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
|
||||||
},
|
},
|
||||||
.imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0},
|
.imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0},
|
||||||
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1},
|
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1},
|
||||||
};
|
});
|
||||||
|
|
||||||
if (params.aspect & vk::ImageAspectFlagBits::eStencil) {
|
if (params.aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||||
buffer_image_copies[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
buffer_image_copies[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
||||||
vk::BufferImageCopy& stencil_copy = buffer_image_copies[1];
|
|
||||||
|
vk::BufferImageCopy& stencil_copy =
|
||||||
|
buffer_image_copies.emplace_back(buffer_image_copies[0]);
|
||||||
stencil_copy = buffer_image_copies[0];
|
stencil_copy = buffer_image_copies[0];
|
||||||
stencil_copy.bufferOffset += UnpackDepthStencil(staging, format);
|
stencil_copy.bufferOffset += UnpackDepthStencil(staging, format);
|
||||||
stencil_copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
|
stencil_copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
|
||||||
num_copies++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const vk::ImageMemoryBarrier read_barrier = {
|
const vk::ImageMemoryBarrier read_barrier = {
|
||||||
|
@ -877,7 +875,7 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
|
||||||
vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier);
|
vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier);
|
||||||
|
|
||||||
cmdbuf.copyBufferToImage(buffer, params.src_image, vk::ImageLayout::eTransferDstOptimal,
|
cmdbuf.copyBufferToImage(buffer, params.src_image, vk::ImageLayout::eTransferDstOptimal,
|
||||||
num_copies, buffer_image_copies.data());
|
buffer_image_copies);
|
||||||
|
|
||||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags,
|
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags,
|
||||||
vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier);
|
vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier);
|
||||||
|
@ -1085,7 +1083,7 @@ void Surface::ScaleUp(u32 new_scale) {
|
||||||
runtime->renderpass_cache.EndRendering();
|
runtime->renderpass_cache.EndRendering();
|
||||||
scheduler->Record(
|
scheduler->Record(
|
||||||
[raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
[raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
||||||
const auto barriers = MakeInitBarriers(aspect, raw_images, raw_images.size());
|
const auto barriers = MakeInitBarriers(aspect, raw_images);
|
||||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::PipelineStageFlagBits::eTopOfPipe,
|
vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
||||||
|
@ -1351,7 +1349,7 @@ vk::Framebuffer Surface::Framebuffer() noexcept {
|
||||||
runtime->renderpass_cache.GetRenderpass(color_format, depth_format, false);
|
runtime->renderpass_cache.GetRenderpass(color_format, depth_format, false);
|
||||||
const auto attachments = std::array{ImageView()};
|
const auto attachments = std::array{ImageView()};
|
||||||
framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(),
|
framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(),
|
||||||
GetScaledHeight(), attachments, 1);
|
GetScaledHeight(), attachments);
|
||||||
return framebuffers[index].get();
|
return framebuffers[index].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1481,17 +1479,16 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
|
||||||
image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView();
|
image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView();
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 num_attachments = 0;
|
boost::container::static_vector<vk::ImageView, 2> attachments;
|
||||||
std::array<vk::ImageView, 2> attachments;
|
|
||||||
|
|
||||||
if (color) {
|
if (color) {
|
||||||
prepare(0, color);
|
prepare(0, color);
|
||||||
attachments[num_attachments++] = image_views[0];
|
attachments.emplace_back(image_views[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth) {
|
if (depth) {
|
||||||
prepare(1, depth);
|
prepare(1, depth);
|
||||||
attachments[num_attachments++] = image_views[1];
|
attachments.emplace_back(image_views[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const vk::Device device = runtime.GetInstance().GetDevice();
|
const vk::Device device = runtime.GetInstance().GetDevice();
|
||||||
|
@ -1499,11 +1496,10 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
|
||||||
render_pass =
|
render_pass =
|
||||||
renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false);
|
renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false);
|
||||||
framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(),
|
framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(),
|
||||||
color->GetScaledHeight(), {}, 0);
|
color->GetScaledHeight(), {});
|
||||||
} else {
|
} else {
|
||||||
render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false);
|
render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false);
|
||||||
framebuffer =
|
framebuffer = MakeFramebuffer(device, render_pass, width, height, attachments);
|
||||||
MakeFramebuffer(device, render_pass, width, height, attachments, num_attachments);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue