- Cores and Threads options have been added.
- Gets more accurate CPU information.
This commit is contained in:
An Bui 2026-05-01 20:23:30 +07:00
parent bafa8a12d6
commit f0ffd9f397
18 changed files with 274 additions and 20 deletions

View file

@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 112
versionName "4.0.8"
versionCode 113
versionName "4.0.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
@ -68,7 +68,14 @@ android {
}
buildToolsVersion '36.1.0'
ndkVersion '21'
ndkVersion '27.3.13750724'
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
lint {
abortOnError false

View file

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
project("native_helper")
add_library(native_helper SHARED cpu-info.cpp)
find_library(log-lib log)
target_link_libraries(native_helper ${log-lib})

View file

@ -0,0 +1,46 @@
#include <jni.h>
#include <unistd.h>
#include <thread>
#include <sys/auxv.h>
#if defined(__aarch64__)
#include <asm/hwcap.h>
#elif defined(__x86_64__)
#include <cpuid.h>
#endif
extern "C" {
JNIEXPORT jint JNICALL
Java_com_vectras_vm_utils_CpuHelper_getCpuCores(JNIEnv* env, jobject obj) {
return sysconf(_SC_NPROCESSORS_CONF);
}
JNIEXPORT jint JNICALL
Java_com_vectras_vm_utils_CpuHelper_getActiveCpuCores(JNIEnv* env, jobject obj) {
return sysconf(_SC_NPROCESSORS_ONLN);
}
JNIEXPORT jint JNICALL
Java_com_vectras_vm_utils_CpuHelper_getCpuThreads(JNIEnv* env, jobject obj) {
unsigned int threads = std::thread::hardware_concurrency();
return (threads > 0) ? (jint)threads : 1;
}
JNIEXPORT jint JNICALL
Java_com_vectras_vm_utils_CpuHelper_check64Bit(JNIEnv* env, jobject obj) {
// 1: 64-bit, 0: 32-bit, -1: Unknown or error.
#if defined(__aarch64__)
unsigned long hwcaps = getauxval(AT_HWCAP);
return (hwcaps > 0) ? 1 : 0;
#elif defined(__x86_64__)
unsigned int eax, ebx, ecx, edx;
if (__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx)) {
return (edx & (1 << 29)) ? 1 : 0;
}
return -1; // Failed to call CPUID.
#endif
return 0;
}
}

View file

@ -128,6 +128,8 @@ public class ExportRomActivity extends AppCompatActivity {
}
vmConfigMap.put("cpu", current.cpu);
vmConfigMap.put("cores", current.cores);
vmConfigMap.put("threads", current.threads);
boolean isUsingDiskInQemuExtraParams = VMManager.isHaveADisk(current.itemExtra);

View file

@ -11,6 +11,7 @@ import com.vectras.vm.creator.VMCreatorSelector;
import com.vectras.vm.main.vms.DataMainRoms;
import com.vectras.vm.manager.VmFileManager;
import com.vectras.vm.setupwizard.SetupFeatureCore;
import com.vectras.vm.utils.CpuHelper;
import com.vectras.vm.utils.FileUtils;
import java.io.File;
@ -43,11 +44,27 @@ public class StartVM {
String extraParams = vmData.itemExtra;
String cpuParams = "";
String cpu = Objects.requireNonNull(VMCreatorSelector.getCpu(activity, MainSettingsManager.getArch(activity), vmData.cpu).get("value")).toString();
if (!cpu.isEmpty() && !extraParams.contains("-cpu")) {
extraParams = "-cpu " + cpu + " " + extraParams;
if (!cpu.isEmpty() && !extraParams.contains("-cpu ")) {
cpuParams = " -cpu " + cpu;
}
CpuHelper cpuHelper = new CpuHelper();
int cores = Integer.parseInt(Objects.requireNonNull(VMCreatorSelector.getCpuCore(MainSettingsManager.getArch(activity), vmConfigs.cores).get("value")).toString());
int threads = Math.max(1, vmConfigs.threads + 1);
if (!extraParams.contains("-smp ")) {
if (cores * threads > cpuHelper.getCpuThreads()) {
cpuParams += " -smp sockets=1,cores=" + cpuHelper.getCpuCores() + ",threads=1";
} else {
cpuParams += " -smp sockets=1,cores=" + cores + ",threads=" + threads;
}
}
if (!cpuParams.isEmpty()) extraParams = cpuParams + " " + extraParams;
String bootFromParams = Objects.requireNonNull(VMCreatorSelector.getBootFrom(activity, vmData.bootFrom).get("value")).toString();
String showBootMenuParams = vmData.isShowBootMenu ? "menu=on" : "";
String bootParams = "";

View file

@ -4,6 +4,7 @@ import android.content.Context;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.R;
import com.vectras.vm.utils.CpuHelper;
import java.util.ArrayList;
import java.util.HashMap;
@ -113,6 +114,37 @@ public class ListManager {
return list;
}
public static ArrayList<HashMap<String, Object>> cores(String arch) {
ArrayList<HashMap<String, Object>> list = new ArrayList<>();
if (arch.equals(MainSettingsManager.PPC_ARCH)) {
putToList(list, "1", "1");
putToList(list, "2", "2");
} else {
CpuHelper cpuHelper = new CpuHelper();
int cores = cpuHelper.getCpuCores();
int addedCore = 1;
while (addedCore < cores + 1 && addedCore <= 8) {
if (addedCore == 1 || addedCore % 2 == 0)
putToList(list, String.valueOf(addedCore), String.valueOf(addedCore));
addedCore++;
}
}
return list;
}
public static ArrayList<HashMap<String, Object>> threads(String arch) {
ArrayList<HashMap<String, Object>> list = new ArrayList<>();
putToList(list, "1", "1");
if (!(arch.equals(MainSettingsManager.ARM64_ARCH)
|| arch.equals(MainSettingsManager.PPC_ARCH)))
putToList(list, "2", "2");
return list;
}
public static void putToList
(
ArrayList<HashMap<String, Object>> listMap,

View file

@ -38,6 +38,7 @@ import com.vectras.vm.databinding.DialogProgressStyleBinding;
import com.vectras.vm.main.MainActivity;
import com.vectras.vm.manager.VmFileManager;
import com.vectras.vm.utils.ClipboardUltils;
import com.vectras.vm.utils.CpuHelper;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
@ -78,6 +79,8 @@ public class VMCreatorActivity extends AppCompatActivity {
private String thumbnailPath = "";
private String vmID = VMManager.idGenerator();
private int cpu = 0;
private int cores = 0;
private int threads = 0;
private boolean isShowBootMenu = false;
private boolean isUseLocalTime = true;
private boolean isUseUefi = false;
@ -301,6 +304,16 @@ public class VMCreatorActivity extends AppCompatActivity {
binding.sbvCpu.setSubtitle(name);
})));
binding.sbvCore.setOnClickListener(v -> VMCreatorSelector.cpuCore(this, MainSettingsManager.getArch(this), cores, ((position, name, value) -> {
cores = position;
binding.sbvCore.setSubtitle(name);
})));
binding.sbvThread.setOnClickListener(v -> VMCreatorSelector.cpuThread(this, MainSettingsManager.getArch(this), threads, ((position, name, value) -> {
threads = position;
binding.sbvThread.setSubtitle(name);
})));
binding.sbvBootfrom.setOnClickListener(v -> VMCreatorSelector.bootFrom(this, bootFrom, ((position, name, value) -> {
bootFrom = position;
binding.sbvBootfrom.setSubtitle(name);
@ -638,6 +651,12 @@ public class VMCreatorActivity extends AppCompatActivity {
cpu = current.cpu;
binding.sbvCpu.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getCpu(this, MainSettingsManager.getArch(this), current.cpu).get("name")).toString());
cores = current.cores;
binding.sbvCore.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getCpuCore(MainSettingsManager.getArch(this), cores).get("value")).toString());
threads = current.threads;
binding.sbvThread.setSubtitle(String.valueOf(threads + 1));
bootFrom = current.bootFrom;
binding.sbvBootfrom.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getBootFrom(this, current.bootFrom).get("name")).toString());
isShowBootMenu = current.isShowBootMenu;
@ -669,21 +688,35 @@ public class VMCreatorActivity extends AppCompatActivity {
defQemuParams = switch (MainSettingsManager.getArch(this)) {
case "ARM64" ->
"-M virt,virtualization=true -accel tcg,thread=multi -net nic,model=e1000 -net user -device nec-usb-xhci -device usb-kbd -device usb-mouse -device VGA";
case "PPC" -> "-M mac99 -accel tcg,thread=multi -smp 1";
case "PPC" -> "-M mac99 -accel tcg,thread=multi";
default ->
"-M pc -accel tcg,thread=multi -smp 4 -vga std -netdev user,id=usernet -device e1000,netdev=usernet -usb -device usb-tablet";
"-M pc -accel tcg,thread=multi -vga std -netdev user,id=usernet -device e1000,netdev=usernet -usb -device usb-tablet";
};
} else {
defQemuParams = switch (MainSettingsManager.getArch(this)) {
case "ARM64" ->
"-M virt -net nic,model=e1000 -net user -device nec-usb-xhci -device usb-kbd -device usb-mouse -device VGA";
case "PPC" -> "-M mac99 -smp 1";
case "PPC" -> "-M mac99 1";
default ->
"-M pc -smp 4 -vga std -netdev user,id=usernet -device e1000,netdev=usernet -usb -device usb-tablet";
"-M pc -vga std -netdev user,id=usernet -device e1000,netdev=usernet -usb -device usb-tablet";
};
}
binding.title.setText(getString(R.string.new_vm));
binding.qemu.setText(defQemuParams);
String currentArch = MainSettingsManager.getArch(this);
if (currentArch.equals(MainSettingsManager.X86_64_ARCH)) {
cores = Math.min(1, VMCreatorSelector.getCpuCorePosition(new CpuHelper().getCpuCores() - 1));
binding.sbvCore.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getCpuCore(currentArch, cores).get("value")).toString());
} else if (currentArch.equals(MainSettingsManager.ARM64_ARCH)) {
cores = Math.min(2, VMCreatorSelector.getCpuCorePosition(new CpuHelper().getCpuCores() - 1));
binding.sbvCore.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getCpuCore(currentArch, cores).get("value")).toString());
} else {
binding.sbvCore.setSubtitle("1");
}
binding.sbvThread.setSubtitle("1");
}
private void checkVMID() {
@ -802,6 +835,8 @@ public class VMCreatorActivity extends AppCompatActivity {
current.itemName = Objects.requireNonNull(binding.title.getText()).toString();
current.itemIcon = thumbnailPath;
current.cpu = cpu;
current.cores = cores;
current.threads = threads;
current.itemPath = Objects.requireNonNull(binding.drive.getText()).toString();
current.imgCdrom = Objects.requireNonNull(binding.cdrom.getText()).toString();
current.sharedFolder = sharedFolder;

View file

@ -29,16 +29,34 @@ public class VMCreatorSelector {
void onSelected(int position, String name, String value);
}
public static void cpu(Activity activity, String arch, int position, SelectorCallback callback) {
showDialog(activity, ListManager.cpus(activity, arch), position, callback, activity.getString(R.string.processor));
}
public static HashMap<String, Object> getCpu(Context context, String arch, int position) {
return ListManager.cpus(context, arch).get(position);
}
public static HashMap<String, Object> getBootFrom(Context context, int position) {
return ListManager.bootFrom(context).get(position);
public static void cpuCore(Activity activity, String arch, int position, SelectorCallback callback) {
showDialog(activity, ListManager.cores(arch), position, callback, activity.getString(R.string.core));
}
public static void cpu(Activity activity, String arch, int position, SelectorCallback callback) {
showDialog(activity, ListManager.cpus(activity, arch), position, callback, activity.getString(R.string.processor));
public static int getCpuCorePosition(int core) {
if (core > 8) return 7;
if (core > 2) return core - 2;
return core - 1;
}
public static HashMap<String, Object> getCpuCore(String arch, int position) {
return ListManager.cores(arch).get(position);
}
public static void cpuThread(Activity activity, String arch, int position, SelectorCallback callback) {
showDialog(activity, ListManager.threads(arch), position, callback, activity.getString(R.string.thread));
}
public static HashMap<String, Object> getBootFrom(Context context, int position) {
return ListManager.bootFrom(context).get(position);
}
public static void bootFrom(Activity activity, int position, SelectorCallback callback) {

View file

@ -4,6 +4,8 @@ import com.google.gson.annotations.SerializedName;
public class DataMainRoms {
public int cpu = 0;
public int cores = 0;
public int threads = 0;
@SerializedName(
value = "icon",

View file

@ -0,0 +1,16 @@
package com.vectras.vm.utils;
public class CpuHelper {
static {
System.loadLibrary("native_helper");
}
public native int getCpuCores();
public native int getActiveCpuCores();
public native int getCpuThreads();
public native int check64Bit();
public boolean is64Bit() {
return check64Bit() == 1;
}
}

View file

@ -67,9 +67,7 @@ public class DeviceUtils {
}
public static boolean is64bit() {
return Build.SUPPORTED_ABIS[0].contains("64") &&
Build.SUPPORTED_64_BIT_ABIS != null &&
Build.SUPPORTED_64_BIT_ABIS.length > 0;
return new CpuHelper().is64Bit();
}
public static boolean isArm() {
return Build.SUPPORTED_ABIS[0].contains("arm");

View file

@ -203,6 +203,20 @@
app:title="@string/processor"
app:subtitle="@string/defaulttext" />
<com.vectras.vm.view.SelectBoxView
android:id="@+id/sbv_core"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/cores"
app:subtitle="@string/defaulttext" />
<com.vectras.vm.view.SelectBoxView
android:id="@+id/sbv_thread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/threads"
app:subtitle="@string/defaulttext" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View file

@ -558,6 +558,10 @@
<string name="cannot_save_here_please_choose_another_location">Không thể lưu ở đây, hãy chọn một nơi khác.</string>
<string name="processor">Bộ xử lý</string>
<string name="board">Bo mạch chủ</string>
<string name="core">Nhân</string>
<string name="cores">Nhân</string>
<string name="thread">Luồng</string>
<string name="threads">Luồng</string>
<!--======================TERMUX STRINGS====================-->
<string name="application_name" translatable="false">Vterm</string>

View file

@ -569,6 +569,10 @@
<string name="cannot_save_here_please_choose_another_location">Cannot save here, please choose another location.</string>
<string name="processor">Processor</string>
<string name="board">Board</string>
<string name="core">Core</string>
<string name="cores">Cores</string>
<string name="thread">Thread</string>
<string name="threads">Threads</string>
<!--======================TERMUX STRINGS====================-->

16
qemu/11.0.0.sh Normal file
View file

@ -0,0 +1,16 @@
#Example for arm64-v8a
apk update
apk add --no-cache bash curl tar rsync flex build-base git meson bison ninja python3 py3-pip libcap-ng-dev glib-dev pixman-dev sdl2-dev sdl2_image-dev sndio alsa-utils alsaconf zlib-dev libaio-dev liburing-dev libcap libcap-ng libssh lzo snappy capstone libcbor libdw gtk+3.0-dev libssh-dev libnfs-dev libseccomp-dev lzo-dev snappy-dev capstone-dev ndctl-libs libcbor-dev libselinux libselinux-dev fuse-dev vde2-dev nmap sndio-dev pipewire-dev alsa-lib-dev vte3-dev keyutils keyutils-dev rng-tools nettle-dev libgcrypt libgcrypt-dev gnutls-dev iasl gcc-objc rust libudev-zero-dev ndctl-dev libu2f-server libu2f-server-dev libbpf libbpf-dev rdma-core-openrc curl-dev linux-pam linux-pam-dev net-snmp-dev jack-dev fuse3 fuse3-dev linux-virt
pip install Ninja Sphinx --break-system-packages
pip install meson --upgrade --break-system-packages
pip install sphinx-rtd-theme --break-system-packages
export CFLAGS="-O2 -march=armv8-a+crc+nosve"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="-march=armv8-a+crc+nosve"
wget https://download.qemu.org/qemu-11.0.0.tar.xz
tar xf qemu-11.0.0.tar.xz
cd qemu-11.0.0
mkdir ../build && cd ../build
../qemu-11.0.0/configure --enable-gtk --enable-sdl --enable-libssh --enable-cap-ng
ninja -j$(nproc)

23
qemu/9.2.2.sh Normal file
View file

@ -0,0 +1,23 @@
#Example for arm64-v8a
apk update
apk add --no-cache bash curl tar rsync flex build-base git meson bison ninja python3 py3-pip libcap-ng-dev glib-dev pixman-dev sdl2-dev sdl2_image-dev sndio alsa-utils alsaconf zlib-dev libaio-dev liburing-dev libcap libcap-ng libssh lzo snappy capstone libcbor libdw gtk+3.0-dev libssh-dev libnfs-dev libseccomp-dev lzo-dev snappy-dev capstone-dev ndctl-libs libcbor-dev libselinux libselinux-dev fuse-dev vde2-dev nmap sndio-dev pipewire-dev alsa-lib-dev vte3-dev keyutils keyutils-dev rng-tools nettle-dev libgcrypt libgcrypt-dev gnutls-dev iasl gcc-objc rust libudev-zero-dev ndctl-dev libu2f-server libu2f-server-dev libbpf libbpf-dev rdma-core-openrc curl-dev linux-pam linux-pam-dev net-snmp-dev jack-dev fuse3 fuse3-dev linux-virt
pip install Ninja Sphinx --break-system-packages
pip install meson --upgrade --break-system-packages
pip install sphinx-rtd-theme --break-system-packages
export CFLAGS="-O2 -march=armv8-a+crc+nosve"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="-march=armv8-a+crc+nosve"
mkdir myqemu && cd myqemu
git clone https://github.com/kjliew/qemu-3dfx.git
cd qemu-3dfx
echo "" > ~/myqemu/qemu-3dfx/qemu-1/hw/mesa/mglmapbo.c
nano ~/myqemu/qemu-3dfx/qemu-1/hw/mesa/mglmapbo.c
wget https://download.qemu.org/qemu-9.2.2.tar.xz
tar xf qemu-9.2.2.tar.xz
cd qemu-9.2.2
rsync -r ../qemu-0/hw/3dfx ../qemu-1/hw/mesa ./hw/
patch -p0 -i ../00-qemu92x-mesa-glide.patch
bash ../scripts/sign_commit
mkdir ../build && cd ../build
../qemu-9.2.2/configure --enable-gtk --enable-sdl --enable-libssh --enable-cap-ng
ninja -j$(nproc)

12
qemu/mglmapbo.c Normal file
View file

@ -0,0 +1,12 @@
//Example for arm64-v8a
//Qemu 9.2.2
//Fix when building with ARM CPUs
//No need to fix if you don't get errors or don't use Mesa.
//qemu-9.2.2/hw/mesa/mglmapbo.c
//...
#include <arm_acle.h>
//...
//#define _mm_crc32_u64 __builtin_arm_crc32cd
//🠃🠃🠃🠃🠃
#define _mm_crc32_u64 __crc32cd
//...

View file

@ -5,11 +5,11 @@
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
"Message": "<h2>4.0.0</h2>\nBugs fixed.",
"cancellable": true,
"versionCodeBeta":"111",
"versionNameBeta":"4.0.8",
"versionNameBetas":"4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,4.0.7,4.0.8",
"versionCodeBeta":"113",
"versionNameBeta":"4.0.9",
"versionNameBetas":"4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,4.0.7,4.0.8,4.0.9",
"sizeBeta": "45 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>4.0.8</h2>Bugs fixed.",
"MessageBeta": "<h2>4.0.9</h2>Bugs fixed.",
"cancellableBeta": true
}