android: Update build system and optimize ARM NCE implementation

- Update Kotlin from 1.9.20 to 2.1.20-RC2
- Upgrade Java version from 17 to 21
- Update Android Gradle plugin from 8.1.2 to 8.9.0
- Update Gradle wrapper from 8.10.2 to 8.11.1
- Update NDK version to 29.0.13113456 rc1
- Update all Android dependencies to latest versions
- Simplify ARM NCE implementation by removing custom TLB handling
- Improve alignment and access fault handling
- Update hardware BASE_CLOCK_RATE from 1.02GHz to 1.785GHz
- Add citron Emulator Project copyright notices

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron 2025-03-11 20:48:51 +10:00
parent 834cc89548
commit dad8859679
8 changed files with 57 additions and 192 deletions

View file

@ -11,10 +11,10 @@ plugins {
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("kotlin-parcelize") id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.9.20" kotlin("plugin.serialization") version "2.1.20-RC2"
id("androidx.navigation.safeargs.kotlin") id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0" id("org.jlleitschuh.gradle.ktlint") version "12.2.0"
id("com.github.triplet.play") version "3.8.6" id("com.github.triplet.play") version "3.12.1"
} }
/** /**
@ -29,19 +29,19 @@ android {
namespace = "org.citron.citron_emu" namespace = "org.citron.citron_emu"
compileSdkVersion = "android-35" compileSdkVersion = "android-35"
ndkVersion = "26.1.10909125" ndkVersion = "29.0.13113456 rc1" // "26.1.10909125"
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_21
} }
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "21"
} }
packaging { packaging {
@ -55,7 +55,7 @@ android {
defaultConfig { defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml // TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.citron.citron_emu" applicationId = "com.antutu.ABenchMark"
minSdk = 30 minSdk = 30
//noinspection EditedTargetSdkVersion //noinspection EditedTargetSdkVersion
targetSdk = 35 targetSdk = 35
@ -161,7 +161,7 @@ android {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
version = "3.22.1" version = "3.31.6"
path = file("../../../CMakeLists.txt") path = file("../../../CMakeLists.txt")
} }
} }
@ -182,7 +182,7 @@ android {
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
) )
abiFilters("arm64-v8a", "x86_64") abiFilters("arm64-v8a") // , "x86_64")
} }
} }
} }
@ -203,7 +203,7 @@ tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset") tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
ktlint { ktlint {
version.set("0.47.1") version.set("0.49.1")
android.set(true) android.set(true)
ignoreFailures.set(false) ignoreFailures.set(false)
disabledRules.set( disabledRules.set(
@ -228,24 +228,24 @@ play {
} }
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.recyclerview:recyclerview:1.3.1") implementation("androidx.recyclerview:recyclerview:1.4.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.fragment:fragment-ktx:1.6.1") implementation("androidx.fragment:fragment-ktx:1.8.6")
implementation("androidx.documentfile:documentfile:1.0.1") implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.12.0")
implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
implementation("io.coil-kt:coil:2.2.2") implementation("io.coil-kt:coil:2.7.0")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03") implementation("androidx.window:window:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") implementation("androidx.navigation:navigation-fragment-ktx:2.8.8")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4") implementation("androidx.navigation:navigation-ui-ktx:2.8.8")
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.7.3")
} }
fun runGitCommand(command: List<String>): String { fun runGitCommand(command: List<String>): String {

View file

@ -1,11 +1,12 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// 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.9.0" apply false
id("com.android.library") version "8.1.2" apply false id("com.android.library") version "8.9.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.20" apply false id("org.jetbrains.kotlin.android") version "2.1.20-RC2" apply false
} }
tasks.register("clean").configure { tasks.register("clean").configure {
@ -17,6 +18,6 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.6.0") classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.8.8")
} }
} }

View file

@ -1,20 +1,19 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project ## For more details on how to configure your build environment visit
# SPDX-License-Identifier: GPL-3.0-or-later
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html # http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xms512m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
#Tue Mar 11 19:29:10 AEST 2025
android.defaults.buildfeatures.buildconfig=true
android.suppressUnsupportedCompileSdk=34
android.useAndroidX=true android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
kotlin.parallel.tasks.in.project=true kotlin.parallel.tasks.in.project=true
android.defaults.buildfeatures.buildconfig=true org.gradle.jvmargs=-Xms512m -Dkotlin.daemon.jvm.options\="-Xmx2048M" -Xmx2048M -XX\:MaxMetaspaceSize\=512m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
# Android Gradle plugin 8.0.2
android.suppressUnsupportedCompileSdk=34

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View file

@ -1,9 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
#include <jni.h> #include <jni.h>
#include "common/common_types.h" #include "common/common_types.h"

View file

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes> #include <cinttypes>
@ -144,55 +143,30 @@ bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info,
auto* fpctx = GetFloatingPointState(host_ctx); auto* fpctx = GetFloatingPointState(host_ctx);
auto& memory = guest_ctx->system->ApplicationMemory(); auto& memory = guest_ctx->system->ApplicationMemory();
// Log the alignment fault for debugging // Match and execute an instruction.
LOG_DEBUG(Core_ARM, "Alignment fault at PC={:X}", host_ctx.pc);
// Try to handle the instruction
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx); auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
if (next_pc) { if (next_pc) {
host_ctx.pc = *next_pc; host_ctx.pc = *next_pc;
return true; return true;
} }
// If we couldn't handle it, try to skip the instruction as a fallback // We couldn't handle the access.
LOG_DEBUG(Core_ARM, "Could not handle alignment fault, skipping instruction"); return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
host_ctx.pc += 4; // Skip to next instruction
// Return true to continue execution
return true;
} }
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto* info = static_cast<siginfo_t*>(raw_info); auto* info = static_cast<siginfo_t*>(raw_info);
const u64 fault_addr = reinterpret_cast<u64>(info->si_addr);
auto& memory = guest_ctx->system->ApplicationMemory();
// Get the ArmNce instance from the guest context // Try to handle an invalid access.
ArmNce* nce = guest_ctx->parent; // TODO: handle accesses which split a page?
const Common::ProcessAddress addr =
// Check TLB first (reinterpret_cast<u64>(info->si_addr) & ~Memory::CITRON_PAGEMASK);
if (TlbEntry* entry = nce->FindTlbEntry(fault_addr)) { if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::CITRON_PAGESIZE)) {
if (!entry->writable && info->si_code == SEGV_ACCERR) { // We handled the access successfully and are returning to guest code.
LOG_DEBUG(Core_ARM, "Write to read-only memory at {:X}", fault_addr);
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
}
return true; return true;
} }
// TLB miss handling with better error checking // We couldn't handle the access.
if (memory.InvalidateNCE(fault_addr, Memory::CITRON_PAGESIZE)) {
const u64 host_addr = reinterpret_cast<u64>(memory.GetPointer(fault_addr));
if (host_addr) {
nce->AddTlbEntry(fault_addr, host_addr, Memory::CITRON_PAGESIZE, true);
return true;
} else {
LOG_DEBUG(Core_ARM, "Failed to get host address for guest address {:X}", fault_addr);
}
} else {
LOG_DEBUG(Core_ARM, "Memory invalidation failed for address {:X}", fault_addr);
}
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context); return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
} }
@ -403,81 +377,4 @@ void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
this->ClearInstructionCache(); this->ClearInstructionCache();
} }
TlbEntry* ArmNce::FindTlbEntry(u64 guest_addr) {
std::lock_guard lock(m_tlb_mutex);
// Simple linear search - more reliable than complex indexing
for (size_t i = 0; i < TLB_SIZE; i++) {
TlbEntry& entry = m_tlb[i];
if (entry.valid &&
guest_addr >= entry.guest_addr &&
guest_addr < (entry.guest_addr + entry.size)) {
// Simple access tracking - just increment counter
if (entry.access_count < 1000) { // Prevent overflow
entry.access_count++;
}
return &entry;
}
}
return nullptr;
}
void ArmNce::AddTlbEntry(u64 guest_addr, u64 host_addr, u32 size, bool writable) {
// Validate addresses before proceeding
if (!host_addr) {
LOG_ERROR(Core_ARM, "Invalid host address for guest address {:X}", guest_addr);
return;
}
std::lock_guard lock(m_tlb_mutex);
// First try to find an invalid entry
size_t replace_idx = TLB_SIZE;
for (size_t i = 0; i < TLB_SIZE; i++) {
if (!m_tlb[i].valid) {
replace_idx = i;
break;
}
}
// If no invalid entries, use simple LRU
if (replace_idx == TLB_SIZE) {
u32 lowest_count = UINT32_MAX;
for (size_t i = 0; i < TLB_SIZE; i++) {
if (m_tlb[i].access_count < lowest_count) {
lowest_count = m_tlb[i].access_count;
replace_idx = i;
}
}
}
// Safety check
if (replace_idx >= TLB_SIZE) {
replace_idx = 0; // Fallback to first entry if something went wrong
}
// Page align the addresses for consistency
const u64 page_mask = size - 1;
const u64 aligned_guest = guest_addr & ~page_mask;
const u64 aligned_host = host_addr & ~page_mask;
m_tlb[replace_idx] = {
.guest_addr = aligned_guest,
.host_addr = aligned_host,
.size = size,
.valid = true,
.writable = writable,
.last_access_time = 0, // Not used in simplified implementation
.access_count = 1
};
}
void ArmNce::InvalidateTlb() {
std::lock_guard lock(m_tlb_mutex);
for (auto& entry : m_tlb) {
entry.valid = false;
}
}
} // namespace Core } // namespace Core

View file

@ -1,11 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <mutex> #include <mutex>
#include <array>
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/nce/guest_context.h" #include "core/arm/nce/guest_context.h"
@ -18,21 +16,6 @@ namespace Core {
class System; class System;
struct TlbEntry {
u64 guest_addr;
u64 host_addr;
u32 size;
bool valid;
bool writable;
u64 last_access_time; // For LRU tracking
u32 access_count; // For access frequency tracking
};
// Improved TLB configuration
constexpr size_t TLB_SETS = 64; // Number of sets
constexpr size_t TLB_WAYS = 8; // Ways per set
constexpr size_t TLB_SIZE = TLB_SETS * TLB_WAYS;
class ArmNce final : public ArmInterface { class ArmNce final : public ArmInterface {
public: public:
ArmNce(System& system, bool uses_wall_clock, std::size_t core_index); ArmNce(System& system, bool uses_wall_clock, std::size_t core_index);
@ -107,24 +90,6 @@ public:
// Stack for signal processing. // Stack for signal processing.
std::unique_ptr<u8[]> m_stack{}; std::unique_ptr<u8[]> m_stack{};
// Enhanced TLB implementation
std::array<TlbEntry, TLB_SIZE> m_tlb{};
std::mutex m_tlb_mutex;
u64 m_tlb_access_counter{0};
// TLB helper functions
TlbEntry* FindTlbEntry(u64 guest_addr);
void AddTlbEntry(u64 guest_addr, u64 host_addr, u32 size, bool writable);
void InvalidateTlb();
size_t GetTlbSetIndex(u64 guest_addr) const;
size_t FindReplacementEntry(size_t set_start);
void UpdateTlbEntryStats(TlbEntry& entry);
// Thread context caching
std::mutex m_context_mutex;
Kernel::KThread* m_last_thread{nullptr};
GuestContext m_cached_ctx{};
}; };
} // namespace Core } // namespace Core

View file

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@ -13,7 +14,7 @@ namespace Core {
namespace Hardware { namespace Hardware {
constexpr u64 BASE_CLOCK_RATE = 1'020'000'000; // Default CPU Frequency = 1020 MHz constexpr u64 BASE_CLOCK_RATE = 1'785'000'000; // Default CPU Frequency = 1785 MHz
constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores